1 ----------------------------------------------------------------------
2 -- presentation goodies for Ipe
8 This ipelet adds a few goodies for presentation, including
9 1. Ability to create beamer-like boxes with/without header
10 2. Add a framed box for the selected objects (including text)
11 3. A function to deselect all selected on all pages
12 4. The boxes can be edited as text/path objects (press E)
13 5. A few items can be added to the style sheet to add preferred
14 symbolics for box colors etc. This has the benefit that this
15 preferences can be changed to affect all boxes (see below).
17 The design of these boxes is from Martin Nöllenburg's presentation
18 example posted on the Ipe7 wiki page.
20 STYLESHEET (CHANGING PREFERRED SETTINGS)
22 Example style sheet to cascade with presentation.isy is as follows.
24 ---- content of example.isy ---
26 <!DOCTYPE ipestyle SYSTEM "ipe.dtd">
27 <ipestyle name="mine">
28 <color name="tab_header" value="0 0 0.5"/>
29 <color name="tab_body" value="0.827"/>
30 <color name="box_fill" value="0.827"/>
31 <color name="box_border" value="0 0 0"/>
32 <pen name="boxborder" value="2.4"/>
37 ---- end content of example.isy ---
40 tab_header= color of the tab header in a tabbed box
41 tab_body = color of the tab body in a tabbed box
42 box_fill = fill color a box
43 box_border= color of the box border
44 boxborder = linewidth of the box border
46 The preferred box mode (stroked/filled/strokedfilled) can be changed by
47 changing the hard-wired value (no stylesheet option) PREFERRED_BOX_MODE below
49 With the above style sheet, one can start an empty presentation using
50 ipe -sheet presentation -sheet /path/to/example.isy
54 Shortcuts to these functions can be changed as for other ipelets:
55 shortcuts.ipelet_x_presentation = "Key"
56 where x is the index (starting 1) of the sub-menu for the function
60 version 0. Initial Release. Zhengdao Wang 2010
61 version 1. add a line here if a change is made
65 This file can be distributed and modified under the terms of the GNU General
66 Public License as published by the Free Software Foundation; either version
67 3, or (at your option) any later version.
69 This file is distributed in the hope that it will be useful, but WITHOUT ANY
70 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
71 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
76 ----------------------------------------------------------------------
78 label = "Presentation"
81 Presentation Goodies: Add boxes around objects, deselect all,
82 add tabbed/boxed text.
88 -- table storing the height of the first line box
93 local c=10 -- corner size
100 local PREFERRED_BOX_MODE="strokedfilled" -- or stroked or filled
102 local BOX_DIALOG_SIZE={400,140}
104 -- "spline" is recommended here, as it should work with most
106 local SPLINE_TYPE_NAME="spline"
107 -- local SPLINE_TYPE_NAME="oldspline"
108 -- local SPLINE_TYPE_NAME="bezier"
110 -- initialize the Height table
111 local function init_spacing(model)
115 local sizes = model.doc:sheets():allNames("textsize")
116 for i,size in ipairs(sizes) do
117 obj=ipe.Text({textsize=size, minipage=true}, "ABC\n\nDEF", V(xx,xx), xx)
118 local layer=p:active(model.vno)
119 p:insert(nil, obj, nil, layer)
120 obj=ipe.Text({textsize=size, minipage=true}, "ABC", V(xx,xx), xx)
121 p:insert(nil, obj, nil, layer)
126 Height[iH[i]]=xx-p:bbox(#p):bottomLeft().y
128 Spacing[iH[i]]=(xx-p:bbox(#p):bottomLeft().y)/2-Height[iH[i]]
134 -- generate a path with given properties
135 local function path(model, shape, props)
137 for k,v in pairs(props) do
138 oldvalues[k]=model.attributes[k]
139 model.attributes[k]=v
141 local obj = ipe.Path(model.attributes, shape)
143 for k,v in pairs(props) do
144 model.attributes[k]=oldvalues[k]
146 if props.pen then obj:set('pen', props.pen) end
147 obj:set('pathmode', props.pathmode, props.stroke, props.fill)
151 -- create a square box #shape=3
152 local function boxshape_square(v1, v2)
153 return { type="curve", closed=true;
154 { type="segment"; v1, V(v1.x, v2.y) }, -- L
155 { type="segment"; V(v1.x, v2.y), v2 }, -- T
156 { type="segment"; v2, V(v2.x, v1.y) } } -- R
159 -- create a square box with a pointer #shape=6
160 local function boxshape_square_pointer(v1, v2, v3, v4, v5)
163 local v3=v3 or V(v1.x+.4*dx, v2.y)
164 local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
165 local v5=v5 or V(v1.x+.5*dx,v2.y)
166 return { type="curve", closed=true;
167 { type="segment"; v1, V(v1.x, v2.y) }, -- L
168 { type="segment"; V(v1.x, v2.y), v3}, -- T
169 { type="segment"; v3, v4}, -- P
170 { type="segment"; v4, v5}, --P
171 { type="segment"; v5, v2}, -- T
172 { type="segment"; v2, V(v2.x, v1.y) } } -- R
175 -- create a header box: round corner on topRight
176 local function boxshape_roundTR(v1, v2)
177 return { type="curve", closed=true;
178 { type="segment"; v1, V(v1.x, v2.y) },
179 { type="segment"; V(v1.x, v2.y), V(v2.x-c, v2.y) },
180 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2, v2.y),
181 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) },
182 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y) } }
185 -- create a body box: round corner on bottom Left
186 local function boxshape_roundLL(v1, v2)
187 return { type="curve", closed=true;
188 { type="segment"; v2, V(v2.x, v1.y) },
189 { type="segment"; V(v2.x, v1.y), V(v1.x+c, v1.y) },
190 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
191 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) },
192 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y) } }
195 -- create a body box: 4 round corners #shape=8
196 local function boxshape_round(v1, v2)
197 return { type="curve", closed=true;
198 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
199 { type=SPLINE_TYPE_NAME; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
200 V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
201 { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
202 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
203 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
204 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
205 { type=SPLINE_TYPE_NAME; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
206 V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
207 { type="segment"; V(v1.x+c, v2.y), V(v2.x-c, v2.y) }, -- T
208 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
209 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
213 -- create a body box: 4 round corners, with pointer #shape=11
214 local function boxshape_round_pointer(v1, v2, v3, v4, v5)
217 local v3=v3 or V(v1.x+.4*dx, v2.y)
218 local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
219 local v5=v5 or V(v1.x+.5*dx,v2.y)
220 return { type="curve", closed=true;
221 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
222 { type=SPLINE_TYPE_NAME; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
223 V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
224 { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
225 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
226 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
227 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
228 { type=SPLINE_TYPE_NAME; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
229 V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
230 { type="segment"; V(v1.x+c, v2.y), v3}, -- T
231 { type="segment"; v3, v4}, -- P
232 { type="segment"; v4, v5}, --P
233 { type="segment"; v5, V(v2.x-c, v2.y)}, -- T
234 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
235 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
239 -- parse the values from a group obj
240 local function parse_group_values(model,prim)
241 local fs = model.doc:sheets():find("layout").framesize
243 local bbox=p:bbox(prim)
244 local pos=V(bbox:bottomLeft().x, bbox:topRight().y)
246 local elements=p[prim]:elements()
248 local hb,bb,ht,bt=elements[1],elements[2],elements[3],elements[4]
249 if hb:type()=="path" and bb:type()=="path" and
250 ht:type()=="text" and bt:type()=="text" then
251 local values={htext=ht:text(),
253 pinned=(p[prim]:get("pinned")=="horizontal"),
254 fwidth=string.format('%.2f',
255 (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
256 hcolor=hb:get("fill"),
257 bcolor=bb:get("fill"),
258 size=ht:get("textsize")}
259 return TABBED_TEXT,values,pos
262 local bb,bt=elements[1],elements[2]
263 if bb:type()=="path" and #bb:shape()==1 and
264 bb:shape()[1].closed==true and
265 (#bb:shape()[1]==3 or #bb:shape()[1]==8 or
266 #bb:shape()[1]==6 or #bb:shape()[1]==11) then
267 if bt:type()=="text" then
268 local values={btext=bt:text(),
269 pinned=(p[prim]:get("pinned")=="horizontal"),
270 size=bt:get("textsize"),
271 fwidth=string.format('%.2f',
272 (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
273 bcolor=bb:get("fill")}
274 if #bb:shape()[1]==6 then
275 pos=V(pos.x,bb:shape()[1][1][2].y)
276 elseif #bb:shape()[1]==11 then
277 pos=V(pos.x,bb:shape()[1][10][2].y)
279 return BOXED_TEXT,values,pos
288 function mainWindow(model)
289 if model.ui.win == nil then
292 return model.ui:win()
296 -- Edit the values for the frame
297 local function edit_tabbed_values(model,values)
298 local d = ipeui.Dialog(mainWindow(model), "Create tabbed text")
299 local colors = model.doc:sheets():allNames("color")
300 local sizes = model.doc:sheets():allNames("textsize")
301 d:add("hlabel", "label", { label="Enter Header" }, 1, 1, 1, 1)
302 d:add("hcolor", "combo", colors, 1, 4)
303 d:add("htext", "input", { syntax="latex" }, 2, 1, 1, 4)
304 d:add("blabel", "label", { label="Enter Body"}, 3, 1, 1, 3)
305 d:add("bcolor", "combo", colors, 3, 4)
306 d:add("btext", "text", { syntax="latex" }, 4, 1, 1, 4)
307 d:add("size", "combo", sizes, 5, 1)
308 d:add("wlabel", "label", { label="width [0-1]"}, 5, 2, 1, 2)
309 d:add("fwidth", "input", {size=2}, 5, 3, 1, 1)
310 d:add("pinned", "checkbox", { label="pinned"}, 5, 4)
311 d:add("ok", "button", { label="&Ok", action="accept" }, 6, 4)
312 d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 3)
313 _G.addEditorField(d, "btext", 6, 2)
314 d:setStretch("row", 2, 1)
315 d:setStretch("column", 1, 1)
316 d:set("fwidth", "0.8")
319 if indexOf("tab_header", colors) then
320 d:set("hcolor", indexOf("tab_header", colors))
321 elseif indexOf("darkblue", colors) then
322 d:set("hcolor", indexOf("darkblue", colors))
324 d:set("hcolor", indexOf("black", colors))
327 if indexOf("tab_body", colors) then
328 d:set("bcolor", indexOf("tab_body", colors))
329 elseif indexOf("lightgray", colors) then
330 d:set("bcolor", indexOf("lightgray", colors))
332 d:set("bcolor", indexOf("white", colors))
336 for k,v in pairs(values) do
337 if k=="hcolor" or k=="bcolor" then v=indexOf(v, colors) end
338 if k=="size" then v=indexOf(v, sizes) end
343 local r = d:execute(prefs.editor_size)
344 if not r then return end
346 newvalues.htext=d:get("htext")
347 newvalues.btext=d:get("btext")
348 newvalues.pinned=d:get("pinned")
349 newvalues.fwidth=d:get("fwidth")
350 newvalues.size=sizes[d:get("size")]
351 newvalues.hcolor=colors[d:get("hcolor")]
352 newvalues.bcolor=colors[d:get("bcolor")]
353 -- if newvalues.fwidth=="" or tonumber(newvalues.fwidth)>.99 then
354 -- newvalues.pinned=true
359 -- measure the height a piece of given text
360 local function measure_height(model,text,size,width)
362 local obj= ipe.Text(model.attributes, text, V(0,0), width)
363 obj:set('textsize', size)
364 local layer=p:active(model.vno)
365 p:insert(nil, obj, nil, layer)
366 if not model.doc:runLatex() then
370 local bbox=p:bbox(#p)
372 return bbox:topRight().y-bbox:bottomLeft().y
376 local function create_boxed(model,values, pos, prim)
377 local fs = model.doc:sheets():find("layout").framesize
378 local p = model:page()
379 local editmode=(prim~=nil)
381 local width fwidth=tonumber(values.fwidth)
383 if not fwidth or fwidth<0 or fwidth>1 then
391 local s=Spacing[values.size]
392 local h=Height[values.size]
393 if not s or not h then
395 s=Spacing[values.size]
396 h=Height[values.size]
399 local bheight=measure_height(model,values.btext,values.size,width-2*s)
413 if fwidth>.99 then x1,x2=0,fs.x end
417 local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
418 bt:set('textsize', values.size)
422 if values.rounded then
423 shape2 = { boxshape_round(V(x1,y1), V(x2,y2)) }
425 shape2 = { boxshape_square(V(x1,y1), V(x2,y2)) }
427 local bb = path(model, shape2,
428 {pathmode='filled', fill=values.bcolor, stroke="white"})
431 local elements={bb,bt}
432 local obj=ipe.Group(elements)
433 -- obj:setMatrix(p[prim]:matrix()) -- currently not working
434 if values.pinned then obj:set('pinned', 'horizontal') end
437 local t={original=p[prim]:clone(),
438 label="edit boxed text",
443 t.undo = function (t, doc)
444 doc[t.pno]:replace(t.primary, t.original)
446 t.redo = function (t, doc)
447 doc[t.pno]:replace(t.primary, t.final)
451 model:creation("create boxed text", obj)
452 -- model.doc:runLatex() -- may crash the thing
456 -- Create the requested object from values
457 local function create_tabbed(model,values, pos, prim)
458 local fs = model.doc:sheets():find("layout").framesize
459 local p = model:page()
460 local editmode=(prim~=nil)
462 local width fwidth=tonumber(values.fwidth)
464 if not fwidth or fwidth<0 or fwidth>1 then
472 local s=Spacing[values.size]
473 local h=Height[values.size]
474 if not s or not h then
476 s=Spacing[values.size]
477 h=Height[values.size]
480 local bheight=measure_height(model,values.btext,values.size,width-2*s)
487 y1=y2-h-bheight-3.8*s
492 y1=y2-h-bheight-3.8*s
494 if fwidth>.99 then x1,x2=0,fs.x end
498 local ht= ipe.Text(model.attributes, values.htext, pos, width-2*s)
499 ht:set('stroke', 'white')
500 ht:set('textsize', values.size)
503 pos=V(x1+s, y2-s-h-2*s)
504 local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
505 bt:set('textsize', values.size)
508 local shape1 = { boxshape_roundTR(V(x1,y2-h-2*s), V(x2,y2)) }
509 local hb = path(model, shape1,
510 {pathmode='filled', fill=values.hcolor, stroke="white"})
511 hb:set('pathmode', 'filled', "white", values.hcolor)
514 local shape2 = { boxshape_roundLL(V(x1,y1), V(x2,y2-h-2*s)) }
515 local bb = path(model, shape2,
516 {pathmode='filled', fill=values.bcolor, stroke="white"})
519 local elements={hb,bb,ht,bt}
520 local obj=ipe.Group(elements)
521 if values.pinned then obj:set('pinned', 'horizontal') end
524 local t={original=p[prim]:clone(),
525 label="edit tabbed text",
530 t.undo = function (t, doc)
531 doc[t.pno]:replace(t.primary, t.original)
533 t.redo = function (t, doc)
534 doc[t.pno]:replace(t.primary, t.final)
538 model:creation("create tabbed text", obj)
539 -- model.doc:runLatex() -- may crash the thing
544 -- create the dialog for editing box properties
545 local function box_property_dialog(model)
546 local colors = model.doc:sheets():allNames("color")
547 local pens= model.doc:sheets():allNames("pen")
548 local pathmodes = {"stroked", "strokedfilled", "filled"}
549 local d = ipeui.Dialog(mainWindow(model), "Edit box properties")
551 d:add("rounded", "checkbox", { label="Round Corner"}, 1, 1, 1, 1)
552 d:add("pointer", "checkbox", { label="Pointer"}, 1, 2, 1, 1)
553 d:add("mlabel", "label", { label="Mode"}, 2, 1)
554 d:add("pathmode", "combo", pathmodes, 2, 2)
555 d:add("flabel", "label", { label="Fill Color" }, 3, 1)
556 d:add("fill", "combo", colors, 3, 2)
557 d:add("slabel", "label", { label="Stroke Color" }, 4, 1)
558 d:add("stroke", "combo", colors, 4, 2)
559 d:add("plabel", "label", { label="Line Width"}, 5, 1)
560 d:add("pen", "combo", pens, 5, 2)
561 d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 1)
562 d:add("ok", "button", { label="&Ok", action="accept" }, 6, 2)
563 d:setStretch("column", 2, 1)
568 local function edit_box(model, prim)
569 local colors = model.doc:sheets():allNames("color")
570 local pens= model.doc:sheets():allNames("pen")
571 local pathmodes = {"stroked", "strokedfilled", "filled"}
574 local elements=p[prim]:elements()
576 local bbs=bb:shape()[1]
578 local d=box_property_dialog(model)
581 d:set("rounded", #bbs>=7)
582 d:set("pathmode", indexOf(bb:get('pathmode'),pathmodes))
583 d:set("pointer", #bbs==6 or #bbs==11)
584 if indexOf(bb:get('stroke'),colors) then
585 d:set("stroke", indexOf(bb:get('stroke'),colors) )
586 elseif not model.attributes.stroke then
587 d:set("stroke", indexOf(model.attributes.stroke,colors) )
589 if indexOf(bb:get('fill'),colors) then
590 d:set("fill", indexOf(bb:get('fill'),colors) )
591 elseif not model.attributes.fill then
592 d:set("fill", indexOf(model.attributes.fill,colors) )
594 if indexOf(bb:get('pen'),pens) then
595 d:set("pen", indexOf(bb:get('pen'),pens) )
596 elseif not model.attributes.pen then
597 d:set("pen", indexOf(model.attributes.pen,pens) )
600 local r = d:execute(BOX_DIALOG_SIZE)
601 if not r then return end
602 local pathmode=pathmodes[d:get("pathmode")]
603 local stroke=colors[d:get("stroke")]
604 local fill=colors[d:get("fill")]
605 local pen=pens[d:get("pen")]
608 if d:get('rounded') and d:get('pointer') then
609 boxshape=boxshape_round_pointer
610 elseif d:get('rounded') and not d:get('pointer') then
611 boxshape=boxshape_round
612 elseif not d:get('rounded') and d:get('pointer') then
613 boxshape=boxshape_square_pointer
615 boxshape=boxshape_square
618 -- v1=BL, v2=TR, v3=P1, v4=P2, v5=P3. Pointer=(P1,P2,P3)
619 local v1,v2,v3,v4,v5,shape
621 v1=bbs[1][1];v2=bbs[2][2]
622 shape={ boxshape(v1,v2) }
623 elseif #bb:shape()[1]==6 then
624 v1=bbs[1][1];v3=bbs[2][2]; v4=bbs[3][2]; v5=bbs[4][2];
626 shape={ boxshape(v1,v2,v3,v4,v5) }
627 elseif #bb:shape()[1]==8 then
628 v1=V(bbs[5][1].x,bbs[3][1].y)
629 v2=V(bbs[1][1].x,bbs[7][1].y)
630 shape={ boxshape(v1,v2) }
631 elseif #bb:shape()[1]==11 then
632 v1=V(bbs[5][1].x,bbs[3][1].y)
633 v2=V(bbs[1][1].x,bbs[7][1].y)
634 v3=bbs[7][2]; v4=bbs[8][2]; v5=bbs[9][2];
635 shape={ boxshape(v1,v2,v3,v4,v5) }
638 local obj = path(model, shape,
639 {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
642 local final = ipe.Group(elements)
643 final:setMatrix(p[prim]:matrix())
645 local t = { label="edit box", pno=model.pno, vno=model.vno,
646 layer=p:active(model.vno),
647 original=p[prim]:clone(),
648 primary=prim, final=final }
649 t.undo = function (t, doc)
650 doc[t.pno]:replace(t.primary, t.original)
652 t.redo = function (t, doc)
653 doc[t.pno]:replace(t.primary, t.final)
658 -- Edit a group object
659 local function action_edit_group(model,prim,obj)
660 local otype,values,pos=parse_group_values(model,prim)
661 if otype==UNKNOWN then
662 model:warning("Cannot edit this object")
664 elseif otype==TABBED_TEXT then
665 local newvalues=edit_tabbed_values(model, values)
666 if not newvalues then return end
667 if newvalues.htext=="" then
668 newvalues.rounded=true
669 create_boxed(model,newvalues,pos,prim)
671 create_tabbed(model,newvalues,pos,prim)
673 elseif otype==BOXED_OTHER or otype==BOXED_TEXT then
678 -- saving the old function
679 function _G.MODEL:presentation_backup_actinon_edit () end
680 _G.MODEL.presentation_backup_action_edit = _G.MODEL.action_edit
682 -- modify the global edit action
683 function _G.MODEL:action_edit()
684 local p = self:page()
685 local prim = p:primarySelection()
687 self:presentation_backup_action_edit()
691 if obj:type() == "group" then
692 action_edit_group(self, prim, obj)
694 self:presentation_backup_action_edit()
698 -- Run to create a new object
699 function tabbedboxed(model)
700 local values=edit_tabbed_values(model)
701 if not values then return end
702 if values.htext=="" then
704 create_boxed(model,values)
706 create_tabbed(model,values)
710 -- deselect all selected
711 function deselectAll(model)
712 local doc = model.doc
713 for i,p in doc:pages() do
718 -- box the selected objects
719 function boxit(model)
721 local box = ipe.Rect()
723 for i,obj,sel,layer in p:objects() do
726 elements[#elements+1]=obj:clone()
730 model.ui:explain('No selection to box')
734 local layout = model.doc:sheets():find("layout")
735 -- local maxx=layout.framesize.x
737 local x1=box:bottomLeft().x-s
738 -- if x1 < 0 then x1 = 0 end
739 local y1=box:bottomLeft().y-s
741 local x2=box:topRight().x+s
742 -- if x2 > maxx then x2 = maxx end
743 local y2=box:topRight().y+s
745 local d=box_property_dialog(model)
747 local colors = model.doc:sheets():allNames("color")
748 local pens= model.doc:sheets():allNames("pen")
749 local pathmodes = {"stroked", "strokedfilled", "filled"}
752 d:set("rounded", true)
754 d:set("pathmode", indexOf(PREFERRED_BOX_MODE,pathmodes))
756 if indexOf("box_border",colors) then
757 d:set("stroke", indexOf("box_border",colors))
758 elseif model.attributes.stroke then
759 d:set("stroke", indexOf(model.attributes.stroke,colors) )
762 if indexOf("box_fill",colors) then
763 d:set("fill", indexOf("box_fill",colors))
764 elseif model.attributes.fill then
765 d:set("fill", indexOf(model.attributes.fill,colors) )
768 if indexOf("boxborder",pens) then
769 d:set("pen", indexOf("boxborder",pens))
770 elseif model.attributes.pen then
771 d:set("pen", indexOf(model.attributes.pen,pens) )
774 local r = d:execute(BOX_DIALOG_SIZE)
776 if not r then return end
777 local pathmode=pathmodes[d:get("pathmode")]
778 local stroke=colors[d:get("stroke")]
779 local fill=colors[d:get("fill")]
780 local pen=pens[d:get("pen")]
783 if d:get('rounded') and d:get('pointer') then
784 boxshape=boxshape_round_pointer
785 elseif d:get('rounded') and not d:get('pointer') then
786 boxshape=boxshape_round
787 elseif not d:get('rounded') and d:get('pointer') then
788 boxshape=boxshape_square_pointer
790 boxshape=boxshape_square
793 local shape = { boxshape(V(x1,y1), V(x2,y2)) }
795 local obj = path(model, shape, {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
798 local final = ipe.Group(elements)
800 local t = { label="add box", pno=model.pno, vno=model.vno,
801 layer=p:active(model.vno), object=obj,
802 selection=model:selection(),
803 undo=_G.revertOriginal,
806 t.redo = function (t, doc)
808 for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
809 p:insert(nil, t.final, 1, t.layer)
815 { label = "Box It", run=boxit},
816 { label = "Tabbed/Boxed Text", run=tabbedboxed},
817 { label = "Deselect All", run=deselectAll},