[Ipe-discuss] problem with the 'presentation' ipelet

Zhengdao Wang zhengdao at iastate.edu
Sat Mar 10 21:51:15 CET 2012


I attached a current version that I am using, hoping that this will
solve the problem.

Zhengdao

-----Original Message-----
> From: Shengping Zhang <shengping.zhang at gmail.com>
> Date: Fri, 9 Mar 2012 18:53:41 -0800
> To: ipe-discuss at lists.science.uu.nl
> Subject: [Ipe-discuss] problem with the 'presentation' ipelet
> 
> Hello,
> 
> When I use the 'presentation' ipelet to add box around text. I can successfully do it for the first time. But when i want to add a box around another text, it fails. I need to  reboot ipe and the can successfully do it. But if i want to continue to add box around other tex, i need to reboot ipe again. 
> 
> I use ipe  7.1.2 on Mac. 
> 
> Did anyone else meet the same problem?  How to solve it?
> 
> Thanks!
> -- 
> Best regards,
>  
> Shengping Zhang
>  
> ----------------------------------------------------------------
> Redwood Center for Theoretical Neuroscience
> University of California at Berkeley
> 567 Evans Hall, MC# 3198
> Berkeley, CA 94720-3198, United States
> School of Computer Science and Technology
> Harbin Institute of Technology
> 92 Xidazhi Street, Harbin 150001, China
> Phone: 510-502-8557 / +86-451-86416485
> http://redwood.berkeley.edu/spzhang/
> ----------------------------------------------------------------
> 
> 
> 

> _______________________________________________
> Ipe-discuss mailing list
> Ipe-discuss at lists.science.uu.nl
> http://lists.science.uu.nl/mailman/listinfo/ipe-discuss

-------------- next part --------------
----------------------------------------------------------------------
-- presentation goodies for Ipe

--[[

SUMMARY

 This ipelet adds a few goodies for presentation, including
  1. Ability to create beamer-like boxes with/without header
  2. Add a framed box for the selected objects (including text)
  3. A function to deselect all selected on all pages
  4. The boxes can be edited as text/path objects (press E)
  5. A few items can be added to the style sheet to add preferred
     symbolics for box colors etc. This has the benefit that this
     preferences can be changed to affect all boxes (see below).

  The design of these boxes is from Martin N?llenburg's presentation
  example posted on the Ipe7 wiki page.

STYLESHEET (CHANGING PREFERRED SETTINGS)

 Example style sheet to cascade with presentation.isy is as follows.

---- content of example.isy ---
<?xml version="1.0"?>
<!DOCTYPE ipestyle SYSTEM "ipe.dtd">
<ipestyle name="mine">
<color name="tab_header" value="0 0 0.5"/>
<color name="tab_body" value="0.827"/>
<color name="box_fill" value="0.827"/>
<color name="box_border" value="0 0 0"/>
<pen name="boxborder" value="2.4"/>
<preamble>
\usepackage{amsmath}
</preamble>
</ipestyle>
---- end content of example.isy ---

 where:
  tab_header= color of the tab header in a tabbed box
  tab_body  = color of the tab body in a tabbed box
  box_fill  = fill color a box
  box_border= color of the box border
  boxborder = linewidth of the box border

 The preferred box mode (stroked/filled/strokedfilled) can be changed by
 changing the hard-wired value (no stylesheet option) PREFERRED_BOX_MODE below

 With the above style sheet, one can start an empty presentation using
  ipe -sheet presentation -sheet /path/to/example.isy

SHORTCUT

 Shortcuts to these functions can be changed as for other ipelets:
  shortcuts.ipelet_x_presentation = "Key"
 where x is the index (starting 1) of the sub-menu for the function

FILE/AUTHOR HISTORY

 verion  0. Initial Release. Zhengdao Wang 2010
 verion  1. add a line here if a change is made

LICENSE

 This file can be distributed and modified under the terms of the GNU General
 Public License as published by the Free Software Foundation; either version
 3, or (at your option) any later version.

 This file is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

--]]

