1 | // Aseprite |
2 | // Copyright (C) 2018-2022 Igara Studio S.A. |
3 | // Copyright (C) 2015-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/app.h" |
13 | #include "app/commands/commands.h" |
14 | #include "app/commands/params.h" |
15 | #include "app/context.h" |
16 | #include "app/doc.h" |
17 | #include "app/doc_access.h" |
18 | #include "app/i18n/strings.h" |
19 | #include "app/inline_command_execution.h" |
20 | #include "app/loop_tag.h" |
21 | #include "app/modules/palettes.h" |
22 | #include "app/pref/preferences.h" |
23 | #include "app/script/api_version.h" |
24 | #include "app/script/docobj.h" |
25 | #include "app/script/engine.h" |
26 | #include "app/script/luacpp.h" |
27 | #include "app/script/security.h" |
28 | #include "app/site.h" |
29 | #include "app/tools/active_tool.h" |
30 | #include "app/tools/ink.h" |
31 | #include "app/tools/tool_box.h" |
32 | #include "app/tools/tool_loop.h" |
33 | #include "app/tools/tool_loop_manager.h" |
34 | #include "app/tx.h" |
35 | #include "app/ui/context_bar.h" |
36 | #include "app/ui/doc_view.h" |
37 | #include "app/ui/editor/editor.h" |
38 | #include "app/ui/editor/tool_loop_impl.h" |
39 | #include "app/ui/timeline/timeline.h" |
40 | #include "app/ui_context.h" |
41 | #include "base/fs.h" |
42 | #include "base/replace_string.h" |
43 | #include "base/version.h" |
44 | #include "doc/layer.h" |
45 | #include "doc/primitives.h" |
46 | #include "doc/tag.h" |
47 | #include "render/render.h" |
48 | #include "ui/alert.h" |
49 | #include "ver/info.h" |
50 | |
51 | #include <cstring> |
52 | #include <iostream> |
53 | |
54 | namespace app { |
55 | namespace script { |
56 | |
57 | int load_sprite_from_file(lua_State* L, const char* filename, |
58 | const LoadSpriteFromFileParam param) |
59 | { |
60 | std::string absFn = base::get_absolute_path(filename); |
61 | if (!ask_access(L, absFn.c_str(), FileAccessMode::Read, ResourceType::File)) |
62 | return luaL_error(L, "script doesn't have access to open file %s" , |
63 | absFn.c_str()); |
64 | |
65 | app::Context* ctx = App::instance()->context(); |
66 | Doc* oldDoc = ctx->activeDocument(); |
67 | |
68 | Command* openCommand = |
69 | Commands::instance()->byId(CommandId::OpenFile()); |
70 | Params params; |
71 | params.set("filename" , absFn.c_str()); |
72 | if (param == LoadSpriteFromFileParam::OneFrameAsSprite || |
73 | param == LoadSpriteFromFileParam::OneFrameAsImage) |
74 | params.set("oneframe" , "true" ); |
75 | ctx->executeCommand(openCommand, params); |
76 | |
77 | Doc* newDoc = ctx->activeDocument(); |
78 | if (newDoc != oldDoc) { |
79 | if (param == LoadSpriteFromFileParam::OneFrameAsImage) { |
80 | doc::Sprite* sprite = newDoc->sprite(); |
81 | |
82 | // Render the first frame of the sprite |
83 | // TODO add "frame" parameter to render different frames |
84 | std::unique_ptr<doc::Image> image(doc::Image::create(sprite->spec())); |
85 | doc::clear_image(image.get(), sprite->transparentColor()); |
86 | render::Render().renderSprite(image.get(), sprite, 0); |
87 | |
88 | // Restore the old document and active and destroy the recently |
89 | // loaded sprite. |
90 | ctx->setActiveDocument(oldDoc); |
91 | |
92 | try { |
93 | DocDestroyer destroyer(ctx, newDoc, 500); |
94 | destroyer.destroyDocument(); |
95 | } |
96 | catch (const LockedDocException& ex) { |
97 | // Almost impossible? |
98 | luaL_error(L, "cannot lock document to close it\n%s" , ex.what()); |
99 | } |
100 | |
101 | push_image(L, image.release()); |
102 | return 1; |
103 | } |
104 | else { |
105 | push_docobj(L, newDoc->sprite()); |
106 | } |
107 | } |
108 | else |
109 | lua_pushnil(L); |
110 | return 1; |
111 | } |
112 | |
113 | namespace { |
114 | |
115 | int App_open(lua_State* L) |
116 | { |
117 | return load_sprite_from_file( |
118 | L, luaL_checkstring(L, 1), LoadSpriteFromFileParam::FullAniAsSprite); |
119 | } |
120 | |
121 | int App_exit(lua_State* L) |
122 | { |
123 | app::Context* ctx = App::instance()->context(); |
124 | if (ctx && ctx->isUIAvailable()) { |
125 | Command* exitCommand = |
126 | Commands::instance()->byId(CommandId::Exit()); |
127 | ctx->executeCommand(exitCommand); |
128 | } |
129 | return 0; |
130 | } |
131 | |
132 | int App_transaction(lua_State* L) |
133 | { |
134 | int top = lua_gettop(L); |
135 | int nresults = 0; |
136 | if (lua_isfunction(L, 1)) { |
137 | Tx tx; // Create a new transaction so it exists in the whole |
138 | // duration of the argument function call. |
139 | lua_pushvalue(L, -1); |
140 | if (lua_pcall(L, 0, LUA_MULTRET, 0) == LUA_OK) |
141 | tx.commit(); |
142 | else |
143 | return lua_error(L); // pcall already put an error object on the stack |
144 | nresults = lua_gettop(L) - top; |
145 | } |
146 | return nresults; |
147 | } |
148 | |
149 | int App_undo(lua_State* L) |
150 | { |
151 | app::Context* ctx = App::instance()->context(); |
152 | if (ctx) { |
153 | Command* undo = Commands::instance()->byId(CommandId::Undo()); |
154 | ctx->executeCommand(undo); |
155 | } |
156 | return 0; |
157 | } |
158 | |
159 | int App_redo(lua_State* L) |
160 | { |
161 | app::Context* ctx = App::instance()->context(); |
162 | if (ctx) { |
163 | Command* redo = Commands::instance()->byId(CommandId::Redo()); |
164 | ctx->executeCommand(redo); |
165 | } |
166 | return 0; |
167 | } |
168 | |
169 | int App_alert(lua_State* L) |
170 | { |
171 | #ifdef ENABLE_UI |
172 | app::Context* ctx = App::instance()->context(); |
173 | if (!ctx || !ctx->isUIAvailable()) |
174 | return 0; // No UI to show the alert |
175 | // app.alert("text...") |
176 | else if (lua_isstring(L, 1)) { |
177 | ui::AlertPtr alert(new ui::Alert); |
178 | alert->addLabel(lua_tostring(L, 1), ui::CENTER); |
179 | alert->addButton(Strings::general_ok()); |
180 | lua_pushinteger(L, alert->show()); |
181 | return 1; |
182 | } |
183 | // app.alert{ ... } |
184 | else if (lua_istable(L, 1)) { |
185 | ui::AlertPtr alert(new ui::Alert); |
186 | |
187 | int type = lua_getfield(L, 1, "title" ); |
188 | if (type != LUA_TNIL) |
189 | alert->setTitle(lua_tostring(L, -1)); |
190 | lua_pop(L, 1); |
191 | |
192 | type = lua_getfield(L, 1, "text" ); |
193 | if (type == LUA_TTABLE) { |
194 | lua_pushnil(L); |
195 | while (lua_next(L, -2) != 0) { |
196 | const char* v = luaL_tolstring(L, -1, nullptr); |
197 | if (v) |
198 | alert->addLabel(v, ui::LEFT); |
199 | lua_pop(L, 2); |
200 | } |
201 | } |
202 | else if (type == LUA_TSTRING) { |
203 | alert->addLabel(lua_tostring(L, -1), ui::LEFT); |
204 | } |
205 | lua_pop(L, 1); |
206 | |
207 | int nbuttons = 0; |
208 | type = lua_getfield(L, 1, "buttons" ); |
209 | if (type == LUA_TTABLE) { |
210 | lua_pushnil(L); |
211 | while (lua_next(L, -2) != 0) { |
212 | const char* v = luaL_tolstring(L, -1, nullptr); |
213 | if (v) { |
214 | alert->addButton(v); |
215 | ++nbuttons; |
216 | } |
217 | lua_pop(L, 2); |
218 | } |
219 | } |
220 | else if (type == LUA_TSTRING) { |
221 | alert->addButton(lua_tostring(L, -1)); |
222 | ++nbuttons; |
223 | } |
224 | lua_pop(L, 1); |
225 | |
226 | if (nbuttons == 0) |
227 | alert->addButton(Strings::general_ok()); |
228 | |
229 | lua_pushinteger(L, alert->show()); |
230 | return 1; |
231 | } |
232 | #endif |
233 | return 0; |
234 | } |
235 | |
236 | int App_refresh(lua_State* L) |
237 | { |
238 | #ifdef ENABLE_UI |
239 | app::Context* ctx = App::instance()->context(); |
240 | if (ctx && ctx->isUIAvailable()) |
241 | app_refresh_screen(); |
242 | #endif |
243 | return 0; |
244 | } |
245 | |
246 | int App_useTool(lua_State* L) |
247 | { |
248 | // First argument must be a table |
249 | if (!lua_istable(L, 1)) |
250 | return luaL_error(L, "app.useTool() must be called with a table as its first argument" ); |
251 | |
252 | auto ctx = App::instance()->context(); |
253 | Site site = ctx->activeSite(); |
254 | |
255 | // Draw in a specific cel, layer, or frame |
256 | int type = lua_getfield(L, 1, "layer" ); |
257 | if (type != LUA_TNIL) { |
258 | if (auto layer = get_docobj<Layer>(L, -1)) { |
259 | site.document(static_cast<Doc*>(layer->sprite()->document())); |
260 | site.sprite(layer->sprite()); |
261 | site.layer(layer); |
262 | } |
263 | } |
264 | lua_pop(L, 1); |
265 | |
266 | type = lua_getfield(L, 1, "frame" ); |
267 | if (type != LUA_TNIL) { |
268 | site.frame(get_frame_number_from_arg(L, -1)); |
269 | } |
270 | lua_pop(L, 1); |
271 | |
272 | type = lua_getfield(L, 1, "cel" ); |
273 | if (type != LUA_TNIL) { |
274 | if (auto cel = get_docobj<Cel>(L, -1)) { |
275 | site.document(static_cast<Doc*>(cel->sprite()->document())); |
276 | site.sprite(cel->sprite()); |
277 | site.layer(cel->layer()); |
278 | site.frame(cel->frame()); |
279 | } |
280 | } |
281 | lua_pop(L, 1); |
282 | |
283 | if (!site.document()) |
284 | return luaL_error(L, "there is no active document to draw with the tool" ); |
285 | |
286 | // Options to create the ToolLoop (tool, ink, color, opacity, etc.) |
287 | ToolLoopParams params; |
288 | |
289 | // Mouse button |
290 | params.button = tools::ToolLoop::Left; |
291 | type = lua_getfield(L, 1, "button" ); |
292 | if (type != LUA_TNIL) { |
293 | // Only supported button at the moment left (default) or right |
294 | if (lua_tointeger(L, -1) == (int)ui::kButtonRight) |
295 | params.button = tools::ToolLoop::Right; |
296 | } |
297 | lua_pop(L, 1); |
298 | |
299 | // Select tool by name |
300 | const int buttonIdx = (params.button == tools::ToolLoop::Left ? 0: 1); |
301 | auto activeToolMgr = App::instance()->activeToolManager(); |
302 | params.tool = activeToolMgr->activeTool(); |
303 | params.ink = params.tool->getInk(buttonIdx); |
304 | params.controller = params.tool->getController(buttonIdx); |
305 | type = lua_getfield(L, 1, "tool" ); |
306 | if (type != LUA_TNIL) { |
307 | if (auto toolArg = get_tool_from_arg(L, -1)) { |
308 | params.tool = toolArg; |
309 | params.ink = params.tool->getInk(buttonIdx); |
310 | params.controller = params.tool->getController(buttonIdx); |
311 | } |
312 | else |
313 | return luaL_error(L, "invalid tool specified in app.useTool() function" ); |
314 | } |
315 | lua_pop(L, 1); |
316 | |
317 | // Select ink by name |
318 | type = lua_getfield(L, 1, "ink" ); |
319 | if (type != LUA_TNIL) |
320 | params.inkType = get_value_from_lua<tools::InkType>(L, -1); |
321 | lua_pop(L, 1); |
322 | |
323 | // Color |
324 | type = lua_getfield(L, 1, "color" ); |
325 | if (type != LUA_TNIL) |
326 | params.fg = convert_args_into_color(L, -1); |
327 | else { |
328 | // Default color is the active fgColor |
329 | params.fg = Preferences::instance().colorBar.fgColor(); |
330 | } |
331 | lua_pop(L, 1); |
332 | |
333 | type = lua_getfield(L, 1, "bgColor" ); |
334 | if (type != LUA_TNIL) |
335 | params.bg = convert_args_into_color(L, -1); |
336 | else |
337 | params.bg = params.fg; |
338 | lua_pop(L, 1); |
339 | |
340 | // Adjust ink depending on "inkType" and "color" |
341 | // (e.g. InkType::SIMPLE depends on the color too, to adjust |
342 | // eraser/alpha compositing/opaque depending on the color alpha |
343 | // value). |
344 | params.ink = activeToolMgr->adjustToolInkDependingOnSelectedInkType( |
345 | params.ink, params.inkType, params.fg); |
346 | |
347 | // Brush |
348 | type = lua_getfield(L, 1, "brush" ); |
349 | if (type != LUA_TNIL) |
350 | params.brush = get_brush_from_arg(L, -1); |
351 | else { |
352 | // Default brush is the active brush in the context bar |
353 | #ifdef ENABLE_UI |
354 | if (App::instance()->isGui() && |
355 | App::instance()->contextBar()) { |
356 | params.brush = App::instance() |
357 | ->contextBar()->activeBrush(params.tool, |
358 | params.ink); |
359 | } |
360 | #endif |
361 | } |
362 | lua_pop(L, 1); |
363 | if (!params.brush) { |
364 | // In case the brush is nullptr (e.g. there is no UI) we use the |
365 | // default 1 pixel brush (e.g. to run scripts from CLI). |
366 | params.brush.reset(new Brush(BrushType::kCircleBrushType, 1, 0)); |
367 | } |
368 | |
369 | // Opacity, tolerance, and others |
370 | type = lua_getfield(L, 1, "opacity" ); |
371 | if (type != LUA_TNIL) { |
372 | params.opacity = lua_tointeger(L, -1); |
373 | params.opacity = std::clamp(params.opacity, 0, 255); |
374 | } |
375 | lua_pop(L, 1); |
376 | |
377 | type = lua_getfield(L, 1, "tolerance" ); |
378 | if (type != LUA_TNIL) { |
379 | params.tolerance = lua_tointeger(L, -1); |
380 | params.tolerance = std::clamp(params.tolerance, 0, 255); |
381 | } |
382 | lua_pop(L, 1); |
383 | |
384 | type = lua_getfield(L, 1, "contiguous" ); |
385 | if (type != LUA_TNIL) |
386 | params.contiguous = lua_toboolean(L, -1); |
387 | lua_pop(L, 1); |
388 | |
389 | type = lua_getfield(L, 1, "freehandAlgorithm" ); |
390 | if (type != LUA_TNIL) |
391 | params.freehandAlgorithm = get_value_from_lua<tools::FreehandAlgorithm>(L, -1); |
392 | lua_pop(L, 1); |
393 | |
394 | if (params.ink->isSelection()) { |
395 | gen::SelectionMode selectionMode = Preferences::instance().selection.mode(); |
396 | |
397 | type = lua_getfield(L, 1, "selection" ); |
398 | if (type != LUA_TNIL) |
399 | selectionMode = get_value_from_lua<gen::SelectionMode>(L, -1); |
400 | lua_pop(L, 1); |
401 | |
402 | switch (selectionMode) { |
403 | case gen::SelectionMode::REPLACE: |
404 | params.modifiers = tools::ToolLoopModifiers::kReplaceSelection; |
405 | break; |
406 | case gen::SelectionMode::ADD: |
407 | params.modifiers = tools::ToolLoopModifiers::kAddSelection; |
408 | break; |
409 | case gen::SelectionMode::SUBTRACT: |
410 | params.modifiers = tools::ToolLoopModifiers::kSubtractSelection; |
411 | break; |
412 | case gen::SelectionMode::INTERSECT: |
413 | params.modifiers = tools::ToolLoopModifiers::kIntersectSelection; |
414 | break; |
415 | } |
416 | } |
417 | |
418 | // Are we going to modify pixels or tiles? |
419 | type = lua_getfield(L, 1, "tilemapMode" ); |
420 | if (type != LUA_TNIL) { |
421 | site.tilemapMode(TilemapMode(lua_tointeger(L, -1))); |
422 | } |
423 | lua_pop(L, 1); |
424 | |
425 | // How the tileset must be modified depending on this tool usage |
426 | type = lua_getfield(L, 1, "tilesetMode" ); |
427 | if (type != LUA_TNIL) { |
428 | site.tilesetMode(TilesetMode(lua_tointeger(L, -1))); |
429 | } |
430 | lua_pop(L, 1); |
431 | |
432 | // Do the tool loop |
433 | type = lua_getfield(L, 1, "points" ); |
434 | if (type == LUA_TTABLE) { |
435 | InlineCommandExecution inlineCmd(ctx); |
436 | |
437 | std::unique_ptr<tools::ToolLoop> loop( |
438 | create_tool_loop_for_script(ctx, site, params)); |
439 | if (!loop) |
440 | return luaL_error(L, "cannot draw in the active site" ); |
441 | |
442 | tools::ToolLoopManager manager(loop.get()); |
443 | tools::Pointer lastPointer; |
444 | bool first = true; |
445 | |
446 | lua_pushnil(L); |
447 | while (lua_next(L, -2) != 0) { |
448 | gfx::Point pt = convert_args_into_point(L, -1); |
449 | |
450 | tools::Pointer pointer( |
451 | pt, |
452 | // TODO configurable params |
453 | tools::Vec2(0.0f, 0.0f), |
454 | tools::Pointer::Button::Left, |
455 | tools::Pointer::Type::Unknown, |
456 | 0.0f); |
457 | if (first) { |
458 | first = false; |
459 | manager.prepareLoop(pointer); |
460 | manager.pressButton(pointer); |
461 | } |
462 | else { |
463 | manager.movement(pointer); |
464 | } |
465 | lastPointer = pointer; |
466 | lua_pop(L, 1); |
467 | } |
468 | if (!first) |
469 | manager.releaseButton(lastPointer); |
470 | |
471 | manager.end(); |
472 | } |
473 | lua_pop(L, 1); |
474 | return 0; |
475 | } |
476 | |
477 | int App_get_events(lua_State* L) |
478 | { |
479 | push_app_events(L); |
480 | return 1; |
481 | } |
482 | |
483 | int App_get_activeSprite(lua_State* L) |
484 | { |
485 | app::Context* ctx = App::instance()->context(); |
486 | Doc* doc = ctx->activeDocument(); |
487 | if (doc) |
488 | push_docobj(L, doc->sprite()); |
489 | else |
490 | lua_pushnil(L); |
491 | return 1; |
492 | } |
493 | |
494 | int App_get_activeLayer(lua_State* L) |
495 | { |
496 | app::Context* ctx = App::instance()->context(); |
497 | Site site = ctx->activeSite(); |
498 | if (site.layer()) |
499 | push_docobj(L, site.layer()); |
500 | else |
501 | lua_pushnil(L); |
502 | return 1; |
503 | } |
504 | |
505 | int App_get_activeFrame(lua_State* L) |
506 | { |
507 | app::Context* ctx = App::instance()->context(); |
508 | Site site = ctx->activeSite(); |
509 | if (site.sprite()) |
510 | push_sprite_frame(L, site.sprite(), site.frame()); |
511 | else |
512 | lua_pushnil(L); |
513 | return 1; |
514 | } |
515 | |
516 | int App_get_activeCel(lua_State* L) |
517 | { |
518 | app::Context* ctx = App::instance()->context(); |
519 | Site site = ctx->activeSite(); |
520 | if (site.cel()) |
521 | push_sprite_cel(L, site.cel()); |
522 | else |
523 | lua_pushnil(L); |
524 | return 1; |
525 | } |
526 | |
527 | int App_get_activeImage(lua_State* L) |
528 | { |
529 | app::Context* ctx = App::instance()->context(); |
530 | Site site = ctx->activeSite(); |
531 | if (site.cel()) |
532 | push_cel_image(L, site.cel()); |
533 | else |
534 | lua_pushnil(L); |
535 | return 1; |
536 | } |
537 | |
538 | int App_get_activeTag(lua_State* L) |
539 | { |
540 | Tag* tag = nullptr; |
541 | |
542 | app::Context* ctx = App::instance()->context(); |
543 | Site site = ctx->activeSite(); |
544 | if (site.sprite()) { |
545 | #ifdef ENABLE_UI |
546 | if (App::instance()->timeline()) { |
547 | tag = App::instance()->timeline()->getTagByFrame(site.frame(), false); |
548 | } |
549 | else |
550 | #endif |
551 | { |
552 | tag = get_animation_tag(site.sprite(), site.frame()); |
553 | } |
554 | } |
555 | |
556 | if (tag) |
557 | push_docobj(L, tag); |
558 | else |
559 | lua_pushnil(L); |
560 | return 1; |
561 | } |
562 | |
563 | int App_get_sprites(lua_State* L) |
564 | { |
565 | push_sprites(L); |
566 | return 1; |
567 | } |
568 | |
569 | int App_get_fgColor(lua_State* L) |
570 | { |
571 | push_obj<app::Color>(L, Preferences::instance().colorBar.fgColor()); |
572 | return 1; |
573 | } |
574 | |
575 | int App_set_fgColor(lua_State* L) |
576 | { |
577 | Preferences::instance().colorBar.fgColor(convert_args_into_color(L, 2)); |
578 | return 0; |
579 | } |
580 | |
581 | int App_get_bgColor(lua_State* L) |
582 | { |
583 | push_obj<app::Color>(L, Preferences::instance().colorBar.bgColor()); |
584 | return 1; |
585 | } |
586 | |
587 | int App_set_bgColor(lua_State* L) |
588 | { |
589 | Preferences::instance().colorBar.bgColor(convert_args_into_color(L, 2)); |
590 | return 0; |
591 | } |
592 | |
593 | int App_get_site(lua_State* L) |
594 | { |
595 | app::Context* ctx = App::instance()->context(); |
596 | Site site = ctx->activeSite(); |
597 | push_obj(L, site); |
598 | return 1; |
599 | } |
600 | |
601 | int App_get_range(lua_State* L) |
602 | { |
603 | app::Context* ctx = App::instance()->context(); |
604 | Site site = ctx->activeSite(); |
605 | push_doc_range(L, site); |
606 | return 1; |
607 | } |
608 | |
609 | int App_get_isUIAvailable(lua_State* L) |
610 | { |
611 | app::Context* ctx = App::instance()->context(); |
612 | lua_pushboolean(L, ctx && ctx->isUIAvailable()); |
613 | return 1; |
614 | } |
615 | |
616 | int App_get_version(lua_State* L) |
617 | { |
618 | std::string ver = get_app_version(); |
619 | base::replace_string(ver, "-x64" , "" ); // Remove "-x64" suffix |
620 | push_version(L, base::Version(ver)); |
621 | return 1; |
622 | } |
623 | |
624 | int App_get_apiVersion(lua_State* L) |
625 | { |
626 | lua_pushinteger(L, API_VERSION); |
627 | return 1; |
628 | } |
629 | |
630 | int App_get_activeTool(lua_State* L) |
631 | { |
632 | tools::Tool* tool = App::instance()->activeToolManager()->activeTool(); |
633 | push_tool(L, tool); |
634 | return 1; |
635 | } |
636 | |
637 | int App_get_activeBrush(lua_State* L) |
638 | { |
639 | #if ENABLE_UI |
640 | App* app = App::instance(); |
641 | if (app->isGui()) { |
642 | doc::BrushRef brush = app->contextBar()->activeBrush(); |
643 | push_brush(L, brush); |
644 | return 1; |
645 | } |
646 | #endif |
647 | push_brush(L, doc::BrushRef(new doc::Brush())); |
648 | return 1; |
649 | } |
650 | |
651 | int App_get_defaultPalette(lua_State* L) |
652 | { |
653 | const Palette* pal = get_default_palette(); |
654 | if (pal) |
655 | push_palette(L, new Palette(*pal)); |
656 | else |
657 | lua_pushnil(L); |
658 | return 1; |
659 | } |
660 | |
661 | int App_set_activeSprite(lua_State* L) |
662 | { |
663 | auto sprite = get_docobj<Sprite>(L, 2); |
664 | app::Context* ctx = App::instance()->context(); |
665 | doc::Document* doc = sprite->document(); |
666 | ctx->setActiveDocument(static_cast<Doc*>(doc)); |
667 | return 0; |
668 | } |
669 | |
670 | int App_set_activeLayer(lua_State* L) |
671 | { |
672 | auto layer = get_docobj<Layer>(L, 2); |
673 | app::Context* ctx = App::instance()->context(); |
674 | ctx->setActiveLayer(layer); |
675 | return 0; |
676 | } |
677 | |
678 | int App_set_activeFrame(lua_State* L) |
679 | { |
680 | const doc::frame_t frame = get_frame_number_from_arg(L, 2); |
681 | app::Context* ctx = App::instance()->context(); |
682 | ctx->setActiveFrame(frame); |
683 | return 0; |
684 | } |
685 | |
686 | int App_set_activeCel(lua_State* L) |
687 | { |
688 | const auto cel = get_docobj<Cel>(L, 2); |
689 | app::Context* ctx = App::instance()->context(); |
690 | ctx->setActiveLayer(cel->layer()); |
691 | ctx->setActiveFrame(cel->frame()); |
692 | return 0; |
693 | } |
694 | |
695 | int App_set_activeImage(lua_State* L) |
696 | { |
697 | const auto cel = get_image_cel_from_arg(L, 2); |
698 | if (!cel) |
699 | return 0; |
700 | |
701 | app::Context* ctx = App::instance()->context(); |
702 | ctx->setActiveLayer(cel->layer()); |
703 | ctx->setActiveFrame(cel->frame()); |
704 | return 0; |
705 | } |
706 | |
707 | int App_set_activeTool(lua_State* L) |
708 | { |
709 | if (auto tool = get_tool_from_arg(L, 2)) |
710 | App::instance()->activeToolManager()->setSelectedTool(tool); |
711 | return 0; |
712 | } |
713 | |
714 | int App_set_activeBrush(lua_State* L) |
715 | { |
716 | #if ENABLE_UI |
717 | if (auto brush = get_brush_from_arg(L, 2)) { |
718 | App* app = App::instance(); |
719 | if (app->isGui()) |
720 | app->contextBar()->setActiveBrush(brush); |
721 | } |
722 | #endif |
723 | return 0; |
724 | } |
725 | |
726 | int App_set_defaultPalette(lua_State* L) |
727 | { |
728 | if (const doc::Palette* pal = get_palette_from_arg(L, 2)) |
729 | set_default_palette(pal); |
730 | return 0; |
731 | } |
732 | |
733 | const luaL_Reg App_methods[] = { |
734 | { "open" , App_open }, |
735 | { "exit" , App_exit }, |
736 | { "transaction" , App_transaction }, |
737 | { "undo" , App_undo }, |
738 | { "redo" , App_redo }, |
739 | { "alert" , App_alert }, |
740 | { "refresh" , App_refresh }, |
741 | { "useTool" , App_useTool }, |
742 | { nullptr, nullptr } |
743 | }; |
744 | |
745 | const Property App_properties[] = { |
746 | { "activeSprite" , App_get_activeSprite, App_set_activeSprite }, |
747 | { "activeLayer" , App_get_activeLayer, App_set_activeLayer }, |
748 | { "activeFrame" , App_get_activeFrame, App_set_activeFrame }, |
749 | { "activeCel" , App_get_activeCel, App_set_activeCel }, |
750 | { "activeImage" , App_get_activeImage, App_set_activeImage }, |
751 | { "activeTag" , App_get_activeTag, nullptr }, |
752 | { "activeTool" , App_get_activeTool, App_set_activeTool }, |
753 | { "activeBrush" , App_get_activeBrush, App_set_activeBrush }, |
754 | { "sprites" , App_get_sprites, nullptr }, |
755 | { "fgColor" , App_get_fgColor, App_set_fgColor }, |
756 | { "bgColor" , App_get_bgColor, App_set_bgColor }, |
757 | { "version" , App_get_version, nullptr }, |
758 | { "apiVersion" , App_get_apiVersion, nullptr }, |
759 | { "site" , App_get_site, nullptr }, |
760 | { "range" , App_get_range, nullptr }, |
761 | { "isUIAvailable" , App_get_isUIAvailable, nullptr }, |
762 | { "defaultPalette" , App_get_defaultPalette, App_set_defaultPalette }, |
763 | { "events" , App_get_events, nullptr }, |
764 | { nullptr, nullptr, nullptr } |
765 | }; |
766 | |
767 | } // anonymous namespace |
768 | |
769 | DEF_MTNAME(App); |
770 | |
771 | void register_app_object(lua_State* L) |
772 | { |
773 | REG_CLASS(L, App); |
774 | REG_CLASS_PROPERTIES(L, App); |
775 | |
776 | lua_newtable(L); // Create a table which will be the "app" object |
777 | lua_pushvalue(L, -1); |
778 | luaL_getmetatable(L, get_mtname<App>()); |
779 | lua_setmetatable(L, -2); |
780 | lua_setglobal(L, "app" ); |
781 | lua_pop(L, 1); // Pop app table |
782 | } |
783 | |
784 | void set_app_params(lua_State* L, const Params& params) |
785 | { |
786 | lua_getglobal(L, "app" ); |
787 | lua_newtable(L); |
788 | for (const auto& kv : params) { |
789 | lua_pushstring(L, kv.second.c_str()); |
790 | lua_setfield(L, -2, kv.first.c_str()); |
791 | } |
792 | lua_setfield(L, -2, "params" ); |
793 | lua_pop(L, 1); |
794 | } |
795 | |
796 | } // namespace script |
797 | } // namespace app |
798 | |