1 // Aseprite
2 // Copyright (C) 2001-2018  David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "app/context.h"
12 
13 #include "app/app.h"
14 #include "app/commands/command.h"
15 #include "app/commands/commands.h"
16 #include "app/console.h"
17 #include "app/doc.h"
18 #include "app/site.h"
19 #include "doc/layer.h"
20 
21 #include <algorithm>
22 #include <stdexcept>
23 
24 namespace app {
25 
Context()26 Context::Context()
27   : m_docs(this)
28   , m_lastSelectedDoc(nullptr)
29 {
30   m_docs.add_observer(this);
31 }
32 
~Context()33 Context::~Context()
34 {
35   m_docs.remove_observer(this);
36 }
37 
sendDocumentToTop(Doc * document)38 void Context::sendDocumentToTop(Doc* document)
39 {
40   ASSERT(document != NULL);
41 
42   documents().move(document, 0);
43 }
44 
activeSite() const45 Site Context::activeSite() const
46 {
47   Site site;
48   onGetActiveSite(&site);
49   return site;
50 }
51 
activeDocument() const52 Doc* Context::activeDocument() const
53 {
54   Site site;
55   onGetActiveSite(&site);
56   return site.document();
57 }
58 
setActiveDocument(Doc * document)59 void Context::setActiveDocument(Doc* document)
60 {
61   onSetActiveDocument(document);
62 }
63 
hasModifiedDocuments() const64 bool Context::hasModifiedDocuments() const
65 {
66   for (auto doc : documents())
67     if (doc->isModified())
68       return true;
69   return false;
70 }
71 
notifyActiveSiteChanged()72 void Context::notifyActiveSiteChanged()
73 {
74   Site site = activeSite();
75   notify_observers<const Site&>(&ContextObserver::onActiveSiteChange, site);
76 }
77 
executeCommand(const char * commandName)78 void Context::executeCommand(const char* commandName)
79 {
80   Command* cmd = Commands::instance()->byId(commandName);
81   if (cmd)
82     executeCommand(cmd);
83   else
84     throw std::runtime_error("Invalid command name");
85 }
86 
executeCommand(Command * command,const Params & params)87 void Context::executeCommand(Command* command, const Params& params)
88 {
89   Console console;
90 
91   ASSERT(command != NULL);
92   if (command == NULL)
93     return;
94 
95   LOG(VERBOSE) << "CTXT: Executing command " << command->id() << "\n";
96   try {
97     m_flags.update(this);
98 
99     ASSERT(!command->needsParams() || !params.empty());
100 
101     command->loadParams(params);
102 
103     CommandExecutionEvent ev(command);
104     BeforeCommandExecution(ev);
105 
106     if (ev.isCanceled()) {
107       LOG(VERBOSE) << "CTXT: Command " << command->id() << " was canceled/simulated.\n";
108     }
109     else if (command->isEnabled(this)) {
110       command->execute(this);
111       LOG(VERBOSE) << "CTXT: Command " << command->id() << " executed successfully\n";
112     }
113     else {
114       LOG(VERBOSE) << "CTXT: Command " << command->id() << " is disabled\n";
115     }
116 
117     AfterCommandExecution(ev);
118 
119     // TODO move this code to another place (e.g. a Workplace/Tabs widget)
120     if (isUIAvailable())
121       app_rebuild_documents_tabs();
122   }
123   catch (base::Exception& e) {
124     LOG(ERROR) << "CTXT: Exception caught executing " << command->id() << " command\n"
125                << e.what() << "\n";
126 
127     Console::showException(e);
128   }
129   catch (std::exception& e) {
130     LOG(ERROR) << "CTXT: std::exception caught executing " << command->id() << " command\n"
131                << e.what() << "\n";
132 
133     console.printf("An error ocurred executing the command.\n\nDetails:\n%s", e.what());
134   }
135 #ifdef NDEBUG
136   catch (...) {
137     LOG(ERROR) << "CTXT: Unknown exception executing " << command->id() << " command\n";
138 
139     console.printf("An unknown error ocurred executing the command.\n"
140                    "Please save your work, close the program, try it\n"
141                    "again, and report this bug.\n\n"
142                    "Details: Unknown exception caught. This can be bad\n"
143                    "memory access, divison by zero, etc.");
144   }
145 #endif
146 }
147 
onCreateDocument(CreateDocArgs * args)148 void Context::onCreateDocument(CreateDocArgs* args)
149 {
150   args->setDocument(new Doc(nullptr));
151 }
152 
onAddDocument(Doc * doc)153 void Context::onAddDocument(Doc* doc)
154 {
155   m_lastSelectedDoc = doc;
156 }
157 
onRemoveDocument(Doc * doc)158 void Context::onRemoveDocument(Doc* doc)
159 {
160   if (doc == m_lastSelectedDoc)
161     m_lastSelectedDoc = nullptr;
162 }
163 
onGetActiveSite(Site * site) const164 void Context::onGetActiveSite(Site* site) const
165 {
166   // Default/dummy site (maybe for batch/command line mode)
167   if (Doc* doc = m_lastSelectedDoc) {
168     site->document(doc);
169     site->sprite(doc->sprite());
170     site->layer(doc->sprite()->root()->firstLayer());
171     site->frame(0);
172   }
173 }
174 
onSetActiveDocument(Doc * doc)175 void Context::onSetActiveDocument(Doc* doc)
176 {
177   m_lastSelectedDoc = doc;
178 }
179 
180 } // namespace app
181