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