aseprite/src/app/app.cpp
David Capello f939ef5f02 Add keyboard shortcuts customization (close #253)
Changes:
* Add KeyboardShortcutsCommand and window
* Add SelectAccelerator window
* Replace modules/gui.cpp functions with app::KeyboardShortcuts and
  app::Key with the logic to load/save/handle keyboard shortcuts
* Change ui::Accelerator concept: now it represent just one keyboard
  shortcut, not a set of shortcuts
* Remove ui::Accelerator from ui::MenuItem, now the key is associated
  in app::AppMenuItem and it's a app::Key
* Add Command::onGetFriendlyName() to get a user friendly name of the
  command depending on its parameters
2014-10-29 11:58:03 -03:00

449 lines
11 KiB
C++

/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/app_options.h"
#include "app/check_update.h"
#include "app/color_utils.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
#include "app/data_recovery.h"
#include "app/document_exporter.h"
#include "app/document_location.h"
#include "app/file/file.h"
#include "app/file/file_formats_manager.h"
#include "app/file/palette_file.h"
#include "app/file_system.h"
#include "app/find_widget.h"
#include "app/gui_xml.h"
#include "app/ini_file.h"
#include "app/load_widget.h"
#include "app/log.h"
#include "app/modules.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/recent_files.h"
#include "app/resource_finder.h"
#include "app/send_crash.h"
#include "app/settings/settings.h"
#include "app/shell.h"
#include "app/tools/tool_box.h"
#include "app/ui/color_bar.h"
#include "app/ui/document_view.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/editor_view.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/tabs.h"
#include "app/ui/toolbar.h"
#include "app/ui_context.h"
#include "app/util/boundary.h"
#include "app/util/render.h"
#include "app/webserver.h"
#include "base/exception.h"
#include "base/fs.h"
#include "base/path.h"
#include "base/unique_ptr.h"
#include "doc/document_observer.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/palette.h"
#include "raster/sprite.h"
#include "scripting/engine.h"
#include "she/display.h"
#include "she/error.h"
#include "she/system.h"
#include "ui/intern.h"
#include "ui/ui.h"
#include <iostream>
#include <memory>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
namespace app {
using namespace ui;
class App::Modules {
public:
ConfigModule m_configModule;
LoggerModule m_loggerModule;
FileSystemModule m_file_system_module;
tools::ToolBox m_toolbox;
CommandsModule m_commands_modules;
UIContext m_ui_context;
RecentFiles m_recent_files;
app::DataRecovery m_recovery;
scripting::Engine m_scriptingEngine;
Modules(bool console, bool verbose)
: m_loggerModule(verbose)
, m_recovery(&m_ui_context) {
}
};
App* App::m_instance = NULL;
// Initializes the application loading the modules, setting the
// graphics mode, loading the configuration and resources, etc.
App::App(int argc, const char* argv[])
: m_modules(NULL)
, m_legacy(NULL)
, m_isGui(false)
, m_isShell(false)
, m_exporter(NULL)
{
ASSERT(m_instance == NULL);
m_instance = this;
AppOptions options(argc, argv);
m_modules = new Modules(!options.startUI(), options.verbose());
m_isGui = options.startUI();
m_isShell = options.startShell();
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
m_files = options.files();
if (options.hasExporterParams()) {
m_exporter.reset(new DocumentExporter);
m_exporter->setDataFilename(options.data());
m_exporter->setTextureFilename(options.sheet());
m_exporter->setScale(options.scale());
}
// Register well-known image file types.
FileFormatsManager::instance()->registerAllFormats();
// init editor cursor
Editor::editor_cursor_init();
// Load RenderEngine configuration
RenderEngine::loadConfig();
if (isPortable())
PRINTF("Running in portable mode\n");
// Default palette.
std::string palFile(!options.paletteFileName().empty() ?
options.paletteFileName():
std::string(get_config_string("GfxMode", "Palette", "")));
if (palFile.empty()) {
// Try to use a default pixel art palette.
ResourceFinder rf;
rf.includeDataDir("palettes/db32.gpl");
if (rf.findFirst())
palFile = rf.filename();
}
if (!palFile.empty()) {
PRINTF("Loading custom palette file: %s\n", palFile.c_str());
base::UniquePtr<Palette> pal(load_palette(palFile.c_str()));
if (pal.get() != NULL) {
set_default_palette(pal.get());
}
else {
PRINTF("Error loading custom palette file\n");
}
}
// Set system palette to the default one.
set_current_palette(NULL, true);
}
int App::run()
{
UIContext* context = UIContext::instance();
// Initialize GUI interface
if (isGui()) {
PRINTF("GUI mode\n");
// Setup the GUI cursor and redraw screen
ui::set_use_native_cursors(
context->settings()->experimental()->useNativeCursor());
jmouse_set_cursor(kArrowCursor);
ui::Manager::getDefault()->invalidate();
// Create the main window and show it.
m_mainWindow.reset(new MainWindow);
// Default status of the main window.
app_rebuild_documents_tabs();
app_default_statusbar_message();
// Default window title bar.
updateDisplayTitleBar();
m_mainWindow->openWindow();
// Redraw the whole screen.
ui::Manager::getDefault()->invalidate();
}
// Procress options
PRINTF("Processing options...\n");
{
Console console;
for (const std::string& filename : m_files) {
// Load the sprite
Document* document = load_document(context, filename.c_str());
if (!document) {
if (!isGui())
console.printf("Error loading file \"%s\"\n", filename.c_str());
}
else {
// Add the given file in the argument as a "recent file" only
// if we are running in GUI mode. If the program is executed
// in batch mode this is not desirable.
if (isGui())
getRecentFiles()->addRecentFile(filename.c_str());
// Add the document to the exporter.
if (m_exporter != NULL)
m_exporter->addDocument(document);
}
}
}
// Export
if (m_exporter != NULL) {
PRINTF("Exporting sheet...\n");
m_exporter->exportSheet();
m_exporter.reset(NULL);
}
// Run the GUI
if (isGui()) {
#ifdef ENABLE_UPDATER
// Launch the thread to check for updates.
app::CheckUpdateThreadLauncher checkUpdate;
checkUpdate.launch();
#endif
#ifdef ENABLE_WEBSERVER
// Launch the webserver.
app::WebServer webServer;
webServer.start();
#endif
app::SendCrash sendCrash;
sendCrash.search();
// Run the GUI main message loop
gui_run();
// Destroy all documents in the UIContext.
const doc::Documents& docs = m_modules->m_ui_context.documents();
while (!docs.empty()) {
doc::Document* doc = docs.back();
// First we close the document. In this way we receive recent
// notifications related to the document as an app::Document. If
// we delete the document directly, we destroy the app::Document
// too early, and then doc::~Document() call
// DocumentsObserver::onRemoveDocument(). In this way, observers
// could think that they have a fully created app::Document when
// in reality it's a doc::Document (in the middle of a
// destruction process).
//
// TODO: This problem is because we're extending doc::Document,
// in the future, we should remove app::Document.
doc->close();
delete doc;
}
// Destroy the window.
m_mainWindow.reset(NULL);
}
// Start shell to execute scripts.
else if (m_isShell) {
m_systemConsole.prepareShell();
if (m_modules->m_scriptingEngine.supportEval()) {
Shell shell;
shell.run(m_modules->m_scriptingEngine);
}
else {
std::cerr << "Your version of " PACKAGE " wasn't compiled with shell support.\n";
}
}
return 0;
}
// Finishes the Aseprite application.
App::~App()
{
try {
ASSERT(m_instance == this);
// Remove Aseprite handlers
PRINTF("ASE: Uninstalling\n");
// Delete file formats.
FileFormatsManager::destroyInstance();
// Fire App Exit signal.
App::instance()->Exit();
// Finalize modules, configuration and core.
Editor::editor_cursor_exit();
boundary_exit();
delete m_legacy;
delete m_modules;
// Destroy the loaded gui.xml data.
delete KeyboardShortcuts::instance();
delete GuiXml::instance();
m_instance = NULL;
}
catch (...) {
she::error_message("Error closing ASE.\n(uncaught exception)");
// no re-throw
}
}
bool App::isPortable()
{
static bool* is_portable = NULL;
if (!is_portable) {
is_portable =
new bool(
base::is_file(base::join_path(
base::get_file_path(base::get_app_path()),
"aseprite.ini")));
}
return *is_portable;
}
tools::ToolBox* App::getToolBox() const
{
ASSERT(m_modules != NULL);
return &m_modules->m_toolbox;
}
RecentFiles* App::getRecentFiles() const
{
ASSERT(m_modules != NULL);
return &m_modules->m_recent_files;
}
void App::showNotification(INotificationDelegate* del)
{
m_mainWindow->showNotification(del);
}
void App::updateDisplayTitleBar()
{
std::string defaultTitle = PACKAGE " v" VERSION;
std::string title;
DocumentView* docView = UIContext::instance()->activeView();
if (docView) {
// Prepend the document's filename.
title += docView->getDocument()->name();
title += " - ";
}
title += defaultTitle;
she::instance()->defaultDisplay()->setTitleBar(title);
}
// Updates palette and redraw the screen.
void app_refresh_screen()
{
Context* context = UIContext::instance();
ASSERT(context != NULL);
DocumentLocation location = context->activeLocation();
if (Palette* pal = location.palette())
set_current_palette(pal, false);
else
set_current_palette(NULL, false);
// Invalidate the whole screen.
ui::Manager::getDefault()->invalidate();
}
void app_rebuild_documents_tabs()
{
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
}
PixelFormat app_get_current_pixel_format()
{
Context* context = UIContext::instance();
ASSERT(context != NULL);
Document* document = context->activeDocument();
if (document != NULL)
return document->sprite()->pixelFormat();
else
return IMAGE_RGB;
}
void app_default_statusbar_message()
{
StatusBar::instance()
->setStatusText(250, "%s %s | %s", PACKAGE, VERSION, COPYRIGHT);
}
int app_get_color_to_clear_layer(Layer* layer)
{
ASSERT(layer != NULL);
app::Color color;
// The `Background' is erased with the `Background Color'
if (layer->isBackground()) {
if (ColorBar::instance())
color = ColorBar::instance()->getBgColor();
else
color = app::Color::fromRgb(0, 0, 0); // TODO get background color color from doc::Settings
}
else // All transparent layers are cleared with the mask color
color = app::Color::fromMask();
return color_utils::color_for_layer(color, layer);
}
} // namespace app