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 ---
25 example removed, not possible to upload this file to the ipe-wiki otherwise
26 ---- end content of example.isy ---
29 tab_header= color of the tab header in a tabbed box
30 tab_body = color of the tab body in a tabbed box
31 box_fill = fill color a box
32 box_border= color of the box border
33 boxborder = linewidth of the box border
35 The preferred box mode (stroked/filled/strokedfilled) can be changed by
36 changing the hard-wired value (no stylesheet option) PREFERRED_BOX_MODE below
38 With the above style sheet, one can start an empty presentation using
39 ipe -sheet presentation -sheet /path/to/example.isy
43 Shortcuts to these functions can be changed as for other ipelets:
44 shortcuts.ipelet_x_presentation = "Key"
45 where x is the index (starting 1) of the sub-menu for the function
49 version 0. Initial Release. Zhengdao Wang 2010
50 version 1. add a line here if a change is made
54 This file can be distributed and modified under the terms of the GNU General
55 Public License as published by the Free Software Foundation; either version
56 3, or (at your option) any later version.
58 This file is distributed in the hope that it will be useful, but WITHOUT ANY
59 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
60 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
65 ----------------------------------------------------------------------
67 label = "Presentation"
70 Presentation Goodies: Add boxes around objects, deselect all,
71 add tabbed/boxed text.
77 -- table storing the height of the first line box
82 local c=10 -- corner size
89 local PREFERRED_BOX_MODE="strokedfilled" -- or stroked or filled
91 local BOX_DIALOG_SIZE={400,140}
93 -- "spline" is recommended here, as it should work with most
95 local SPLINE_TYPE_NAME="spline"
96 -- local SPLINE_TYPE_NAME="oldspline"
97 -- local SPLINE_TYPE_NAME="bezier"
99 -- initialize the Height table
100 local function init_spacing(model)
104 local sizes = model.doc:sheets():allNames("textsize")
105 for i,size in ipairs(sizes) do
106 obj=ipe.Text({textsize=size, minipage=true}, "ABC\n\nDEF", V(xx,xx), xx)
107 local layer=p:active(model.vno)
108 p:insert(nil, obj, nil, layer)
109 obj=ipe.Text({textsize=size, minipage=true}, "ABC", V(xx,xx), xx)
110 p:insert(nil, obj, nil, layer)
115 Height[iH[i]]=xx-p:bbox(#p):bottomLeft().y
117 Spacing[iH[i]]=(xx-p:bbox(#p):bottomLeft().y)/2-Height[iH[i]]
123 -- generate a path with given properties
124 local function path(model, shape, props)
126 for k,v in pairs(props) do
127 oldvalues[k]=model.attributes[k]
128 model.attributes[k]=v
130 local obj = ipe.Path(model.attributes, shape)
132 for k,v in pairs(props) do
133 model.attributes[k]=oldvalues[k]
135 if props.pen then obj:set('pen', props.pen) end
136 obj:set('pathmode', props.pathmode, props.stroke, props.fill)
140 -- create a square box #shape=3
141 local function boxshape_square(v1, v2)
142 return { type="curve", closed=true;
143 { type="segment"; v1, V(v1.x, v2.y) }, -- L
144 { type="segment"; V(v1.x, v2.y), v2 }, -- T
145 { type="segment"; v2, V(v2.x, v1.y) } } -- R
148 -- create a square box with a pointer #shape=6
149 local function boxshape_square_pointer(v1, v2, v3, v4, v5)
152 local v3=v3 or V(v1.x+.4*dx, v2.y)
153 local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
154 local v5=v5 or V(v1.x+.5*dx,v2.y)
155 return { type="curve", closed=true;
156 { type="segment"; v1, V(v1.x, v2.y) }, -- L
157 { type="segment"; V(v1.x, v2.y), v3}, -- T
158 { type="segment"; v3, v4}, -- P
159 { type="segment"; v4, v5}, --P
160 { type="segment"; v5, v2}, -- T
161 { type="segment"; v2, V(v2.x, v1.y) } } -- R
164 -- create a header box: round corner on topRight
165 local function boxshape_roundTR(v1, v2)
166 return { type="curve", closed=true;
167 { type="segment"; v1, V(v1.x, v2.y) },
168 { type="segment"; V(v1.x, v2.y), V(v2.x-c, v2.y) },
169 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2, v2.y),
170 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) },
171 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y) } }
174 -- create a body box: round corner on bottom Left
175 local function boxshape_roundLL(v1, v2)
176 return { type="curve", closed=true;
177 { type="segment"; v2, V(v2.x, v1.y) },
178 { type="segment"; V(v2.x, v1.y), V(v1.x+c, v1.y) },
179 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
180 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) },
181 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y) } }
184 -- create a body box: 4 round corners #shape=8
185 local function boxshape_round(v1, v2)
186 return { type="curve", closed=true;
187 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
188 { type=SPLINE_TYPE_NAME; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
189 V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
190 { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
191 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
192 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
193 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
194 { type=SPLINE_TYPE_NAME; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
195 V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
196 { type="segment"; V(v1.x+c, v2.y), V(v2.x-c, v2.y) }, -- T
197 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
198 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
202 -- create a body box: 4 round corners, with pointer #shape=11
203 local function boxshape_round_pointer(v1, v2, v3, v4, v5)
206 local v3=v3 or V(v1.x+.4*dx, v2.y)
207 local v4=v4 or V(v1.x+.6*dx,v2.y+.3*dy)
208 local v5=v5 or V(v1.x+.5*dx,v2.y)
209 return { type="curve", closed=true;
210 { type="segment"; V(v2.x, v2.y-c), V(v2.x, v1.y+c) }, -- R
211 { type=SPLINE_TYPE_NAME; V(v2.x,v1.y+c), V(v2.x, v1.y+c/2),
212 V(v2.x-c/2,v1.y), V(v2.x-c,v1.y)}, -- BR
213 { type="segment"; V(v2.x-c, v1.y), V(v1.x+c, v1.y) }, -- B
214 { type=SPLINE_TYPE_NAME; V(v1.x+c, v1.y), V(v1.x+c/2,v1.y),
215 V(v1.x, v1.y+c/2), V(v1.x, v1.y+c) }, -- BL
216 { type="segment"; V(v1.x, v1.y+c), V(v1.x, v2.y-c) }, -- L
217 { type=SPLINE_TYPE_NAME; V(v1.x, v2.y-c), V(v1.x,v2.y-c/2),
218 V(v1.x+c/2, v2.y), V(v1.x+c, v2.y) }, -- TL
219 { type="segment"; V(v1.x+c, v2.y), v3}, -- T
220 { type="segment"; v3, v4}, -- P
221 { type="segment"; v4, v5}, --P
222 { type="segment"; v5, V(v2.x-c, v2.y)}, -- T
223 { type=SPLINE_TYPE_NAME; V(v2.x-c, v2.y), V(v2.x-c/2,v2.y),
224 V(v2.x, v2.y-c/2), V(v2.x, v2.y-c) }, -- TR
228 -- parse the values from a group obj
229 local function parse_group_values(model,prim)
230 local fs = model.doc:sheets():find("layout").framesize
232 local bbox=p:bbox(prim)
233 local pos=V(bbox:bottomLeft().x, bbox:topRight().y)
235 local elements=p[prim]:elements()
237 local hb,bb,ht,bt=elements[1],elements[2],elements[3],elements[4]
238 if hb:type()=="path" and bb:type()=="path" and
239 ht:type()=="text" and bt:type()=="text" then
240 local values={htext=ht:text(),
242 pinned=(p[prim]:get("pinned")=="horizontal"),
243 fwidth=string.format('%.2f',
244 (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
245 hcolor=hb:get("fill"),
246 bcolor=bb:get("fill"),
247 size=ht:get("textsize")}
248 return TABBED_TEXT,values,pos
251 local bb,bt=elements[1],elements[2]
252 if bb:type()=="path" and #bb:shape()==1 and
253 bb:shape()[1].closed==true and
254 (#bb:shape()[1]==3 or #bb:shape()[1]==8 or
255 #bb:shape()[1]==6 or #bb:shape()[1]==11) then
256 if bt:type()=="text" then
257 local values={btext=bt:text(),
258 pinned=(p[prim]:get("pinned")=="horizontal"),
259 size=bt:get("textsize"),
260 fwidth=string.format('%.2f',
261 (bbox:topRight().x-bbox:bottomLeft().x)/fs.x),
262 bcolor=bb:get("fill")}
263 if #bb:shape()[1]==6 then
264 pos=V(pos.x,bb:shape()[1][1][2].y)
265 elseif #bb:shape()[1]==11 then
266 pos=V(pos.x,bb:shape()[1][10][2].y)
268 return BOXED_TEXT,values,pos
277 function mainWindow(model)
278 if model.ui.win == nil then
281 return model.ui:win()
285 -- Edit the values for the frame
286 local function edit_tabbed_values(model,values)
287 local d = ipeui.Dialog(mainWindow(model), "Create tabbed text")
288 local colors = model.doc:sheets():allNames("color")
289 local sizes = model.doc:sheets():allNames("textsize")
290 d:add("hlabel", "label", { label="Enter Header" }, 1, 1, 1, 1)
291 d:add("hcolor", "combo", colors, 1, 4)
292 d:add("htext", "input", { syntax="latex" }, 2, 1, 1, 4)
293 d:add("blabel", "label", { label="Enter Body"}, 3, 1, 1, 3)
294 d:add("bcolor", "combo", colors, 3, 4)
295 d:add("btext", "text", { syntax="latex" }, 4, 1, 1, 4)
296 d:add("size", "combo", sizes, 5, 1)
297 d:add("wlabel", "label", { label="width [0-1]"}, 5, 2, 1, 2)
298 d:add("fwidth", "input", {size=2}, 5, 3, 1, 1)
299 d:add("pinned", "checkbox", { label="pinned"}, 5, 4)
300 d:add("ok", "button", { label="&Ok", action="accept" }, 6, 4)
301 d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 3)
302 _G.addEditorField(d, "btext", 6, 2)
303 d:setStretch("row", 2, 1)
304 d:setStretch("column", 1, 1)
305 d:set("fwidth", "0.8")
308 if indexOf("tab_header", colors) then
309 d:set("hcolor", indexOf("tab_header", colors))
310 elseif indexOf("darkblue", colors) then
311 d:set("hcolor", indexOf("darkblue", colors))
313 d:set("hcolor", indexOf("black", colors))
316 if indexOf("tab_body", colors) then
317 d:set("bcolor", indexOf("tab_body", colors))
318 elseif indexOf("lightgray", colors) then
319 d:set("bcolor", indexOf("lightgray", colors))
321 d:set("bcolor", indexOf("white", colors))
325 for k,v in pairs(values) do
326 if k=="hcolor" or k=="bcolor" then v=indexOf(v, colors) end
327 if k=="size" then v=indexOf(v, sizes) end
332 local r = d:execute(prefs.editor_size)
333 if not r then return end
335 newvalues.htext=d:get("htext")
336 newvalues.btext=d:get("btext")
337 newvalues.pinned=d:get("pinned")
338 newvalues.fwidth=d:get("fwidth")
339 newvalues.size=sizes[d:get("size")]
340 newvalues.hcolor=colors[d:get("hcolor")]
341 newvalues.bcolor=colors[d:get("bcolor")]
342 -- if newvalues.fwidth=="" or tonumber(newvalues.fwidth)>.99 then
343 -- newvalues.pinned=true
348 -- measure the height a piece of given text
349 local function measure_height(model,text,size,width)
351 local obj= ipe.Text(model.attributes, text, V(0,0), width)
352 obj:set('textsize', size)
353 local layer=p:active(model.vno)
354 p:insert(nil, obj, nil, layer)
355 if not model.doc:runLatex() then
359 local bbox=p:bbox(#p)
361 return bbox:topRight().y-bbox:bottomLeft().y
365 local function create_boxed(model,values, pos, prim)
366 local fs = model.doc:sheets():find("layout").framesize
367 local p = model:page()
368 local editmode=(prim~=nil)
370 local width fwidth=tonumber(values.fwidth)
372 if not fwidth or fwidth<0 or fwidth>1 then
380 local s=Spacing[values.size]
381 local h=Height[values.size]
382 if not s or not h then
384 s=Spacing[values.size]
385 h=Height[values.size]
388 local bheight=measure_height(model,values.btext,values.size,width-2*s)
402 if fwidth>.99 then x1,x2=0,fs.x end
406 local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
407 bt:set('textsize', values.size)
411 if values.rounded then
412 shape2 = { boxshape_round(V(x1,y1), V(x2,y2)) }
414 shape2 = { boxshape_square(V(x1,y1), V(x2,y2)) }
416 local bb = path(model, shape2,
417 {pathmode='filled', fill=values.bcolor, stroke="white"})
420 local elements={bb,bt}
421 local obj=ipe.Group(elements)
422 -- obj:setMatrix(p[prim]:matrix()) -- currently not working
423 if values.pinned then obj:set('pinned', 'horizontal') end
426 local t={original=p[prim]:clone(),
427 label="edit boxed text",
432 t.undo = function (t, doc)
433 doc[t.pno]:replace(t.primary, t.original)
435 t.redo = function (t, doc)
436 doc[t.pno]:replace(t.primary, t.final)
440 model:creation("create boxed text", obj)
441 -- model.doc:runLatex() -- may crash the thing
445 -- Create the requested object from values
446 local function create_tabbed(model,values, pos, prim)
447 local fs = model.doc:sheets():find("layout").framesize
448 local p = model:page()
449 local editmode=(prim~=nil)
451 local width fwidth=tonumber(values.fwidth)
453 if not fwidth or fwidth<0 or fwidth>1 then
461 local s=Spacing[values.size]
462 local h=Height[values.size]
463 if not s or not h then
465 s=Spacing[values.size]
466 h=Height[values.size]
469 local bheight=measure_height(model,values.btext,values.size,width-2*s)
476 y1=y2-h-bheight-3.8*s
481 y1=y2-h-bheight-3.8*s
483 if fwidth>.99 then x1,x2=0,fs.x end
487 local ht= ipe.Text(model.attributes, values.htext, pos, width-2*s)
488 ht:set('stroke', 'white')
489 ht:set('textsize', values.size)
492 pos=V(x1+s, y2-s-h-2*s)
493 local bt= ipe.Text(model.attributes, values.btext, pos, width-2*s)
494 bt:set('textsize', values.size)
497 local shape1 = { boxshape_roundTR(V(x1,y2-h-2*s), V(x2,y2)) }
498 local hb = path(model, shape1,
499 {pathmode='filled', fill=values.hcolor, stroke="white"})
500 hb:set('pathmode', 'filled', "white", values.hcolor)
503 local shape2 = { boxshape_roundLL(V(x1,y1), V(x2,y2-h-2*s)) }
504 local bb = path(model, shape2,
505 {pathmode='filled', fill=values.bcolor, stroke="white"})
508 local elements={hb,bb,ht,bt}
509 local obj=ipe.Group(elements)
510 if values.pinned then obj:set('pinned', 'horizontal') end
513 local t={original=p[prim]:clone(),
514 label="edit tabbed text",
519 t.undo = function (t, doc)
520 doc[t.pno]:replace(t.primary, t.original)
522 t.redo = function (t, doc)
523 doc[t.pno]:replace(t.primary, t.final)
527 model:creation("create tabbed text", obj)
528 -- model.doc:runLatex() -- may crash the thing
533 -- create the dialog for editing box properties
534 local function box_property_dialog(model)
535 local colors = model.doc:sheets():allNames("color")
536 local pens= model.doc:sheets():allNames("pen")
537 local pathmodes = {"stroked", "strokedfilled", "filled"}
538 local d = ipeui.Dialog(mainWindow(model), "Edit box properties")
540 d:add("rounded", "checkbox", { label="Round Corner"}, 1, 1, 1, 1)
541 d:add("pointer", "checkbox", { label="Pointer"}, 1, 2, 1, 1)
542 d:add("mlabel", "label", { label="Mode"}, 2, 1)
543 d:add("pathmode", "combo", pathmodes, 2, 2)
544 d:add("flabel", "label", { label="Fill Color" }, 3, 1)
545 d:add("fill", "combo", colors, 3, 2)
546 d:add("slabel", "label", { label="Stroke Color" }, 4, 1)
547 d:add("stroke", "combo", colors, 4, 2)
548 d:add("plabel", "label", { label="Line Width"}, 5, 1)
549 d:add("pen", "combo", pens, 5, 2)
550 d:add("cancel", "button", { label="&Cancel", action="reject" }, 6, 1)
551 d:add("ok", "button", { label="&Ok", action="accept" }, 6, 2)
552 d:setStretch("column", 2, 1)
557 local function edit_box(model, prim)
558 local colors = model.doc:sheets():allNames("color")
559 local pens= model.doc:sheets():allNames("pen")
560 local pathmodes = {"stroked", "strokedfilled", "filled"}
563 local elements=p[prim]:elements()
565 local bbs=bb:shape()[1]
567 local d=box_property_dialog(model)
570 d:set("rounded", #bbs>=7)
571 d:set("pathmode", indexOf(bb:get('pathmode'),pathmodes))
572 d:set("pointer", #bbs==6 or #bbs==11)
573 if indexOf(bb:get('stroke'),colors) then
574 d:set("stroke", indexOf(bb:get('stroke'),colors) )
575 elseif not model.attributes.stroke then
576 d:set("stroke", indexOf(model.attributes.stroke,colors) )
578 if indexOf(bb:get('fill'),colors) then
579 d:set("fill", indexOf(bb:get('fill'),colors) )
580 elseif not model.attributes.fill then
581 d:set("fill", indexOf(model.attributes.fill,colors) )
583 if indexOf(bb:get('pen'),pens) then
584 d:set("pen", indexOf(bb:get('pen'),pens) )
585 elseif not model.attributes.pen then
586 d:set("pen", indexOf(model.attributes.pen,pens) )
589 local r = d:execute(BOX_DIALOG_SIZE)
590 if not r then return end
591 local pathmode=pathmodes[d:get("pathmode")]
592 local stroke=colors[d:get("stroke")]
593 local fill=colors[d:get("fill")]
594 local pen=pens[d:get("pen")]
597 if d:get('rounded') and d:get('pointer') then
598 boxshape=boxshape_round_pointer
599 elseif d:get('rounded') and not d:get('pointer') then
600 boxshape=boxshape_round
601 elseif not d:get('rounded') and d:get('pointer') then
602 boxshape=boxshape_square_pointer
604 boxshape=boxshape_square
607 -- v1=BL, v2=TR, v3=P1, v4=P2, v5=P3. Pointer=(P1,P2,P3)
608 local v1,v2,v3,v4,v5,shape
610 v1=bbs[1][1];v2=bbs[2][2]
611 shape={ boxshape(v1,v2) }
612 elseif #bb:shape()[1]==6 then
613 v1=bbs[1][1];v3=bbs[2][2]; v4=bbs[3][2]; v5=bbs[4][2];
615 shape={ boxshape(v1,v2,v3,v4,v5) }
616 elseif #bb:shape()[1]==8 then
617 v1=V(bbs[5][1].x,bbs[3][1].y)
618 v2=V(bbs[1][1].x,bbs[7][1].y)
619 shape={ boxshape(v1,v2) }
620 elseif #bb:shape()[1]==11 then
621 v1=V(bbs[5][1].x,bbs[3][1].y)
622 v2=V(bbs[1][1].x,bbs[7][1].y)
623 v3=bbs[7][2]; v4=bbs[8][2]; v5=bbs[9][2];
624 shape={ boxshape(v1,v2,v3,v4,v5) }
627 local obj = path(model, shape,
628 {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
631 local final = ipe.Group(elements)
632 final:setMatrix(p[prim]:matrix())
634 local t = { label="edit box", pno=model.pno, vno=model.vno,
635 layer=p:active(model.vno),
636 original=p[prim]:clone(),
637 primary=prim, final=final }
638 t.undo = function (t, doc)
639 doc[t.pno]:replace(t.primary, t.original)
641 t.redo = function (t, doc)
642 doc[t.pno]:replace(t.primary, t.final)
647 -- Edit a group object
648 local function action_edit_group(model,prim,obj)
649 local otype,values,pos=parse_group_values(model,prim)
650 if otype==UNKNOWN then
651 model:warning("Cannot edit this object")
653 elseif otype==TABBED_TEXT then
654 local newvalues=edit_tabbed_values(model, values)
655 if not newvalues then return end
656 if newvalues.htext=="" then
657 newvalues.rounded=true
658 create_boxed(model,newvalues,pos,prim)
660 create_tabbed(model,newvalues,pos,prim)
662 elseif otype==BOXED_OTHER or otype==BOXED_TEXT then
667 -- saving the old function
668 function _G.MODEL:presentation_backup_actinon_edit () end
669 _G.MODEL.presentation_backup_action_edit = _G.MODEL.action_edit
671 -- modify the global edit action
672 function _G.MODEL:action_edit()
673 local p = self:page()
674 local prim = p:primarySelection()
676 self:presentation_backup_action_edit()
680 if obj:type() == "group" then
681 action_edit_group(self, prim, obj)
683 self:presentation_backup_action_edit()
687 -- Run to create a new object
688 function tabbedboxed(model)
689 local values=edit_tabbed_values(model)
690 if not values then return end
691 if values.htext=="" then
693 create_boxed(model,values)
695 create_tabbed(model,values)
699 -- deselect all selected
700 function deselectAll(model)
701 local doc = model.doc
702 for i,p in doc:pages() do
707 -- box the selected objects
708 function boxit(model)
710 local box = ipe.Rect()
712 for i,obj,sel,layer in p:objects() do
715 elements[#elements+1]=obj:clone()
719 model.ui:explain('No selection to box')
723 local layout = model.doc:sheets():find("layout")
724 -- local maxx=layout.framesize.x
726 local x1=box:bottomLeft().x-s
727 -- if x1 < 0 then x1 = 0 end
728 local y1=box:bottomLeft().y-s
730 local x2=box:topRight().x+s
731 -- if x2 > maxx then x2 = maxx end
732 local y2=box:topRight().y+s
734 local d=box_property_dialog(model)
736 local colors = model.doc:sheets():allNames("color")
737 local pens= model.doc:sheets():allNames("pen")
738 local pathmodes = {"stroked", "strokedfilled", "filled"}
741 d:set("rounded", true)
743 d:set("pathmode", indexOf(PREFERRED_BOX_MODE,pathmodes))
745 if indexOf("box_border",colors) then
746 d:set("stroke", indexOf("box_border",colors))
747 elseif model.attributes.stroke then
748 d:set("stroke", indexOf(model.attributes.stroke,colors) )
751 if indexOf("box_fill",colors) then
752 d:set("fill", indexOf("box_fill",colors))
753 elseif model.attributes.fill then
754 d:set("fill", indexOf(model.attributes.fill,colors) )
757 if indexOf("boxborder",pens) then
758 d:set("pen", indexOf("boxborder",pens))
759 elseif model.attributes.pen then
760 d:set("pen", indexOf(model.attributes.pen,pens) )
763 local r = d:execute(BOX_DIALOG_SIZE)
765 if not r then return end
766 local pathmode=pathmodes[d:get("pathmode")]
767 local stroke=colors[d:get("stroke")]
768 local fill=colors[d:get("fill")]
769 local pen=pens[d:get("pen")]
772 if d:get('rounded') and d:get('pointer') then
773 boxshape=boxshape_round_pointer
774 elseif d:get('rounded') and not d:get('pointer') then
775 boxshape=boxshape_round
776 elseif not d:get('rounded') and d:get('pointer') then
777 boxshape=boxshape_square_pointer
779 boxshape=boxshape_square
782 local shape = { boxshape(V(x1,y1), V(x2,y2)) }
784 local obj = path(model, shape, {pen=pen, pathmode=pathmode, stroke=stroke, fill=fill})
787 local final = ipe.Group(elements)
789 local t = { label="add box", pno=model.pno, vno=model.vno,
790 layer=p:active(model.vno), object=obj,
791 selection=model:selection(),
792 undo=_G.revertOriginal,
795 t.redo = function (t, doc)
797 for i = #t.selection,1,-1 do p:remove(t.selection[i]) end
798 p:insert(nil, t.final, 1, t.layer)
804 { label = "Box It", run=boxit},
805 { label = "Tabbed/Boxed Text", run=tabbedboxed},
806 { label = "Deselect All", run=deselectAll},