1 // --------------------------------------------------------------------
2 // Lua bindings for user interface
3 // --------------------------------------------------------------------
4 /*
5 
6     This file is part of the extensible drawing editor Ipe.
7     Copyright (c) 1993-2020 Otfried Cheong
8 
9     Ipe is free software; you can redistribute it and/or modify it
10     under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 3 of the License, or
12     (at your option) any later version.
13 
14     As a special exception, you have permission to link Ipe with the
15     CGAL library and distribute executables, as long as you follow the
16     requirements of the Gnu General Public License in regard to all of
17     the software in the executable aside from CGAL.
18 
19     Ipe is distributed in the hope that it will be useful, but WITHOUT
20     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21     or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
22     License for more details.
23 
24     You should have received a copy of the GNU General Public License
25     along with Ipe; if not, you can find it at
26     "http://www.gnu.org/copyleft/gpl.html", or write to the Free
27     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 
29 */
30 
31 #include "appui.h"
32 #include "ipecanvas.h"
33 #include "tools.h"
34 
35 #include "ipelua.h"
36 
37 #include "ipethumbs.h"
38 
39 #include <cstdio>
40 #include <cstdlib>
41 
42 using namespace ipe;
43 using namespace ipelua;
44 
45 // --------------------------------------------------------------------
46 
check_canvas(lua_State * L,int i)47 static CanvasBase *check_canvas(lua_State *L, int i)
48 {
49   AppUiBase **ui = (AppUiBase **) luaL_checkudata(L, i, "Ipe.appui");
50   return (*ui)->canvas();
51 }
52 
check_appui(lua_State * L,int i)53 inline AppUiBase **check_appui(lua_State *L, int i)
54 {
55   return (AppUiBase **) luaL_checkudata(L, i, "Ipe.appui");
56 }
57 
appui_tostring(lua_State * L)58 static int appui_tostring(lua_State *L)
59 {
60   check_appui(L, 1);
61   lua_pushfstring(L, "AppUi@%p", lua_topointer(L, 1));
62   return 1;
63 }
64 
65 /* When the Lua model is collected, its "ui" userdata will be garbage
66    collected as well.  At this point, the C++ object has long been
67    deleted. */
appui_destructor(lua_State * L)68 static int appui_destructor(lua_State *L)
69 {
70   check_appui(L, 1);
71   ipeDebug("AppUi Lua destructor");
72   return 0;
73 }
74 
75 // --------------------------------------------------------------------
76 
appui_setPage(lua_State * L)77 static int appui_setPage(lua_State *L)
78 {
79   AppUiBase **ui = check_appui(L, 1);
80   Page *page = check_page(L, 2)->page;
81   int pno = luaL_checkinteger(L, 3) - 1;
82   int view = check_viewno(L, 4, page);
83   Cascade *sheets = check_cascade(L, 5)->cascade;
84   (*ui)->canvas()->setPage(page, pno, view, sheets);
85   return 0;
86 }
87 
appui_setResources(lua_State * L)88 static int appui_setResources(lua_State *L)
89 {
90   CanvasBase *canvas = check_canvas(L, 1);
91   Document **d = check_document(L, 2);
92   const PdfResources *res = (*d)->resources();
93   canvas->setResources(res);
94   return 0;
95 }
96 
appui_pan(lua_State * L)97 static int appui_pan(lua_State *L)
98 {
99   CanvasBase *canvas = check_canvas(L, 1);
100   Vector p = canvas->pan();
101   push_vector(L, p);
102   return 1;
103 }
104 
appui_setPan(lua_State * L)105 static int appui_setPan(lua_State *L)
106 {
107   CanvasBase *canvas = check_canvas(L, 1);
108   Vector *v = check_vector(L, 2);
109   canvas->setPan(*v);
110   return 0;
111 }
112 
appui_zoom(lua_State * L)113 static int appui_zoom(lua_State *L)
114 {
115   CanvasBase *canvas = check_canvas(L, 1);
116   lua_pushnumber(L, canvas->zoom());
117   return 1;
118 }
119 
appui_setZoom(lua_State * L)120 static int appui_setZoom(lua_State *L)
121 {
122   AppUiBase **ui = check_appui(L, 1);
123   (*ui)->setZoom(luaL_checknumber(L, 2));
124   return 0;
125 }
126 
appui_setSnapIndicator(lua_State * L)127 static int appui_setSnapIndicator(lua_State *L)
128 {
129   AppUiBase **ui = check_appui(L, 1);
130   (*ui)->setSnapIndicator(luaL_checklstring(L, 2, nullptr));
131   return 0;
132 }
133 
appui_pos(lua_State * L)134 static int appui_pos(lua_State *L)
135 {
136   CanvasBase *canvas = check_canvas(L, 1);
137   push_vector(L, canvas->pos());
138   return 1;
139 }
140 
appui_globalPos(lua_State * L)141 static int appui_globalPos(lua_State *L)
142 {
143   CanvasBase *canvas = check_canvas(L, 1);
144   push_vector(L, canvas->globalPos());
145   return 1;
146 }
147 
appui_unsnappedPos(lua_State * L)148 static int appui_unsnappedPos(lua_State *L)
149 {
150   CanvasBase *canvas = check_canvas(L, 1);
151   push_vector(L, canvas->unsnappedPos());
152   return 1;
153 }
154 
appui_simpleSnapPos(lua_State * L)155 static int appui_simpleSnapPos(lua_State *L)
156 {
157   CanvasBase *canvas = check_canvas(L, 1);
158   push_vector(L, canvas->simpleSnapPos());
159   return 1;
160 }
161 
appui_setFifiVisible(lua_State * L)162 static int appui_setFifiVisible(lua_State *L)
163 {
164   CanvasBase *canvas = check_canvas(L, 1);
165   canvas->setFifiVisible(lua_toboolean(L, 2));
166   return 0;
167 }
168 
appui_setInkMode(lua_State * L)169 static int appui_setInkMode(lua_State *L)
170 {
171   AppUiBase **ui = check_appui(L, 1);
172   bool ink = lua_toboolean(L, 2);
173   (*ui)->setInkMode(ink);
174   (*ui)->canvas()->setInkMode(ink);
175   return 0;
176 }
177 
appui_setSelectionVisible(lua_State * L)178 static int appui_setSelectionVisible(lua_State *L)
179 {
180   CanvasBase *canvas = check_canvas(L, 1);
181   canvas->setSelectionVisible(lua_toboolean(L, 2));
182   return 0;
183 }
184 
appui_setSnap(lua_State * L)185 static int appui_setSnap(lua_State *L)
186 {
187   CanvasBase *canvas = check_canvas(L, 1);
188   Snap snap = canvas->snap();
189   get_snap(L, 2, snap);
190   canvas->setSnap(snap);
191   return 0;
192 }
193 
appui_setAutoOrigin(lua_State * L)194 static int appui_setAutoOrigin(lua_State *L)
195 {
196   CanvasBase *canvas = check_canvas(L, 1);
197   Vector *v = check_vector(L, 2);
198   canvas->setAutoOrigin(*v);
199   return 0;
200 }
201 
appui_update(lua_State * L)202 static int appui_update(lua_State *L)
203 {
204   CanvasBase *canvas = check_canvas(L, 1);
205   if (lua_isnone(L, 2) || lua_isboolean(L, 2)) {
206     if (lua_isnone(L, 2) || lua_toboolean(L, 2))
207       canvas->update();
208     else
209       canvas->updateTool();
210   } else {
211     ipe::Rect *r = check_rect(L, 2);
212     Vector bl = canvas->userToDev(r->topLeft());
213     Vector tr = canvas->userToDev(r->bottomRight());
214     canvas->invalidate(int(bl.x-1), int(bl.y-1),
215 		       int(tr.x - bl.x + 2),
216 		       int(tr.y - bl.y + 2));
217   }
218   return 0;
219 }
220 
appui_finishTool(lua_State * L)221 static int appui_finishTool(lua_State *L)
222 {
223   AppUiBase **ui = check_appui(L, 1);
224   (*ui)->canvas()->finishTool();
225   (*ui)->explain("", 0);
226   return 0;
227 }
228 
appui_canvasSize(lua_State * L)229 static int appui_canvasSize(lua_State *L)
230 {
231   AppUiBase **ui = check_appui(L, 1);
232   CanvasBase *canvas = (*ui)->canvas();
233   push_vector(L, Vector(canvas->canvasWidth(), canvas->canvasHeight()));
234   lua_pushinteger(L, (*ui)->dpi());
235   return 2;
236 }
237 
check_rgb(lua_State * L,int i,int & r,int & g,int & b)238 static void check_rgb(lua_State *L, int i, int &r, int &g, int &b)
239 {
240   r = int(1000 * luaL_checknumber(L, i) + 0.5);
241   g = int(1000 * luaL_checknumber(L, i+1) + 0.5);
242   b = int(1000 * luaL_checknumber(L, i+2) + 0.5);
243   luaL_argcheck(L, (0 <= r && r <= 1000 &&
244 		    0 <= g && g <= 1000 &&
245 		    0 <= b && b <= 1000), 2,
246 		"color components must be between 0.0 and 1.0");
247 }
248 
appui_setCursor(lua_State * L)249 static int appui_setCursor(lua_State *L)
250 {
251   CanvasBase *canvas = check_canvas(L, 1);
252   if (lua_isnumber(L, 2)) {
253     double s = lua_tonumberx(L, 2, nullptr);
254     int r, g, b;
255     check_rgb(L, 3, r, g, b);
256     Color color(r, g, b);
257     canvas->setCursor(CanvasBase::EDotCursor, s, &color);
258   } else if (lua_isstring(L, 2)) {
259     static const char * const cursor_names[] =
260       { "standard", "hand", "cross", nullptr };
261     CanvasBase::TCursor t =
262       CanvasBase::TCursor(luaL_checkoption(L, 2, nullptr, cursor_names));
263     canvas->setCursor(t);
264   } else
265     canvas->setCursor(CanvasBase::EStandardCursor);
266   return 0;
267 }
268 
appui_setNumbering(lua_State * L)269 static int appui_setNumbering(lua_State *L)
270 {
271   CanvasBase *canvas = check_canvas(L, 1);
272   bool t = lua_toboolean(L, 2);
273   CanvasBase::Style s = canvas->canvasStyle();
274   s.numberPages = t;
275   canvas->setCanvasStyle(s);
276   return 0;
277 }
278 
appui_setPretty(lua_State * L)279 static int appui_setPretty(lua_State *L)
280 {
281   CanvasBase *canvas = check_canvas(L, 1);
282   bool t = lua_toboolean(L, 2);
283   CanvasBase::Style s = canvas->canvasStyle();
284   s.pretty = t;
285   canvas->setCanvasStyle(s);
286   return 0;
287 }
288 
appui_setScreen(lua_State * L)289 static int appui_setScreen(lua_State *L)
290 {
291   AppUiBase **ui = check_appui(L, 1);
292   static const char * const screen_names[] = { "normal", "maximized", "full", nullptr };
293   int t = luaL_checkoption(L, 2, nullptr, screen_names);
294   (*ui)->setFullScreen(t);
295   return 0;
296 }
297 
appui_type3Font(lua_State * L)298 static int appui_type3Font(lua_State *L)
299 {
300   CanvasBase *canvas = check_canvas(L, 1);
301   lua_pushboolean(L, canvas->type3Font());
302   return 1;
303 }
304 
305 // --------------------------------------------------------------------
306 
appui_pantool(lua_State * L)307 static int appui_pantool(lua_State *L)
308 {
309   ipeDebug("pantool");
310   CanvasBase *canvas = check_canvas(L, 1);
311   Page *page = check_page(L, 2)->page;
312   int view = check_viewno(L, 3, page);
313   PanTool *tool = new PanTool(canvas, page, view);
314   canvas->setTool(tool);
315   return 0;
316 }
317 
appui_selecttool(lua_State * L)318 static int appui_selecttool(lua_State *L)
319 {
320   CanvasBase *canvas = check_canvas(L, 1);
321   Page *page = check_page(L, 2)->page;
322   int view = check_viewno(L, 3, page);
323   double selectDistance = luaL_checknumber(L, 4);
324   bool nonDestructive = lua_toboolean(L, 5);
325   SelectTool *tool = new SelectTool(canvas, page, view, selectDistance,
326 				    nonDestructive);
327   canvas->setTool(tool);
328   return 0;
329 }
330 
appui_transformtool(lua_State * L)331 static int appui_transformtool(lua_State *L)
332 {
333   static const char * const option_names[] = {
334     "translate", "scale", "stretch", "rotate", "shear", nullptr };
335   CanvasBase *canvas = check_canvas(L, 1);
336   Page *page = check_page(L, 2)->page;
337   int view = check_viewno(L, 3, page);
338   int type = luaL_checkoption(L, 4, nullptr, option_names);
339   bool withShift = lua_toboolean(L, 5);
340   lua_pushvalue(L, 6);
341   int method = luaL_ref(L, LUA_REGISTRYINDEX);
342   IpeTransformTool *tool =
343     new IpeTransformTool(canvas, page, view, TransformTool::TType(type),
344 			 withShift, L, method);
345   if (tool->isValid()) {
346     canvas->setTool(tool);
347     lua_pushboolean(L, true);
348     return 1;
349   } else {
350     delete tool;
351     return 0;
352   }
353 }
354 
luatool_setcolor(lua_State * L)355 static int luatool_setcolor(lua_State *L)
356 {
357   LuaTool *tool = (LuaTool *) lua_touserdata(L, lua_upvalueindex(1));
358   int r, g, b;
359   check_rgb(L, 1, r, g, b);
360   tool->setColor(Color(r, g, b));
361   return 0;
362 }
363 
shapetool_setshape(lua_State * L)364 static int shapetool_setshape(lua_State *L)
365 {
366   ShapeTool *tool = (ShapeTool *) lua_touserdata(L, lua_upvalueindex(1));
367   Shape shape = check_shape(L, 1);
368   int which = 0;
369   if (!lua_isnoneornil(L, 2))
370     which = (int)luaL_checkinteger(L, 2);
371   double pen = 1.0;
372   if (lua_isnumber(L, 3))
373     pen = luaL_checknumber(L, 3);
374   tool->setShape(shape, which, pen);
375   return 0;
376 }
377 
shapetool_setsnapping(lua_State * L)378 static int shapetool_setsnapping(lua_State *L)
379 {
380   ShapeTool *tool = (ShapeTool *) lua_touserdata(L, lua_upvalueindex(1));
381   bool snap = lua_toboolean(L, 1);
382   bool skipLast = lua_toboolean(L, 2);
383   tool->setSnapping(snap, skipLast);
384   return 0;
385 }
386 
shapetool_setmarks(lua_State * L)387 static int shapetool_setmarks(lua_State *L)
388 {
389   ShapeTool *tool = (ShapeTool *) lua_touserdata(L, lua_upvalueindex(1));
390   luaL_argcheck(L, lua_istable(L, 1), 1, "argument is not a table");
391   int no = lua_rawlen(L, 1);
392   tool->clearMarks();
393   for (int i = 1; i + 1 <= no; i += 2) {
394     lua_rawgeti(L, 1, i);
395     luaL_argcheck(L, is_type(L, -1, "Ipe.vector"), 1,
396 		  "element is not a vector");
397     Vector *v = check_vector(L, -1);
398     lua_rawgeti(L, 1, i+1);
399     luaL_argcheck(L, lua_isnumber(L, -1), 1, "element is not a number");
400     int t = lua_tointegerx(L, -1, nullptr);
401     luaL_argcheck(L, ShapeTool::EVertex <= t && t < ShapeTool::ENumMarkTypes,
402 		  1, "number is not a mark type");
403     lua_pop(L, 2); // v, t
404     tool->addMark(*v, ShapeTool::TMarkType(t));
405   }
406   return 0;
407 }
408 
pastetool_setmatrix(lua_State * L)409 static int pastetool_setmatrix(lua_State *L)
410 {
411   PasteTool *tool = (PasteTool *) lua_touserdata(L, lua_upvalueindex(1));
412   Matrix *m = check_matrix(L, 1);
413   tool->setMatrix(*m);
414   return 0;
415 }
416 
appui_shapetool(lua_State * L)417 static int appui_shapetool(lua_State *L)
418 {
419   CanvasBase *canvas = check_canvas(L, 1);
420   lua_pushvalue(L, 2);
421   int luatool = luaL_ref(L, LUA_REGISTRYINDEX);
422   ShapeTool *tool = new ShapeTool(canvas, L, luatool);
423   // add methods to Lua tool
424   lua_rawgeti(L, LUA_REGISTRYINDEX, luatool);
425   lua_pushlightuserdata(L, tool);
426   lua_pushcclosure(L, luatool_setcolor, 1);
427   lua_setfield(L, -2, "setColor");
428   lua_pushlightuserdata(L, tool);
429   lua_pushcclosure(L, shapetool_setshape, 1);
430   lua_setfield(L, -2, "setShape");
431   lua_pushlightuserdata(L, tool);
432   lua_pushcclosure(L, shapetool_setmarks, 1);
433   lua_setfield(L, -2, "setMarks");
434   lua_pushlightuserdata(L, tool);
435   lua_pushcclosure(L, shapetool_setsnapping, 1);
436   lua_setfield(L, -2, "setSnapping");
437   canvas->setTool(tool);
438   return 0;
439 }
440 
appui_pastetool(lua_State * L)441 static int appui_pastetool(lua_State *L)
442 {
443   CanvasBase *canvas = check_canvas(L, 1);
444   Object *obj = check_object(L, 2)->obj;
445   lua_pushvalue(L, 3);
446   int luatool = luaL_ref(L, LUA_REGISTRYINDEX);
447   PasteTool *tool = new PasteTool(canvas, L, luatool, obj->clone());
448   // add methods to Lua tool
449   lua_rawgeti(L, LUA_REGISTRYINDEX, luatool);
450   lua_pushlightuserdata(L, tool);
451   lua_pushcclosure(L, luatool_setcolor, 1);
452   lua_setfield(L, -2, "setColor");
453   lua_pushlightuserdata(L, tool);
454   lua_pushcclosure(L, pastetool_setmatrix, 1);
455   lua_setfield(L, -2, "setMatrix");
456   canvas->setTool(tool);
457   return 0;
458 }
459 
460 // --------------------------------------------------------------------
461 
appui_win(lua_State * L)462 static int appui_win(lua_State *L)
463 {
464   AppUiBase **ui = check_appui(L, 1);
465   push_winid(L, (*ui)->windowId());
466   return 1;
467 }
468 
appui_close(lua_State * L)469 static int appui_close(lua_State *L)
470 {
471   AppUiBase **ui = check_appui(L, 1);
472   (*ui)->closeWindow();
473   return 0;
474 }
475 
appui_clipboard(lua_State * L)476 static int appui_clipboard(lua_State *L)
477 {
478   AppUiBase **ui = check_appui(L, 1);
479   return (*ui)->clipboard(L);
480 }
481 
appui_setClipboard(lua_State * L)482 static int appui_setClipboard(lua_State *L)
483 {
484   AppUiBase **ui = check_appui(L, 1);
485   return (*ui)->setClipboard(L);
486 }
487 
488 // This is only used on Windows to compute the shortcuts...
appui_actionInfo(lua_State * L)489 static int appui_actionInfo(lua_State *L)
490 {
491   AppUiBase **ui = check_appui(L, 1);
492   return (*ui)->actionInfo(L);
493 }
494 
appui_actionState(lua_State * L)495 static int appui_actionState(lua_State *L)
496 {
497   AppUiBase **ui = check_appui(L, 1);
498   const char *name = luaL_checklstring(L, 2, nullptr);
499   lua_pushboolean(L, (*ui)->actionState(name));
500   return 1;
501 }
502 
appui_setActionState(lua_State * L)503 static int appui_setActionState(lua_State *L)
504 {
505   AppUiBase **ui = check_appui(L, 1);
506   const char *name = luaL_checklstring(L, 2, nullptr);
507   bool val = lua_toboolean(L, 3);
508   (*ui)->setActionState(name, val);
509   return 0;
510 }
511 
appui_setupSymbolicNames(lua_State * L)512 static int appui_setupSymbolicNames(lua_State *L)
513 {
514   AppUiBase **ui = check_appui(L, 1);
515   Cascade *sheets = check_cascade(L, 2)->cascade;
516   (*ui)->setupSymbolicNames(sheets);
517   return 0;
518 }
519 
appui_setAttributes(lua_State * L)520 static int appui_setAttributes(lua_State *L)
521 {
522   AppUiBase **ui = check_appui(L, 1);
523   Cascade *sheets = check_cascade(L, 2)->cascade;
524   AllAttributes all;
525   check_allattributes(L, 3, all);
526   (*ui)->setAttributes(all, sheets);
527   return 0;
528 }
529 
appui_setGridAngleSize(lua_State * L)530 static int appui_setGridAngleSize(lua_State *L)
531 {
532   AppUiBase **ui = check_appui(L, 1);
533   Attribute gs = check_number_attribute(L, 2);
534   Attribute as = check_number_attribute(L, 3);
535   (*ui)->setGridAngleSize(gs, as);
536   return 0;
537 }
538 
appui_setLayers(lua_State * L)539 static int appui_setLayers(lua_State *L)
540 {
541   AppUiBase **ui = check_appui(L, 1);
542   Page *page = check_page(L, 2)->page;
543   int view = check_viewno(L, 3, page);
544   (*ui)->setLayers(page, view);
545   return 0;
546 }
547 
appui_setNumbers(lua_State * L)548 static int appui_setNumbers(lua_State *L)
549 {
550   AppUiBase **ui = check_appui(L, 1);
551   String vno;
552   String pno;
553   if (!lua_isnil(L, 2))
554     vno = luaL_checklstring(L, 2, nullptr);
555   bool vm = lua_toboolean(L, 3);
556   if (!lua_isnil(L, 4))
557     pno = luaL_checklstring(L, 4, nullptr);
558   bool pm = lua_toboolean(L, 5);
559   (*ui)->setNumbers(vno, vm, pno, pm);
560   return 0;
561 }
562 
appui_setBookmarks(lua_State * L)563 static int appui_setBookmarks(lua_State *L)
564 {
565   AppUiBase **ui = check_appui(L, 1);
566   luaL_argcheck(L, lua_istable(L, 2), 2, "argument is not a table");
567   int no = lua_rawlen(L, 2);
568   std::vector<String> bm;
569   for (int i = 1; i <= no; ++i) {
570     lua_rawgeti(L, 2, i);
571     luaL_argcheck(L, lua_isstring(L, -1), 2, "item is not a string");
572     bm.push_back(String(lua_tolstring(L, -1, nullptr)));
573     lua_pop(L, 1);
574   }
575   (*ui)->setBookmarks(no, &bm[0]);
576   return 0;
577 }
578 
appui_setWindowTitle(lua_State * L)579 static int appui_setWindowTitle(lua_State *L)
580 {
581   AppUiBase **ui = check_appui(L, 1);
582   bool mod = lua_toboolean(L, 2);
583   const char *s = luaL_checklstring(L, 3, nullptr);
584   (*ui)->setWindowCaption(mod, s);
585   return 0;
586 }
587 
appui_setNotes(lua_State * L)588 static int appui_setNotes(lua_State *L)
589 {
590   AppUiBase **ui = check_appui(L, 1);
591   const char *s = luaL_checklstring(L, 2, nullptr);
592   (*ui)->setNotes(s);
593   return 0;
594 }
595 
appui_setRecentFiles(lua_State * L)596 static int appui_setRecentFiles(lua_State *L)
597 {
598   AppUiBase **ui = check_appui(L, 1);
599   luaL_argcheck(L, lua_istable(L, 2), 2, "argument is not a table");
600   int no = lua_rawlen(L, 2);
601   std::vector<String> s;
602   for (int i = 1; i <= no; ++i) {
603     lua_rawgeti(L, 2, i);
604     luaL_argcheck(L, lua_isstring(L, -1), 2, "item is not a string");
605     s.push_back(String(lua_tolstring(L, -1, nullptr)));
606     lua_pop(L, 1);
607   }
608   (*ui)->setRecentFileMenu(s);
609   return 0;
610 }
611 
appui_showTool(lua_State * L)612 static int appui_showTool(lua_State *L)
613 {
614   static const char * const option_names[] = {
615     "properties", "bookmarks", "notes", "layers", nullptr };
616 
617   AppUiBase **ui = check_appui(L, 1);
618   int m = luaL_checkoption(L, 2, nullptr, option_names);
619   bool s = lua_toboolean(L, 3);
620   (*ui)->setToolVisible(m, s);
621   return 0;
622 }
623 
appui_explain(lua_State * L)624 static int appui_explain(lua_State *L)
625 {
626   AppUiBase **ui = check_appui(L, 1);
627   const char *s = luaL_checklstring(L, 2, nullptr);
628   int t = 4000;
629   if (lua_isnumber(L, 3))
630     t = lua_tointegerx(L, 3, nullptr);
631   (*ui)->explain(s, t);
632   return 0;
633 }
634 
635 // --------------------------------------------------------------------
636 
get_page_sorter_size(lua_State * L,int & width,int & height,int & thumbWidth)637 void get_page_sorter_size(lua_State *L, int &width, int &height,
638 			  int &thumbWidth)
639 {
640   thumbWidth = 300;
641   width = 600;
642   height = 480;
643 
644   lua_getglobal(L, "prefs");
645   lua_getfield(L, -1, "page_sorter_size");
646   if (lua_istable(L, -1)) {
647     lua_rawgeti(L, -1, 1);
648     if (lua_isnumber(L, -1))
649       width = lua_tointegerx(L, -1, nullptr);
650     lua_rawgeti(L, -2, 2);
651     if (lua_isnumber(L, -1))
652       height = lua_tointegerx(L, -1, nullptr);
653     lua_pop(L, 2);
654   }
655   lua_pop(L, 1); // page_sorter_size
656 
657   lua_getfield(L, -1, "thumbnail_width");
658   if (lua_isnumber(L, -1))
659     thumbWidth = lua_tointegerx(L, -1, nullptr);
660   lua_pop(L, 1); // thumbnail_width, prefs
661 }
662 
appui_selectPage(lua_State * L)663 static int appui_selectPage(lua_State *L)
664 {
665   check_appui(L, 1);  // not actually used now
666   Document **doc = check_document(L, 2);
667   int page = -1;
668   if (lua_isnumber(L, 3)) {
669     page = lua_tointegerx(L, 3, nullptr);
670     luaL_argcheck(L, 1 <= page && page <= (*doc)->countPages(),
671 		  3, "invalid page number");
672   }
673 
674   int startIndex = 1;
675   if (lua_isnumber(L, 4)) {
676     startIndex = lua_tointegerx(L, 4, nullptr);
677     int maxIndex = (page < 0) ? (*doc)->countPages() :
678       (*doc)->page(page-1)->countViews();
679     luaL_argcheck(L, 1 <= startIndex && startIndex <= maxIndex,
680 		  4, "invalid start index");
681   }
682 
683   int width, height, thumbWidth;
684   get_page_sorter_size(L, width, height, thumbWidth);
685 
686   int sel = CanvasBase::selectPageOrView(*doc, page - 1, startIndex - 1,
687 					 thumbWidth, width, height);
688   if (sel >= 0) {
689     lua_pushinteger(L, sel + 1);
690     return 1;
691   } else
692     return 0;
693 }
694 
appui_pageSorter(lua_State * L)695 static int appui_pageSorter(lua_State *L)
696 {
697   AppUiBase **ui = check_appui(L, 1);
698   Document **doc = check_document(L, 2);
699   int page = -1;
700   if (lua_isnumber(L, 3)) {
701     page = lua_tointegerx(L, 3, nullptr);
702     luaL_argcheck(L, 1 <= page && page <= (*doc)->countPages(),
703 		  3, "invalid page number");
704   }
705 
706   int width, height, thumbWidth;
707   get_page_sorter_size(L, width, height, thumbWidth);
708 
709   return (*ui)->pageSorter(L, *doc, page - 1, width, height, thumbWidth);
710 }
711 
712 static const char *const render_formats[] = {"svg", "png", "eps", "pdf", nullptr };
713 
appui_renderPage(lua_State * L)714 static int appui_renderPage(lua_State *L)
715 {
716   // AppUiBase **ui = check_appui(L, 1);  // not used
717   Document **doc = check_document(L, 2);
718   int pageno = luaL_checkinteger(L, 3);
719   int viewno = luaL_checkinteger(L, 4);
720   Thumbnail::TargetFormat fm =
721     Thumbnail::TargetFormat(luaL_checkoption(L, 5, nullptr, render_formats));
722   const char *dst = luaL_checklstring(L, 6, nullptr);
723   double zoom = luaL_checknumber(L, 7);
724   Thumbnail tn(*doc, 0);
725   tn.setTransparent(lua_toboolean(L, 8));
726   tn.setNoCrop(lua_toboolean(L, 9));
727   const Page *page = (*doc)->page(pageno - 1);
728   tn.saveRender(fm, dst, page, viewno - 1, zoom);
729   return 0;
730 }
731 
732 // --------------------------------------------------------------------
733 
734 static const struct luaL_Reg appui_methods[] = {
735   { "__tostring", appui_tostring },
736   { "__gc", appui_destructor },
737   // --------------------------------------------------------------------
738   { "setPage", appui_setPage},
739   { "pan", appui_pan},
740   { "setPan", appui_setPan},
741   { "zoom", appui_zoom},
742   { "setZoom", appui_setZoom},
743   { "setResources", appui_setResources},
744   { "pos", appui_pos},
745   { "globalPos", appui_globalPos},
746   { "unsnappedPos", appui_unsnappedPos},
747   { "simpleSnapPos", appui_simpleSnapPos },
748   { "setFifiVisible", appui_setFifiVisible},
749   { "setInkMode", appui_setInkMode },
750   { "setSelectionVisible", appui_setSelectionVisible},
751   { "setSnap", appui_setSnap},
752   { "setSnapIndicator", appui_setSnapIndicator},
753   { "setAutoOrigin", appui_setAutoOrigin },
754   { "update", appui_update},
755   { "finishTool", appui_finishTool},
756   { "canvasSize", appui_canvasSize },
757   { "setCursor", appui_setCursor },
758   { "setNumbering", appui_setNumbering },
759   { "setPretty", appui_setPretty },
760   { "setScreen", appui_setScreen },
761   { "type3Font", appui_type3Font },
762   // --------------------------------------------------------------------
763   { "panTool", appui_pantool },
764   { "selectTool", appui_selecttool },
765   { "transformTool", appui_transformtool },
766   { "shapeTool", appui_shapetool },
767   { "pasteTool", appui_pastetool },
768   // --------------------------------------------------------------------
769   { "win", appui_win},
770   { "close", appui_close},
771   { "setClipboard", appui_setClipboard },
772   { "clipboard", appui_clipboard },
773   { "setActionState", appui_setActionState },
774   { "actionState", appui_actionState },
775   { "actionInfo", appui_actionInfo },
776   { "explain", appui_explain },
777   { "setWindowTitle", appui_setWindowTitle },
778   { "setupSymbolicNames", appui_setupSymbolicNames },
779   { "setAttributes", appui_setAttributes },
780   { "setGridAngleSize", appui_setGridAngleSize },
781   { "setLayers", appui_setLayers },
782   { "setNumbers", appui_setNumbers },
783   { "setBookmarks", appui_setBookmarks },
784   { "setNotes", appui_setNotes },
785   { "setRecentFiles", appui_setRecentFiles },
786   { "showTool", appui_showTool },
787   { "selectPage", appui_selectPage },
788   { "pageSorter", appui_pageSorter },
789   { "renderPage", appui_renderPage },
790   { nullptr, nullptr }
791 };
792 
793 // --------------------------------------------------------------------
794 
appui_constructor(lua_State * L)795 static int appui_constructor(lua_State *L)
796 {
797   luaL_checktype(L, 1, LUA_TTABLE); // this is the model
798 
799   AppUiBase **ui = (AppUiBase **) lua_newuserdata(L, sizeof(AppUiBase *));
800   *ui = nullptr;
801   luaL_getmetatable(L, "Ipe.appui");
802   lua_setmetatable(L, -2);
803 
804   lua_pushvalue(L, 1);
805   int model = luaL_ref(L, LUA_REGISTRYINDEX);
806   *ui = createAppUi(L, model);
807 
808   CanvasBase::Style style;
809   style.pretty = false;
810   style.paperColor = Color(1000,1000,1000);
811   style.primarySelectionColor = Color(1000,0,0);
812   style.secondarySelectionColor = Color(1000,0,1000);
813   style.classicGrid = false;
814   style.thinLine = 0.2;
815   style.thickLine = 0.9;
816   style.thinStep = 1;
817   style.thickStep = 4;
818   style.paperClip = false;
819   style.numberPages = false;
820 
821   lua_getglobal(L, "prefs");
822 
823   lua_getfield(L, -1, "canvas_style");
824   if (!lua_isnil(L, -1)) {
825     lua_getfield(L, -1, "paper_color");
826     if (!lua_isnil(L, -1))
827       style.paperColor = check_color(L, lua_gettop(L));
828     lua_pop(L, 1); // paper_color
829 
830     lua_getfield(L, -1, "primary_color");
831     if (!lua_isnil(L, -1))
832       style.primarySelectionColor = check_color(L, lua_gettop(L));
833     lua_pop(L, 1); // primary_color
834 
835     lua_getfield(L, -1, "secondary_color");
836     if (!lua_isnil(L, -1))
837       style.secondarySelectionColor = check_color(L, lua_gettop(L));
838     lua_pop(L, 1); // secondary_color
839 
840     lua_getfield(L, -1, "classic_grid");
841     style.classicGrid = lua_toboolean(L, -1);
842     lua_pop(L, 1); // classic_grid
843 
844     lua_getfield(L, -1, "thin_grid_line");
845     if (lua_isnumber(L, -1))
846       style.thinLine = lua_tonumberx(L, -1, nullptr);
847     lua_pop(L, 1); // thin_grid_line
848 
849     lua_getfield(L, -1, "thick_grid_line");
850     if (lua_isnumber(L, -1))
851       style.thickLine = lua_tonumberx(L, -1, nullptr);
852     lua_pop(L, 1); // thick_grid_line
853 
854     lua_getfield(L, -1, "thin_step");
855     if (lua_isnumber(L, -1))
856       style.thinStep = lua_tointegerx(L, -1, nullptr);
857     lua_pop(L, 1); // thin_step
858 
859     lua_getfield(L, -1, "thick_step");
860     if (lua_isnumber(L, -1))
861       style.thickStep = lua_tointegerx(L, -1, nullptr);
862     lua_pop(L, 1); // thick_step
863   }
864   lua_pop(L, 1); // canvas_style
865 
866   int width = -1, height = -1, x = -1, y = -1;
867   lua_getfield(L, -1, "window_size");
868   if (lua_istable(L, -1)) {
869     lua_rawgeti(L, -1, 1);
870     if (lua_isnumber(L, -1))
871       width = lua_tointegerx(L, -1, nullptr);
872     lua_rawgeti(L, -2, 2);
873     if (lua_isnumber(L, -1))
874       height = lua_tointegerx(L, -1, nullptr);
875     lua_rawgeti(L, -3, 3);
876     if (lua_isnumber(L, -1))
877       x = lua_tointegerx(L, -1, nullptr);
878     lua_rawgeti(L, -4, 4);
879     if (lua_isnumber(L, -1))
880       y = lua_tointegerx(L, -1, nullptr);
881     lua_pop(L, 4);
882   }
883   lua_pop(L, 1); // window_size
884 
885   lua_pop(L, 1); // prefs
886 
887   (*ui)->canvas()->setCanvasStyle(style);
888 
889   (*ui)->showWindow(width, height, x, y);
890 
891   return 1;
892 }
893 
894 // --------------------------------------------------------------------
895 
luaopen_appui(lua_State * L)896 int luaopen_appui(lua_State *L)
897 {
898   lua_pushcfunction(L, appui_constructor);
899   lua_setglobal(L, "AppUi");
900   make_metatable(L, "Ipe.appui", appui_methods);
901   return 0;
902 }
903 
904 // --------------------------------------------------------------------
905