----------------------------------------------------------------------

label = "Presentation"

about = [[
  Presentation Goodies: Add boxes around objects, deselect all,
  add tabbed/boxed text.
]]

V = ipe.Vector
indexOf=_G.indexOf

-- table storing the height of the first line box
Height={}
Spacing={}
Has_Height=nil

local c=10 -- corner size

local UNKNOWN=0
local TABBED_TEXT=1
local BOXED_TEXT=2
local BOXED_OTHER=3

local ROUND_CORNER=true -- true of false
local PREFERRED_BOX_MODE="filled" -- stroked, filled, strokedfilled

local BOX_DIALOG_SIZE={400,140}

-- initialize the Height table
local function init_spacing(model)
  local p=model:page()
  local xx=300
  local iH={}
  local sizes = model.doc:sheets():allNames("textsize")
  for i,size in ipairs(sizes) do
    obj=ipe.Text({textsize=size, minipage=true}, "ABC\n\nDEF", V(xx,xx), xx)
    p:insert(nil, obj, nil, "alpha")
    obj=ipe.Text({textsize=size, minipage=true}, "ABC", V(xx,xx), xx)
    p:insert(nil, obj, nil, "alpha")
    iH[#iH+1]=size
  end
  model.doc:runLatex()
  for i=#sizes,1,-1 do
    Height[iH[i]]=xx-p:bbox(#p):bottomLeft().y
    p:remove(#p)
    Spacing[iH[i]]=(xx-p:bbox(#p):bottomLeft().y)/2-Height[iH[i]]
    p:remove(#p)
  end
  Has_Height=1
end

-- generate a path with given properties
local function path(model, shape, props)
  local oldvalues={}
  for k,v in pairs(props) do
    oldvalues[k]=model[k]
    model.attributes[k]=v
  end
  local obj = ipe.Path(model.attributes, shape)
  for k,v in pairs(props) do
    model.attributes[k]=oldvalues[k]
  end
  if props.pen then obj:set('pen', props.pen) end
  obj:set('pathmode', props.pathmode, props.stroke, props.fill)
  return obj
end

-- create a square box #shape=3
local function boxshape_square(v1, v2)
  return { type="curve", closed=true;
     { type="segment"; v1, V(v1.x, v2.y) }, -- L
     { type="segment"; V(v1.x, v2.y), v2 }, -- T
     { type="segment"; v2, V(v2.x, v1.y) } } -- R
end

-- create a square box with a pointer #shape=6
local function boxshape_square_pointer(v1, v2, v3, v4, v5)
  local dx=v2.x-v1.x
  local dy=v2.y-v1.y
  local v3=v3 or V(v1.x+.4*dx, v2.y)
  local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
  local v5=v5 or V(v1.x+.5*dx,v2.y)
  return { type="curve", closed=true;
     { type="segment"; v1, V(v1.x, v2.y) }, -- L
     { type="segment"; V(v1.x, v2.y), v3}, -- T
     { type="segment"; v3, v4}, -- P
     { type="segment"; v4, v5}, --P
     { type="segment"; v5, v2}, -- T
     { type="segment"; v2, V(v2.x, v1.y) } } -- R
end

-- create a header box: round corner on topRight
local function boxshape_roundTR(v1, v2)
  return { type="curve", closed=true;
     { type="segment"; v1, V(v1.x, v2.y) },
     { type="segment"; V(v1.x, v2.y), V(v2.x-c, v2.y) },
     { type="bezier"; V(v2.x-c, v2.y), V(v2.x-c/2, v2.y),
      V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) },
     { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y) } }
end

-- create a body box: round corner on bottom Left
local function boxshape_roundLL(v1, v2)
  return { type="curve", closed=true;
     { type="segment"; v2, V(v2.x, v1.y) },
     { type="segment"; V(v2.x, v1.y), V(v1.x+c, v1.y) },
     { type="bezier"; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
      V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) },
     { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y) } }
end

-- create a body box: 4 round corners #shape=8
local function boxshape_round(v1, v2)
  return { type="curve", closed=true;
     { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
     { type="bezier"; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
      V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
     { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
     { type="bezier"; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
      V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
     { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
     { type="bezier"; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
      V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
     { type="segment"; V(v1.x+c, v2.y), V(v2.x-c, v2.y) }, -- T
     { type="bezier"; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
      V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
    }
end

-- create a body box: 4 round corners, with pointer #shape=11
local function boxshape_round_pointer(v1, v2, v3, v4, v5)
  local dx=v2.x-v1.x
  local dy=v2.y-v1.y
  local v3=v3 or V(v1.x+.4*dx, v2.y)
  local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
  local v5=v5 or V(v1.x+.5*dx,v2.y)
  return { type="curve", closed=true;
     { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
     { type="bezier"; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
      V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
     { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
     { type="bezier"; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
      V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
     { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
     { type="bezier"; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
      V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
     { type="segment"; V(v1.x+c, v2.y), v3}, -- T
     { type="segment"; v3, v4}, -- P
     { type="segment"; v4, v5}, --P
     { type="segment"; v5, V(v2.x-c, v2.y)}, -- T
     { type="bezier"; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
      V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
    }
end

-- parse the values from a group obj
local function parse_group_values(model,prim)
  local fs = model.doc:sheets():find("layout").framesize
  local p=model:page()
  local bbox=p:bbox(prim)
  local pos=V(bbox:bottomLeft().x, bbox:topRight().y)

  local elements=p[prim]:elements()
  if #elements==4 then
    local hb,bb,ht,bt=elements[1],elements[2],elements[3],elements[4]
    if hb:type()=="path" and bb:type()=="path" and
      ht:type()=="text" and bt:type()=="text" then
      local values={htext=ht:text(),
        btext=bt:text(),
        pinned=(p[prim]:get("pinned")=="horizontal"),
        fwidth=string.format('%.2f',
          (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
        hcolor=hb:get("fill"),
        bcolor=bb:get("fill"),
        size=ht:get("textsize")}
      return TABBED_TEXT,values,pos
    end
  else
    local bb,bt=elements[1],elements[2]
    if bb:type()=="path" and #bb:shape()==1 and
      bb:shape()[1].closed==true and
      (#bb:shape()[1]==3 or #bb:shape()[1]==8 or
      #bb:shape()[1]==6 or #bb:shape()[1]==11) then
      if bt:type()=="text" then
        local values={btext=bt:text(),
          pinned=(p[prim]:get("pinned")=="horizontal"),
          size=bt:get("textsize"),
          fwidth=string.format('%.2f',
            (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
          bcolor=bb:get("fill")}
        if #bb:shape()[1]==6 then
          pos=V(pos.x,bb:shape()[1][1][2].y)
        elseif #bb:shape()[1]==11 then
          pos=V(pos.x,bb:shape()[1][10][2].y)
        end
        return BOXED_TEXT,values,pos
      else
        return BOXED_OTHER
      end
    end
  end
  return UNKNOWN
end

-- Edit the values for the frame
local function edit_tabbed_values(model,values)
  local d = ipeui.Dialog(model.ui:win(), "Create tabbed text")
  local colors = model.doc:sheets():allNames("color")
  local sizes = model.doc:sheets():allNames("textsize")
  d:add("hlabel", "label", { label="Enter Header" }, 1, 1, 1, 1)
  d:add("hcolor", "combo", colors, 1, 4)
  d:add("htext", "input", { syntax="latex" }, 2, 1, 1, 4)
  d:add("blabel", "label", { label="Enter Body"}, 3, 1, 1, 3)
  d:add("bcolor", "combo", colors, 3, 4)
  d:add("btext", "text", { syntax="latex" }, 4, 1, 1, 4)
  d:add("size", "combo", sizes, 5, 1)
  d:add("wlabel", "label", { label="width [0-1]"}, 5, 2, 1, 2)
  d:add("fwidth", "input", {size=2}, 5, 3, 1, 1)
  d:add("pinned", "checkbox", { label="pinned"}, 5, 4)
  d:add("ok", "button", { label="&Ok", action="accept" }, 6, 4)
  d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 3)
  _G.addEditorField(d, "btext", 6, 2)
  d:setStretch("row", 2, 1)
  d:setStretch("column", 1, 1)

  if indexOf("tab_header", colors) then
    d:set("hcolor", indexOf("tab_header", colors))
  else
    d:set("hcolor", indexOf("darkblue", colors))
  end

  if indexOf("tab_body", colors) then
    d:set("bcolor", indexOf("tab_body", colors))
  else
    d:set("bcolor", indexOf("lightgray", colors))
  end

  if values then
    for k,v in pairs(values) do
      if k=="hcolor" or k=="bcolor" then v=indexOf(v, colors) end
      if k=="size" then v=indexOf(v, sizes) end
      d:set(k, v)
    end
  end

  local r = d:execute(prefs.editor_size)
  if not r then return end
  local newvalues={}
  newvalues.htext=d:get("htext")
  newvalues.btext=d:get("btext")
  newvalues.pinned=d:get("pinned")
  newvalues.fwidth=d:get("fwidth")
  newvalues.size=sizes[d:get("size")]
  newvalues.hcolor=colors[d:get("hcolor")]
  newvalues.bcolor=colors[d:get("bcolor")]
  if newvalues.fwidth=="" or tonumber(newvalues.fwidth)>.99 then
    newvalues.pinned=true
  end
  return newvalues
end

-- measure the height a piece of given text
local function measure_height(model,text,size,width)
  local p=model:page()
  local obj= ipe.Text(model.attributes, text, V(0,0), width)
  obj:set('textsize', size)
  local layer=p:active(model.vno)
  p:insert(nil, obj, nil, layer)
  if not model.doc:runLatex() then
    p:remove(#p)
    return 100
  end
  local bbox=p:bbox(#p)
  p:remove(#p)
  return bbox:topRight().y-bbox:bottomLeft().y
end

-- Create boxed text
local function create_boxed(model,values, pos, prim)
  local fs = model.doc:sheets():find("layout").framesize
  local p = model:page()
  local editmode=(prim~=nil)

  local width fwidth=tonumber(values.fwidth)

  if not fwidth or fwidth<0 or fwidth>1 then
    width=fs.x
    fwidth=1
  else
    width=fwidth*fs.x
  end

  -- spacing
  local s=Spacing[values.size]
  local h=Height[values.size]
  if not s or not h then
    init_spacing(model)
    s=Spacing[values.size]
    h=Height[values.size]
  end

  local bheight=measure_height(model,values.btext,values.size,width-2*s)

  -- location
  if not pos then
    x1=fs.x/2-width/2
    x2=fs.x/2+width/2
    y2=fs.y/2
    y1=y2-bheight-1.8*s
  else
    x1=pos.x
    x2=x1+width
    y2=pos.y
    y1=y2-bheight-1.8*s
  end
  if fwidth>.99 then x1,x2=0,fs.x end

  -- body text
  pos=V(x1+s, y2-s)
  local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
  bt:set('textsize', values.size)

  -- body box
  local shape2
  if values.rounded then
    shape2 = { boxshape_round(V(x1,y1), V(x2,y2)) }
  else
    shape2 = { boxshape_square(V(x1,y1), V(x2,y2)) }
  end
  local bb = path(model, shape2,
    {pathmode='filled', fill=values.bcolor, stroke="white"})

  -- group object
  local elements={bb,bt}
  local obj=ipe.Group(elements)
  -- obj:setMatrix(p[prim]:matrix()) -- currently not working
  if values.pinned then obj:set('pinned', 'horizontal') end

  if editmode then
    local t={original=p[prim]:clone(),
        label="edit boxed text",
        pno=model.pno,
        vno=model.vno,
        primary=prim,
        final=obj }
    t.undo = function (t, doc)
           doc[t.pno]:replace(t.primary, t.original)
         end
    t.redo = function (t, doc)
           doc[t.pno]:replace(t.primary, t.final)
         end
    model:register(t)
  else
    model:creation("create boxed text", obj)
    -- model.doc:runLatex() -- may crash the thing
  end
end

-- Create the requested object from values
local function create_tabbed(model,values, pos, prim)
  local fs = model.doc:sheets():find("layout").framesize
  local p = model:page()
  local editmode=(prim~=nil)

  local width fwidth=tonumber(values.fwidth)

  if not fwidth or fwidth<0 or fwidth>1 then
    width=fs.x
    fwidth=1
  else
    width=fwidth*fs.x
  end

  -- spacing
  local s=Spacing[values.size]
  local h=Height[values.size]
  if not s or not h then
    init_spacing(model)
    s=Spacing[values.size]
    h=Height[values.size]
  end

  local bheight=measure_height(model,values.btext,values.size,width-2*s)

  -- location
  if not pos then
    x1=fs.x/2-width/2
    x2=fs.x/2+width/2
    y2=fs.y/2
    y1=y2-h-bheight-3.8*s
  else
    x1=pos.x
    x2=x1+width
    y2=pos.y
    y1=y2-h-bheight-3.8*s
  end
  if fwidth>.99 then x1,x2=0,fs.x end

  -- header text
  pos=V(x1+s, y2-s)
  local ht= ipe.Text(model.attributes, values.htext, pos, width-2*s)
  ht:set('stroke', 'white')
  ht:set('textsize', values.size)

  -- body text
  pos=V(x1+s, y2-s-h-2*s)
  local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
  bt:set('textsize', values.size)

  -- header box
  local shape1 = { boxshape_roundTR(V(x1,y2-h-2*s), V(x2,y2)) }
  local hb = path(model, shape1,
    {pathmode='filled', fill=values.hcolor, stroke="white"})
    hb:set('pathmode', 'filled', "white", values.hcolor)

  -- body box
  local shape2 = { boxshape_roundLL(V(x1,y1), V(x2,y2-h-2*s)) }
  local bb = path(model, shape2,
    {pathmode='filled', fill=values.bcolor, stroke="white"})

  -- group object
  local elements={hb,bb,ht,bt}
  local obj=ipe.Group(elements)
  if values.pinned then obj:set('pinned', 'horizontal') end

  if editmode then
    local t={original=p[prim]:clone(),
        label="edit tabbed text",
        pno=model.pno,
        vno=model.vno,
        primary=prim,
        final=obj }
    t.undo = function (t, doc)
           doc[t.pno]:replace(t.primary, t.original)
         end
    t.redo = function (t, doc)
           doc[t.pno]:replace(t.primary, t.final)
         end
    model:register(t)
  else
    model:creation("create tabbed text", obj)
    -- model.doc:runLatex() -- may crash the thing
  end

end

-- create the dialog for editing box properties
local function box_property_dialog(model)
  local colors = model.doc:sheets():allNames("color")
  local pens= model.doc:sheets():allNames("pen")
  local pathmodes = {"stroked", "strokedfilled", "filled"}
  local d = ipeui.Dialog(model.ui:win(), "Edit box properties")

  d:add("rounded", "checkbox", { label="Round Corner"}, 1, 1)
  d:add("pointer", "checkbox", { label="Pointer"}, 1, 2)
  d:add("mlabel", "label", { label="Mode"}, 2, 1)
  d:add("pathmode", "combo", pathmodes, 2, 2)
  d:add("flabel", "label", { label="Fill Color" }, 3, 1)
  d:add("fill", "combo", colors, 3, 2)
  d:add("slabel", "label", { label="Stroke Color" }, 4, 1)
  d:add("stroke", "combo", colors, 4, 2)
  d:add("plabel", "label", { label="Line Width"}, 5, 1)
  d:add("pen", "combo", pens, 5, 2)
  d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 1)
  d:add("ok", "button", { label="&Ok", action="accept" }, 6, 2)
  d:setStretch("column", 2, 1)
  return d
end

-- edit a box object
local function edit_box(model, prim)
  local colors = model.doc:sheets():allNames("color")
  local pens= model.doc:sheets():allNames("pen")
  local pathmodes = {"stroked", "strokedfilled", "filled"}

  local p=model:page()
  local elements=p[prim]:elements()
  local bb=elements[1]
  local bbs=bb:shape()[1]

  local d=box_property_dialog(model)

  -- default values
  d:set("rounded", #bbs>=7)
  d:set("pathmode", indexOf(bb:get('pathmode'),pathmodes))
  d:set("pointer", #bbs==6 or #bbs==11)
  if indexOf(bb:get('stroke'),colors) then
    d:set("stroke", indexOf(bb:get('stroke'),colors) )
  elseif model.attributes.stroke then
    d:set("stroke", indexOf(model.attributes.stroke,colors) )
  end
  if indexOf(bb:get('fill'),colors) then
    d:set("fill", indexOf(bb:get('fill'),colors) )
  elseif not model.attributes.fill then
    d:set("fill", indexOf(model.attributes.fill,colors) )
  end
  if indexOf(bb:get('pen'),pens) then
    d:set("pen", indexOf(bb:get('pen'),pens) )
  elseif not model.attributes.pen then
    d:set("pen", indexOf(model.attributes.pen,pens) )
  end

  local r = d:execute(BOX_DIALOG_SIZE)
  if not r then return end
  local pathmode=pathmodes[d:get("pathmode")]
  local stroke=colors[d:get("stroke")]
  local fill=colors[d:get("fill")]
  local pen=pens[d:get("pen")]

  local boxshape
  if d:get('rounded') and d:get('pointer') then
    boxshape=boxshape_round_pointer
  elseif d:get('rounded') and not d:get('pointer') then
    boxshape=boxshape_round
  elseif not d:get('rounded') and d:get('pointer') then
    boxshape=boxshape_square_pointer
  else
    boxshape=boxshape_square
  end

  -- v1=BL, v2=TR, v3=P1, v4=P2, v5=P3. Pointer=(P1,P2,P3)
  local v1,v2,v3,v4,v5,shape
  if #bbs==3 then
    v1=bbs[1][1];v2=bbs[2][2]
    shape={ boxshape(v1,v2) }
  elseif #bb:shape()[1]==6 then
    v1=bbs[1][1];v3=bbs[2][2]; v4=bbs[3][2]; v5=bbs[4][2];
    v2=bbs[5][2];
    shape={ boxshape(v1,v2,v3,v4,v5) }
  elseif #bb:shape()[1]==8 then
    v1=V(bbs[5][1].x,bbs[3][1].y)
    v2=V(bbs[1][1].x,bbs[7][1].y)
    shape={ boxshape(v1,v2) }
  elseif #bb:shape()[1]==11 then
    v1=V(bbs[5][1].x,bbs[3][1].y)
    v2=V(bbs[1][1].x,bbs[7][1].y)
    v3=bbs[7][2]; v4=bbs[8][2]; v5=bbs[9][2];
    shape={ boxshape(v1,v2,v3,v4,v5) }
  end

  local obj = path(model, shape,
    {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})

  elements[1]=obj
  local final = ipe.Group(elements)
  final:setMatrix(p[prim]:matrix())

  local t = { label="edit box", pno=model.pno, vno=model.vno,
          layer=p:active(model.vno),
          original=p[prim]:clone(),
          primary=prim, final=final }
  t.undo = function (t, doc)
         doc[t.pno]:replace(t.primary, t.original)
       end
  t.redo = function (t, doc)
         doc[t.pno]:replace(t.primary, t.final)
       end
  model:register(t)
end

-- Edit a group object
local function action_edit_group(model,prim,obj)
  local otype,values,pos=parse_group_values(model,prim)
  if otype==UNKNOWN then
    model:warning("Cannot edit this object")
    return
  elseif otype==TABBED_TEXT then
    local newvalues=edit_tabbed_values(model, values)
    if not newvalues then return end
    if newvalues.htext=="" then
      newvalues.rounded=true
      create_boxed(model,newvalues,pos,prim)
    else
      create_tabbed(model,newvalues,pos,prim)
    end
  elseif otype==BOXED_OTHER or otype==BOXED_TEXT then
    edit_box(model,prim)
  end
end

-- modify the global edit action
function _G.MODEL:action_edit()
  local p = self:page()
  local prim = p:primarySelection()
  if not prim then self.ui:explain("no selection") return end
  local obj = p[prim]
  if obj:type() == "text" then
    self:action_edit_text(prim, obj)
  elseif obj:type() == "path" then
    self:action_edit_path(prim, obj)
  elseif obj:type() == "group" then
    action_edit_group(self, prim, obj)
  else
    self:warning('cannot edit this object')
  end
end

-- Run to create a new object
function tabbedboxed(model)
  local values=edit_tabbed_values(model)
  if not values then return end
  if values.htext=="" then
    values.rounded=true
    create_boxed(model,values)
  else
    create_tabbed(model,values)
  end
end

-- deselect all selected
function deselectAll(model)
  local doc = model.doc
  model.ui:finishTool() -- in case editing a path
  for i,p in doc:pages() do
    p:deselectAll()
  end
end

-- box the selected objects
function boxit(model)
  local p=model:page()
  local box = ipe.Rect()
  local elements={0}
  for i,obj,sel,layer in p:objects() do
    if sel then
      box:add(p:bbox(i))
      elements[#elements+1]=obj:clone()
    end
  end
  if #elements==1 then
    model.ui:explain('No selection to box')
    return
  end
  local s=8
  local layout = model.doc:sheets():find("layout")
  local maxx=layout.framesize.x

  local x1=box:bottomLeft().x-s
  if x1 < 0 then x1 = 0 end
  local y1=box:bottomLeft().y-s

  local x2=box:topRight().x+s
  if x2 > maxx then x2 = maxx end
  local y2=box:topRight().y+s

  local d=box_property_dialog(model)

  local colors = model.doc:sheets():allNames("color")
  local pens= model.doc:sheets():allNames("pen")
  local pathmodes = {"stroked", "strokedfilled", "filled"}

  -- default values
  d:set("rounded", true)

  d:set("pathmode", indexOf(PREFERRED_BOX_MODE,pathmodes))

  if indexOf("box_border",colors) then
    d:set("stroke", indexOf("box_border",colors))
  elseif not model.attributes.fill then
    d:set("stroke", indexOf(model.attributes.fill,colors) )
  end

  if indexOf("box_fill",colors) then
    d:set("fill", indexOf("box_fill",colors))
  elseif not model.attributes.fill then
    d:set("fill", indexOf(model.attributes.fill,colors) )
  end
  if indexOf("boxborder",pens) then
    d:set("pen", indexOf("boxborder",pens))
  elseif not model.attributes.pen then
    d:set("pen", indexOf(model.attributes.pen,pens) )
  end

  local r = d:execute(BOX_DIALOG_SIZE)

  if not r then return end
  local pathmode=pathmodes[d:get("pathmode")]
  local stroke=colors[d:get("stroke")]
  local fill=colors[d:get("fill")]
  local pen=pens[d:get("pen")]

  local boxshape
  if d:get('rounded') and d:get('pointer') then
    boxshape=boxshape_round_pointer
  elseif d:get('rounded') and not d:get('pointer') then
    boxshape=boxshape_round
  elseif not d:get('rounded') and d:get('pointer') then
    boxshape=boxshape_square_pointer
  else
    boxshape=boxshape_square
  end

  local shape = { boxshape(V(x1,y1), V(x2,y2)) }

  local obj = path(model, shape,
    {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})

  elements[1]=obj
  local final = ipe.Group(elements)

  local t = { label="add box", pno=model.pno, vno=model.vno,
          layer=p:active(model.vno), object=obj,
          selection=model:selection(),
          undo=_G.revertOriginal,
          original=p:clone(),
          final=final }
  t.redo = function (t, doc)
         local p = doc[t.pno]
         for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
         p:insert(nil, t.final, 1, t.layer)
       end
  model:register(t)

end
--

-- set text object to minipage
function to_minipage(model)
  set_text_minipage(model, true)
end

-- set text object to label
function to_label(model)
  set_text_minipage(model, false)
end

-- set text object minipage property
function set_text_minipage(model, minipage)
  local p = model:page()
  local selection = model:selection()
  local selected = {}
  if p:hasSelection()==false then return end
  local sel
  local j=0
  for _,i in ipairs(selection) do
    j=j+1
    if p[i]:type()~='text' then
      model:warning("Not all text objects")
      return
    end
    if j>1 then
      model:warning("So far works only with one text object")
    end
    sel=i
  end

  local final = p[sel]:clone()
  final:set('minipage', minipage)

  p:deselectAll()
  local t = { label="group",
        pno = model.pno,
        vno = model.vno,
        original = p:clone(),
        selection = selection,
        layer = p:active(model.vno),
        final = final,
        undo = _G.revertOriginal,
      }
  t.redo = function (t, doc)
       local p = doc[t.pno]
       for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
       p:insert(nil, t.final, 1, t.layer)
     end
  model:register(t)
end

-- merge text objects into a single text object
function merge_text(model)
  local p = model:page()
  local selection = model:selection()
  local selected = {}
  local key = {}
  local rank = {}
  if p:hasSelection()==false then return end
  for _,i in ipairs(selection) do
    if p[i]:type()~='text' then
      model:warning("Not all text objects")
      return
    end
    selected[#selected+1]=i
    key[#key+1]=p[i]:position().y
    rank[#rank+1]=1
  end

  -- sort the selected elements
  for i=2,#selected do
    for j=1,i-1 do
      if key[j]>key[i] then
        rank[i]=rank[i]+1
      else
        rank[j]=rank[j]+1
      end
    end
  end

  local ptr={}
  for j=1,#selected do ptr[rank[j]]=j end

  -- concatenate the strings
  local str=''
  for j=1,#selected do str=str .. p[selected[ptr[j]]]:text() .. '\n' end

  local final = p[ptr[1]]:clone()
  final:setText(str)
  p:deselectAll()
  local t = { label="group",
        pno = model.pno,
        vno = model.vno,
        original = p:clone(),
        selection = selection,
        layer = p:active(model.vno),
        final = final,
        undo = _G.revertOriginal,
      }
  t.redo = function (t, doc)
       local p = doc[t.pno]
       for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
       p:insert(nil, t.final, 1, t.layer)
     end
  model:register(t)
end

methods = {
  { label = "Box It", run=boxit},
  { label = "Tabbed/Boxed Text", run=tabbedboxed},
  { label = "Deselect All", run=deselectAll},
  { label = "Merge Text", run=merge_text},
  { label = "Convert to Minipage", run=to_minipage},
  { label = "Convert to Label", run=to_label},
}


More information about the Ipe-discuss mailing list