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)
449 model:runLatex() -- Necessary to re-set the resources!
452 model:runLatex() -- Necessary to re-set the resources!
453 model:creation("create boxed text", obj)
457 -- Create the requested object from values
458 local function create_tabbed(model,values, pos, prim)
459 local fs = model.doc:sheets():find("layout").framesize
460 local p = model:page()
461 local editmode=(prim~=nil)
463 local width fwidth=tonumber(values.fwidth)
465 if not fwidth or fwidth<0 or fwidth>1 then
473 local s=Spacing[values.size]
474 local h=Height[values.size]
475 if not s or not h then
477 s=Spacing[values.size]
478 h=Height[values.size]
481 local bheight=measure_height(model,values.btext,values.size,width-2*s)
488 y1=y2-h-bheight-3.8*s
493 y1=y2-h-bheight-3.8*s
495 if fwidth>.99 then x1,x2=0,fs.x end
499 local ht= ipe.Text(model.attributes, values.htext, pos, width-2*s)
500 ht:set('stroke', 'white')
501 ht:set('textsize', values.size)
504 pos=V(x1+s, y2-s-h-2*s)
505 local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
506 bt:set('textsize', values.size)
509 local shape1 = { boxshape_roundTR(V(x1,y2-h-2*s), V(x2,y2)) }
510 local hb = path(model, shape1,
511 {pathmode='filled', fill=values.hcolor, stroke="white"})
512 hb:set('pathmode', 'filled', "white", values.hcolor)
515 local shape2 = { boxshape_roundLL(V(x1,y1), V(x2,y2-h-2*s)) }
516 local bb = path(model, shape2,
517 {pathmode='filled', fill=values.bcolor, stroke="white"})
520 local elements={hb,bb,ht,bt}
521 local obj=ipe.Group(elements)
522 if values.pinned then obj:set('pinned', 'horizontal') end
525 local t={original=p[prim]:clone(),
526 label="edit tabbed text",
531 t.undo = function (t, doc)
532 doc[t.pno]:replace(t.primary, t.original)
534 t.redo = function (t, doc)
535 doc[t.pno]:replace(t.primary, t.final)
537 model:runLatex() -- Necessary to re-set the resources!
540 model:runLatex() -- Necessary to re-set the resources!
541 model:creation("create tabbed text", obj)
542 -- model.doc:runLatex() -- may crash the thing
547 -- create the dialog for editing box properties
548 local function box_property_dialog(model)
549 local colors = model.doc:sheets():allNames("color")
550 local pens= model.doc:sheets():allNames("pen")
551 local pathmodes = {"stroked", "strokedfilled", "filled"}
552 local d = ipeui.Dialog(mainWindow(model), "Edit box properties")
554 d:add("rounded", "checkbox", { label="Round Corner"}, 1, 1, 1, 1)
555 d:add("pointer", "checkbox", { label="Pointer"}, 1, 2, 1, 1)
556 d:add("mlabel", "label", { label="Mode"}, 2, 1)
557 d:add("pathmode", "combo", pathmodes, 2, 2)
558 d:add("flabel", "label", { label="Fill Color" }, 3, 1)
559 d:add("fill", "combo", colors, 3, 2)
560 d:add("slabel", "label", { label="Stroke Color" }, 4, 1)
561 d:add("stroke", "combo", colors, 4, 2)
562 d:add("plabel", "label", { label="Line Width"}, 5, 1)
563 d:add("pen", "combo", pens, 5, 2)
564 d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 1)
565 d:add("ok", "button", { label="&Ok", action="accept" }, 6, 2)
566 d:setStretch("column", 2, 1)
571 local function edit_box(model, prim)
572 local colors = model.doc:sheets():allNames("color")
573 local pens= model.doc:sheets():allNames("pen")
574 local pathmodes = {"stroked", "strokedfilled", "filled"}
577 local elements=p[prim]:elements()
579 local bbs=bb:shape()[1]
581 local d=box_property_dialog(model)
584 d:set("rounded", #bbs>=7)
585 d:set("pathmode", indexOf(bb:get('pathmode'),pathmodes))
586 d:set("pointer", #bbs==6 or #bbs==11)
587 if indexOf(bb:get('stroke'),colors) then
588 d:set("stroke", indexOf(bb:get('stroke'),colors) )
589 elseif not model.attributes.stroke then
590 d:set("stroke", indexOf(model.attributes.stroke,colors) )
592 if indexOf(bb:get('fill'),colors) then
593 d:set("fill", indexOf(bb:get('fill'),colors) )
594 elseif not model.attributes.fill then
595 d:set("fill", indexOf(model.attributes.fill,colors) )
597 if indexOf(bb:get('pen'),pens) then
598 d:set("pen", indexOf(bb:get('pen'),pens) )
599 elseif not model.attributes.pen then
600 d:set("pen", indexOf(model.attributes.pen,pens) )
603 local r = d:execute(BOX_DIALOG_SIZE)
604 if not r then return end
605 local pathmode=pathmodes[d:get("pathmode")]
606 local stroke=colors[d:get("stroke")]
607 local fill=colors[d:get("fill")]
608 local pen=pens[d:get("pen")]
611 if d:get('rounded') and d:get('pointer') then
612 boxshape=boxshape_round_pointer
613 elseif d:get('rounded') and not d:get('pointer') then
614 boxshape=boxshape_round
615 elseif not d:get('rounded') and d:get('pointer') then
616 boxshape=boxshape_square_pointer
618 boxshape=boxshape_square
621 -- v1=BL, v2=TR, v3=P1, v4=P2, v5=P3. Pointer=(P1,P2,P3)
622 local v1,v2,v3,v4,v5,shape
624 v1=bbs[1][1];v2=bbs[2][2]
625 shape={ boxshape(v1,v2) }
626 elseif #bb:shape()[1]==6 then
627 v1=bbs[1][1];v3=bbs[2][2]; v4=bbs[3][2]; v5=bbs[4][2];
629 shape={ boxshape(v1,v2,v3,v4,v5) }
630 elseif #bb:shape()[1]==8 then
631 v1=V(bbs[5][1].x,bbs[3][1].y)
632 v2=V(bbs[1][1].x,bbs[7][1].y)
633 shape={ boxshape(v1,v2) }
634 elseif #bb:shape()[1]==11 then
635 v1=V(bbs[5][1].x,bbs[3][1].y)
636 v2=V(bbs[1][1].x,bbs[7][1].y)
637 v3=bbs[7][2]; v4=bbs[8][2]; v5=bbs[9][2];
638 shape={ boxshape(v1,v2,v3,v4,v5) }
641 local obj = path(model, shape,
642 {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
645 local final = ipe.Group(elements)
646 final:setMatrix(p[prim]:matrix())
648 local t = { label="edit box", pno=model.pno, vno=model.vno,
649 layer=p:active(model.vno),
650 original=p[prim]:clone(),
651 primary=prim, final=final }
652 t.undo = function (t, doc)
653 doc[t.pno]:replace(t.primary, t.original)
655 t.redo = function (t, doc)
656 doc[t.pno]:replace(t.primary, t.final)
661 -- Edit a group object
662 local function action_edit_group(model,prim,obj)
663 local otype,values,pos=parse_group_values(model,prim)
664 if otype==UNKNOWN then
665 model:warning("Cannot edit this object")
667 elseif otype==TABBED_TEXT then
668 local newvalues=edit_tabbed_values(model, values)
669 if not newvalues then return end
670 if newvalues.htext=="" then
671 newvalues.rounded=true
672 create_boxed(model,newvalues,pos,prim)
674 create_tabbed(model,newvalues,pos,prim)
676 elseif otype==BOXED_OTHER or otype==BOXED_TEXT then
681 -- saving the old function
682 function _G.MODEL:presentation_backup_actinon_edit () end
683 _G.MODEL.presentation_backup_action_edit = _G.MODEL.action_edit
685 -- modify the global edit action
686 function _G.MODEL:action_edit()
687 local p = self:page()
688 local prim = p:primarySelection()
690 self:presentation_backup_action_edit()
694 if obj:type() == "group" then
695 action_edit_group(self, prim, obj)
697 self:presentation_backup_action_edit()
701 -- Run to create a new object
702 function tabbedboxed(model)
703 local values=edit_tabbed_values(model)
704 if not values then return end
705 if values.htext=="" then
707 create_boxed(model,values)
709 create_tabbed(model,values)
713 -- deselect all selected
714 function deselectAll(model)
715 local doc = model.doc
716 for i,p in doc:pages() do
721 -- box the selected objects
722 function boxit(model)
724 local box = ipe.Rect()
726 for i,obj,sel,layer in p:objects() do
729 elements[#elements+1]=obj:clone()
733 model.ui:explain('No selection to box')
737 local layout = model.doc:sheets():find("layout")
738 -- local maxx=layout.framesize.x
740 local x1=box:bottomLeft().x-s
741 -- if x1 < 0 then x1 = 0 end
742 local y1=box:bottomLeft().y-s
744 local x2=box:topRight().x+s
745 -- if x2 > maxx then x2 = maxx end
746 local y2=box:topRight().y+s
748 local d=box_property_dialog(model)
750 local colors = model.doc:sheets():allNames("color")
751 local pens= model.doc:sheets():allNames("pen")
752 local pathmodes = {"stroked", "strokedfilled", "filled"}
755 d:set("rounded", true)
757 d:set("pathmode", indexOf(PREFERRED_BOX_MODE,pathmodes))
759 if indexOf("box_border",colors) then
760 d:set("stroke", indexOf("box_border",colors))
761 elseif model.attributes.stroke then
762 d:set("stroke", indexOf(model.attributes.stroke,colors) )
765 if indexOf("box_fill",colors) then
766 d:set("fill", indexOf("box_fill",colors))
767 elseif model.attributes.fill then
768 d:set("fill", indexOf(model.attributes.fill,colors) )
771 if indexOf("boxborder",pens) then
772 d:set("pen", indexOf("boxborder",pens))
773 elseif model.attributes.pen then
774 d:set("pen", indexOf(model.attributes.pen,pens) )
777 local r = d:execute(BOX_DIALOG_SIZE)
779 if not r then return end
780 local pathmode=pathmodes[d:get("pathmode")]
781 local stroke=colors[d:get("stroke")]
782 local fill=colors[d:get("fill")]
783 local pen=pens[d:get("pen")]
786 if d:get('rounded') and d:get('pointer') then
787 boxshape=boxshape_round_pointer
788 elseif d:get('rounded') and not d:get('pointer') then
789 boxshape=boxshape_round
790 elseif not d:get('rounded') and d:get('pointer') then
791 boxshape=boxshape_square_pointer
793 boxshape=boxshape_square
796 local shape = { boxshape(V(x1,y1), V(x2,y2)) }
798 local obj = path(model, shape, {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
801 local final = ipe.Group(elements)
803 local t = { label="add box", pno=model.pno, vno=model.vno,
804 layer=p:active(model.vno), object=obj,
805 selection=model:selection(),
806 undo=_G.revertOriginal,
809 t.redo = function (t, doc)
811 for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
812 p:insert(nil, t.final, 1, t.layer)
818 { label = "Box It", run=boxit},
819 { label = "Tabbed/Boxed Text", run=tabbedboxed},
820 { label = "Deselect All", run=deselectAll},