aseprite/src/app/app_menus.cpp

308 lines
8.5 KiB
C++
Raw Normal View History

2015-02-12 12:16:25 -03:00
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
2007-09-18 23:57:02 +00:00
#ifdef HAVE_CONFIG_H
2007-09-18 23:57:02 +00:00
#include "config.h"
#endif
#include "app/app_menus.h"
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
#include "app/gui_xml.h"
#include "app/recent_files.h"
#include "app/resource_finder.h"
#include "app/tools/tool_box.h"
#include "app/ui/app_menuitem.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/util/filetoks.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/path.h"
#include "ui/ui.h"
2007-09-18 23:57:02 +00:00
#include "tinyxml.h"
#include <cstdio>
#include <cstring>
namespace app {
using namespace ui;
static void destroy_instance(AppMenus* instance)
2007-09-18 23:57:02 +00:00
{
delete instance;
2007-09-18 23:57:02 +00:00
}
// static
AppMenus* AppMenus::instance()
2007-09-18 23:57:02 +00:00
{
static AppMenus* instance = NULL;
if (!instance) {
instance = new AppMenus;
App::instance()->Exit.connect(Bind<void>(&destroy_instance, instance));
}
return instance;
2007-09-18 23:57:02 +00:00
}
AppMenus::AppMenus()
: m_recentListMenuitem(NULL)
2007-09-18 23:57:02 +00:00
{
m_recentFilesConn =
App::instance()->getRecentFiles()->Changed.connect(
Bind(&AppMenus::rebuildRecentList, this));
}
void AppMenus::reload()
{
XmlDocumentRef doc(GuiXml::instance()->doc());
TiXmlHandle handle(doc.get());
const char* path = GuiXml::instance()->filename();
2007-09-18 23:57:02 +00:00
////////////////////////////////////////
// Load menus
2015-08-28 20:48:49 -03:00
LOG(" - Loading menus from \"%s\"...\n", path);
m_rootMenu.reset(loadMenuById(handle, "main_menu"));
// Add a warning element because the user is not using the last well-known gui.xml file.
if (GuiXml::instance()->version() != VERSION)
m_rootMenu->insertChild(0, createInvalidVersionMenuitem());
2015-08-28 20:48:49 -03:00
LOG("Main menu loaded.\n");
m_tabPopupMenu.reset(loadMenuById(handle, "tab_popup"));
m_documentTabPopupMenu.reset(loadMenuById(handle, "document_tab_popup"));
m_layerPopupMenu.reset(loadMenuById(handle, "layer_popup"));
m_framePopupMenu.reset(loadMenuById(handle, "frame_popup"));
m_celPopupMenu.reset(loadMenuById(handle, "cel_popup"));
m_celMovementPopupMenu.reset(loadMenuById(handle, "cel_movement_popup"));
m_frameTagPopupMenu.reset(loadMenuById(handle, "frame_tag_popup"));
m_palettePopupMenu.reset(loadMenuById(handle, "palette_popup"));
m_inkPopupMenu.reset(loadMenuById(handle, "ink_popup"));
2007-09-23 20:13:58 +00:00
////////////////////////////////////////
// Load keyboard shortcuts for commands
2015-08-28 20:48:49 -03:00
LOG(" - Loading commands keyboard shortcuts from \"%s\"...\n", path);
TiXmlElement* xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard").ToElement();
KeyboardShortcuts::instance()->clear();
KeyboardShortcuts::instance()->importFile(xmlKey, KeySource::Original);
// Load user settings
{
ResourceFinder rf;
rf.includeUserDir("user.aseprite-keys");
std::string fn = rf.getFirstOrCreateDefault();
if (base::is_file(fn))
KeyboardShortcuts::instance()->importFile(fn, KeySource::UserDefined);
}
}
bool AppMenus::rebuildRecentList()
{
MenuItem* list_menuitem = m_recentListMenuitem;
MenuItem* menuitem;
// Update the recent file list menu item
if (list_menuitem) {
if (list_menuitem->hasSubmenuOpened())
return false;
Command* cmd_open_file = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
Menu* submenu = list_menuitem->getSubmenu();
if (submenu) {
list_menuitem->setSubmenu(NULL);
submenu->deferDelete();
}
// Build the menu of recent files
submenu = new Menu();
list_menuitem->setSubmenu(submenu);
RecentFiles::const_iterator it = App::instance()->getRecentFiles()->files_begin();
RecentFiles::const_iterator end = App::instance()->getRecentFiles()->files_end();
if (it != end) {
Params params;
for (; it != end; ++it) {
const char* filename = it->c_str();
params.set("filename", filename);
menuitem = new AppMenuItem(
base::get_file_name(filename).c_str(),
cmd_open_file,
params);
submenu->addChild(menuitem);
}
}
else {
menuitem = new AppMenuItem("Nothing", NULL, Params());
menuitem->setEnabled(false);
submenu->addChild(menuitem);
}
2007-09-18 23:57:02 +00:00
}
return true;
2007-09-18 23:57:02 +00:00
}
Menu* AppMenus::loadMenuById(TiXmlHandle& handle, const char* id)
2007-09-18 23:57:02 +00:00
{
ASSERT(id != NULL);
2015-08-28 20:48:49 -03:00
//LOG("loadMenuById(%s)\n", id);
// <gui><menus><menu>
TiXmlElement* xmlMenu = handle
.FirstChild("gui")
.FirstChild("menus")
.FirstChild("menu").ToElement();
while (xmlMenu) {
const char* menu_id = xmlMenu->Attribute("id");
if (menu_id && strcmp(menu_id, id) == 0)
return convertXmlelemToMenu(xmlMenu);
xmlMenu = xmlMenu->NextSiblingElement();
2007-09-18 23:57:02 +00:00
}
2008-01-06 19:30:17 +00:00
throw base::Exception("Error loading menu '%s'\nReinstall the application.", id);
2007-09-18 23:57:02 +00:00
}
Menu* AppMenus::convertXmlelemToMenu(TiXmlElement* elem)
2007-09-18 23:57:02 +00:00
{
Menu* menu = new Menu();
2007-09-23 20:13:58 +00:00
2015-08-28 20:48:49 -03:00
//LOG("convertXmlelemToMenu(%s, %s, %s)\n", elem->Value(), elem->Attribute("id"), elem->Attribute("text"));
2007-09-23 20:13:58 +00:00
TiXmlElement* child = elem->FirstChildElement();
while (child) {
Widget* menuitem = convertXmlelemToMenuitem(child);
if (menuitem)
menu->addChild(menuitem);
else
throw base::Exception("Error converting the element \"%s\" to a menu-item.\n",
static_cast<const char*>(child->Value()));
2007-09-23 20:13:58 +00:00
child = child->NextSiblingElement();
2007-09-18 23:57:02 +00:00
}
2007-09-23 20:13:58 +00:00
return menu;
2007-09-18 23:57:02 +00:00
}
Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem)
2007-09-18 23:57:02 +00:00
{
// is it a <separator>?
if (strcmp(elem->Value(), "separator") == 0)
2015-06-24 13:14:41 -03:00
return new MenuSeparator;
2007-09-18 23:57:02 +00:00
const char* command_name = elem->Attribute("command");
Command* command =
command_name ? CommandsModule::instance()->getCommandByName(command_name):
NULL;
// load params
Params params;
if (command) {
TiXmlElement* xmlParam = elem->FirstChildElement("param");
while (xmlParam) {
const char* param_name = xmlParam->Attribute("name");
const char* param_value = xmlParam->Attribute("value");
if (param_name && param_value)
params.set(param_name, param_value);
xmlParam = xmlParam->NextSiblingElement();
}
}
// Create the item
AppMenuItem* menuitem = new AppMenuItem(elem->Attribute("text"), command, params);
2007-09-18 23:57:02 +00:00
if (!menuitem)
return NULL;
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
/* has it a ID? */
const char* id = elem->Attribute("id");
if (id) {
2007-09-18 23:57:02 +00:00
/* recent list menu */
if (strcmp(id, "recent_list") == 0) {
m_recentListMenuitem = menuitem;
2007-09-18 23:57:02 +00:00
}
}
// Has it a sub-menu (<menu>)?
if (strcmp(elem->Value(), "menu") == 0) {
// Create the sub-menu
Menu* subMenu = convertXmlelemToMenu(elem);
if (!subMenu)
throw base::Exception("Error reading the sub-menu\n");
2007-09-18 23:57:02 +00:00
menuitem->setSubmenu(subMenu);
2007-09-23 20:13:58 +00:00
}
2007-09-18 23:57:02 +00:00
2007-09-23 20:13:58 +00:00
return menuitem;
}
2007-09-18 23:57:02 +00:00
Widget* AppMenus::createInvalidVersionMenuitem()
{
AppMenuItem* menuitem = new AppMenuItem("WARNING!");
Menu* subMenu = new Menu();
subMenu->addChild(new AppMenuItem(PACKAGE " is using a customized gui.xml (maybe from your HOME directory)."));
subMenu->addChild(new AppMenuItem("You should update your customized gui.xml file to the new version to get"));
subMenu->addChild(new AppMenuItem("the latest commands available."));
2015-06-24 13:14:41 -03:00
subMenu->addChild(new MenuSeparator);
subMenu->addChild(new AppMenuItem("You can bypass this validation adding the correct version"));
subMenu->addChild(new AppMenuItem("number in <gui version=\"" VERSION "\"> element."));
menuitem->setSubmenu(subMenu);
return menuitem;
}
void AppMenus::applyShortcutToMenuitemsWithCommand(Command* command, const Params& params, Key* key)
{
applyShortcutToMenuitemsWithCommand(m_rootMenu, command, params, key);
}
void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command, const Params& params, Key* key)
2007-09-23 20:13:58 +00:00
{
UI_FOREACH_WIDGET(menu->getChildren(), it) {
Widget* child = *it;
2007-09-18 23:57:02 +00:00
if (child->type() == kMenuItemWidget) {
AppMenuItem* menuitem = dynamic_cast<AppMenuItem*>(child);
if (!menuitem)
continue;
Command* mi_command = menuitem->getCommand();
const Params& mi_params = menuitem->getParams();
if ((mi_command) &&
(base::utf8_icmp(mi_command->id(), command->id()) == 0) &&
(mi_params == params)) {
// Set the keyboard shortcut to be shown in this menu-item
menuitem->setKey(key);
}
2007-09-18 23:57:02 +00:00
if (Menu* submenu = menuitem->getSubmenu())
applyShortcutToMenuitemsWithCommand(submenu, command, params, key);
2007-09-18 23:57:02 +00:00
}
}
}
} // namespace app