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/app.h" 12 #include "app/doc.h" 13 #include "app/modules/editors.h" 14 #include "app/pref/preferences.h" 15 #include "app/site.h" 16 #include "app/ui/color_bar.h" 17 #include "app/ui/doc_view.h" 18 #include "app/ui/editor/editor.h" 19 #include "app/ui/input_chain.h" 20 #include "app/ui/main_window.h" 21 #include "app/ui/preview_editor.h" 22 #include "app/ui/status_bar.h" 23 #include "app/ui/timeline/timeline.h" 24 #include "app/ui/workspace.h" 25 #include "app/ui/workspace_tabs.h" 26 #include "app/ui_context.h" 27 #include "base/mutex.h" 28 #include "doc/sprite.h" 29 30 namespace app { 31 32 UIContext* UIContext::m_instance = nullptr; 33 UIContext()34UIContext::UIContext() 35 : m_lastSelectedView(nullptr) 36 { 37 documents().add_observer(&Preferences::instance()); 38 39 ASSERT(m_instance == NULL); 40 m_instance = this; 41 } 42 ~UIContext()43UIContext::~UIContext() 44 { 45 ASSERT(m_instance == this); 46 m_instance = NULL; 47 48 documents().remove_observer(&Preferences::instance()); 49 50 // The context must be empty at this point. (It's to check if the UI 51 // is working correctly, i.e. closing all files when the user can 52 // take any action about it.) 53 ASSERT(documents().empty()); 54 } 55 isUIAvailable() const56bool UIContext::isUIAvailable() const 57 { 58 return App::instance()->isGui(); 59 } 60 activeView() const61DocView* UIContext::activeView() const 62 { 63 if (!isUIAvailable()) 64 return nullptr; 65 66 Workspace* workspace = App::instance()->workspace(); 67 if (!workspace) 68 return nullptr; 69 70 WorkspaceView* view = workspace->activeView(); 71 if (DocView* docView = dynamic_cast<DocView*>(view)) 72 return docView; 73 else 74 return nullptr; 75 } 76 setActiveView(DocView * docView)77void UIContext::setActiveView(DocView* docView) 78 { 79 MainWindow* mainWin = App::instance()->mainWindow(); 80 81 // Prioritize workspace for user input. 82 App::instance()->inputChain().prioritize(mainWin->getWorkspace(), nullptr); 83 84 // Do nothing cases: 1) the view is already selected, or 2) the view 85 // is the a preview. 86 if (m_lastSelectedView == docView || 87 (docView && docView->isPreview())) 88 return; 89 90 if (docView) { 91 mainWin->getTabsBar()->selectTab(docView); 92 93 if (mainWin->getWorkspace()->activeView() != docView) 94 mainWin->getWorkspace()->setActiveView(docView); 95 } 96 97 current_editor = (docView ? docView->editor(): nullptr); 98 99 if (current_editor) 100 current_editor->requestFocus(); 101 102 mainWin->getPreviewEditor()->updateUsingEditor(current_editor); 103 mainWin->getTimeline()->updateUsingEditor(current_editor); 104 105 // Change the image-type of color bar. 106 ColorBar::instance()->setPixelFormat(app_get_current_pixel_format()); 107 108 // Restore the palette of the selected document. 109 app_refresh_screen(); 110 111 // Change the main frame title. 112 App::instance()->updateDisplayTitleBar(); 113 114 m_lastSelectedView = docView; 115 116 // TODO all the calls to functions like updateUsingEditor(), 117 // setPixelFormat(), app_refresh_screen(), updateDisplayTitleBar() 118 // Can be replaced with a ContextObserver listening to the 119 // onActiveSiteChange() event. 120 notifyActiveSiteChanged(); 121 } 122 onSetActiveDocument(Doc * document)123void UIContext::onSetActiveDocument(Doc* document) 124 { 125 bool notify = (lastSelectedDoc() != document); 126 app::Context::onSetActiveDocument(document); 127 128 DocView* docView = getFirstDocView(document); 129 if (docView) { // The view can be null if we are in --batch mode 130 setActiveView(docView); 131 notify = false; 132 } 133 134 if (notify) 135 notifyActiveSiteChanged(); 136 } 137 getFirstDocView(Doc * document) const138DocView* UIContext::getFirstDocView(Doc* document) const 139 { 140 Workspace* workspace = App::instance()->workspace(); 141 if (!workspace) // Workspace (main window) can be null if we are in --batch mode 142 return nullptr; 143 144 for (WorkspaceView* view : *workspace) { 145 if (DocView* docView = dynamic_cast<DocView*>(view)) { 146 if (docView->document() == document) { 147 return docView; 148 } 149 } 150 } 151 152 return nullptr; 153 } 154 getAllDocViews(Doc * document) const155DocViews UIContext::getAllDocViews(Doc* document) const 156 { 157 Workspace* workspace = App::instance()->workspace(); 158 DocViews docViews; 159 160 for (WorkspaceView* view : *workspace) { 161 if (DocView* docView = dynamic_cast<DocView*>(view)) { 162 if (docView->document() == document) { 163 docViews.push_back(docView); 164 } 165 } 166 } 167 168 return docViews; 169 } 170 getAllEditorsIncludingPreview(Doc * document) const171Editors UIContext::getAllEditorsIncludingPreview(Doc* document) const 172 { 173 std::vector<Editor*> editors; 174 for (DocView* docView : getAllDocViews(document)) { 175 if (docView->editor()) 176 editors.push_back(docView->editor()); 177 } 178 179 if (MainWindow* mainWin = App::instance()->mainWindow()) { 180 PreviewEditorWindow* previewWin = mainWin->getPreviewEditor(); 181 if (previewWin) { 182 Editor* miniEditor = previewWin->previewEditor(); 183 if (miniEditor && miniEditor->document() == document) 184 editors.push_back(miniEditor); 185 } 186 } 187 return editors; 188 } 189 activeEditor()190Editor* UIContext::activeEditor() 191 { 192 DocView* view = activeView(); 193 if (view) 194 return view->editor(); 195 else 196 return NULL; 197 } 198 onAddDocument(Doc * doc)199void UIContext::onAddDocument(Doc* doc) 200 { 201 app::Context::onAddDocument(doc); 202 203 // We don't create views in batch mode. 204 if (!App::instance()->isGui()) 205 return; 206 207 // Add a new view for this document 208 DocView* view = new DocView( 209 lastSelectedDoc(), 210 DocView::Normal, 211 App::instance()->mainWindow()->getPreviewEditor()); 212 213 // Add a tab with the new view for the document 214 App::instance()->workspace()->addView(view); 215 216 setActiveView(view); 217 view->editor()->setDefaultScroll(); 218 } 219 onRemoveDocument(Doc * doc)220void UIContext::onRemoveDocument(Doc* doc) 221 { 222 app::Context::onRemoveDocument(doc); 223 224 // We don't destroy views in batch mode. 225 if (isUIAvailable()) { 226 Workspace* workspace = App::instance()->workspace(); 227 228 for (DocView* docView : getAllDocViews(doc)) { 229 workspace->removeView(docView); 230 delete docView; 231 } 232 } 233 } 234 onGetActiveSite(Site * site) const235void UIContext::onGetActiveSite(Site* site) const 236 { 237 DocView* view = activeView(); 238 if (view) { 239 view->getSite(site); 240 241 if (site->sprite()) { 242 // Selected layers 243 Timeline* timeline = App::instance()->timeline(); 244 if (timeline && 245 timeline->range().enabled()) { 246 switch (timeline->range().type()) { 247 case DocRange::kCels: site->focus(Site::InCels); break; 248 case DocRange::kFrames: site->focus(Site::InFrames); break; 249 case DocRange::kLayers: site->focus(Site::InLayers); break; 250 } 251 site->selectedLayers(timeline->selectedLayers()); 252 site->selectedFrames(timeline->selectedFrames()); 253 } 254 else { 255 ColorBar* colorBar = ColorBar::instance(); 256 if (colorBar && 257 colorBar->getPaletteView()->getSelectedEntriesCount() > 0) { 258 site->focus(Site::InColorBar); 259 } 260 else { 261 site->focus(Site::InEditor); 262 } 263 } 264 } 265 } 266 else if (!isUIAvailable()) { 267 return app::Context::onGetActiveSite(site); 268 } 269 } 270 271 } // namespace app 272