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
This commit is contained in:
David Capello 2014-10-29 11:58:03 -03:00
parent ec99866a23
commit f939ef5f02
54 changed files with 2128 additions and 838 deletions

View File

@ -2,7 +2,7 @@
<!-- ASE menus, tools and keyboard shortcuts -->
<gui version="1.0.6-dev">
<!-- Keyboard shortcuts -->
<keyboard>
<keyboard version="1">
<!-- Keyboard shortcuts for commands (menu options) -->
<commands>
@ -51,6 +51,7 @@
<key command="ConfigureTools" shortcut="C" />
<key command="Options" shortcut="Ctrl+K" mac="Cmd+," />
<key command="Options" shortcut="Ctrl+Shift+O" mac="Cmd+Shift+O" /> <!-- TODO remove this shortcut in v1.1 -->
<key command="KeyboardShortcuts" shortcut="Ctrl+Alt+Shift+K" mac="Cmd+Alt+Shift+K" />
<!-- Sprite -->
<key command="SpriteProperties" shortcut="Ctrl+P" mac="Cmd+P" />
<!-- Layer -->
@ -345,8 +346,8 @@
<key tool="hand" shortcut="Space" />
</quicktools>
<!-- Special keyboard shortcuts for the sprite editor -->
<spriteeditor>
<!-- Special keyboard shortcuts for specific actions -->
<actions>
<!-- When you drag-and-drop the selection, pressing this
keyboard shortcut you can copy instead of move -->
<key action="CopySelection" shortcut="Ctrl" />
@ -370,7 +371,7 @@
<!-- Modifiers for selection tool -->
<key action="AddSelection" shortcut="Shift" />
<key action="SubtractSelection" shortcut="Alt" />
</spriteeditor>
</actions>
</keyboard>
@ -436,6 +437,7 @@
</menu>
<separator />
<item command="ConfigureTools" text="Tool&amp;s..." />
<item command="KeyboardShortcuts" text="&amp;Keyboard Shortcuts..." />
<item command="Options" text="Pre&amp;ferences..." />
</menu>
<menu text="&amp;Sprite">

View File

@ -0,0 +1,35 @@
<!-- Aseprite -->
<!-- Copyright (C) 2001-2014 by David Capello -->
<gui>
<window id="keyboard_shortcuts" text="Keyboard Shortcuts">
<hbox>
<vbox expansive="true">
<hbox>
<label text="Section:" />
<combobox id="section" expansive="true" />
</hbox>
<view id="menus_view" expansive="true">
<listbox id="menus" />
</view>
<view id="commands_view" expansive="true">
<listbox id="commands" />
</view>
<view id="tools_view" expansive="true">
<listbox id="tools" />
</view>
<view id="actions_view" expansive="true">
<listbox id="actions" />
</view>
</vbox>
<vbox>
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" width="60" />
<button text="&amp;Cancel" closewindow="true" />
<separator horizontal="true" />
<button text="&amp;Import" id="import_button" />
<button text="&amp;Export" id="export_button" />
<separator horizontal="true" />
<button text="&amp;Reset" id="reset_button" />
</vbox>
</hbox>
</window>
</gui>

View File

@ -0,0 +1,36 @@
<!-- Aseprite -->
<!-- Copyright (C) 2001-2014 by David Capello -->
<gui>
<window id="select_accelerator" text="Keyboard Shortcut">
<vbox expansive="true">
<grid columns="3">
<label text="Key:" />
<hbox id="key_placeholder" expansive="true" />
<button text="Clear" id="clear_button" width="60" />
<label text="Modifiers:" />
<hbox cell_hspan="2">
<check text="Ctrl" id="ctrl" />
<check text="Cmd" id="cmd" />
<check text="Alt" id="alt" />
<check text="Shift" id="shift" />
<check text="Space" id="space" />
</hbox>
<label text="" id="assigned_to" cell_hspan="3" />
</grid>
<separator horizontal="true" />
<box horizontal="true">
<boxfiller />
<box horizontal="true" homogeneous="true">
<button text="OK" id="ok_button" magnet="true" width="60" />
<button text="Cancel" id="cancel_button" />
<button text="Delete" id="delete_button" />
</box>
</box>
</vbox>
</window>
</gui>

View File

@ -44,6 +44,7 @@ add_library(app-lib
commands/cmd_grid.cpp
commands/cmd_import_sprite_sheet.cpp
commands/cmd_invert_mask.cpp
commands/cmd_keyboard_shortcuts.cpp
commands/cmd_launch.cpp
commands/cmd_layer_from_background.cpp
commands/cmd_layer_properties.cpp
@ -184,6 +185,7 @@ add_library(app-lib
ui/file_list.cpp
ui/file_selector.cpp
ui/hex_color_entry.cpp
ui/keyboard_shortcuts.cpp
ui/main_menu_bar.cpp
ui/main_window.cpp
ui/mini_editor.cpp
@ -193,6 +195,7 @@ add_library(app-lib
ui/palettes_listbox.cpp
ui/popup_window_pin.cpp
ui/resources_listbox.cpp
ui/select_accelerator.cpp
ui/skin/button_icon_impl.cpp
ui/skin/skin_part.cpp
ui/skin/skin_property.cpp

View File

@ -54,6 +54,7 @@
#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"
@ -326,7 +327,8 @@ App::~App()
delete m_legacy;
delete m_modules;
// Destroy the loaded gui.xml file.
// Destroy the loaded gui.xml data.
delete KeyboardShortcuts::instance();
delete GuiXml::instance();
m_instance = NULL;

View File

@ -29,12 +29,6 @@
#include <string>
#include <vector>
namespace ui {
class MenuBar;
class Widget;
class Window;
}
namespace raster {
class Layer;
}

View File

@ -28,13 +28,15 @@
#include "app/commands/params.h"
#include "app/console.h"
#include "app/gui_xml.h"
#include "app/modules/gui.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 "ui/ui.h"
#include "tinyxml.h"
@ -74,9 +76,8 @@ void AppMenus::reload()
TiXmlHandle handle(doc);
const char* path = GuiXml::instance()->filename();
/**************************************************/
/* load menus */
/**************************************************/
////////////////////////////////////////
// Load menus
PRINTF(" - Loading menus from \"%s\"...\n", path);
@ -94,127 +95,25 @@ void AppMenus::reload()
m_celPopupMenu.reset(loadMenuById(handle, "cel_popup"));
m_celMovementPopupMenu.reset(loadMenuById(handle, "cel_movement_popup"));
/**************************************************/
/* load keyboard shortcuts for commands */
/**************************************************/
////////////////////////////////////////
// Load keyboard shortcuts for commands
PRINTF(" - Loading commands keyboard shortcuts from \"%s\"...\n", path);
// <gui><keyboard><commands><key>
TiXmlElement* xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard")
.FirstChild("commands")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* command_name = xmlKey->Attribute("command");
const char* command_key = getShortcut(xmlKey);
.FirstChild("keyboard").ToElement();
if (command_name && command_key) {
Command* command = CommandsModule::instance()->getCommandByName(command_name);
if (command) {
// Read context
KeyContext keycontext = KeyContext::Any;
const char* keycontextstr = xmlKey->Attribute("context");
if (keycontextstr) {
if (strcmp(keycontextstr, "Selection") == 0)
keycontext = KeyContext::Selection;
else if (strcmp(keycontextstr, "Normal") == 0)
keycontext = KeyContext::Normal;
}
KeyboardShortcuts::instance()->clear();
KeyboardShortcuts::instance()->importFile(xmlKey, KeySource::Original);
// Read params
Params params;
TiXmlElement* xmlParam = xmlKey->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();
}
bool first_shortcut =
(get_accel_to_execute_command(command_name, &params) == NULL);
PRINTF(" - Shortcut for command `%s' <%s>\n", command_name, command_key);
// add the keyboard shortcut to the command
Accelerator* accel =
add_keyboard_shortcut_to_execute_command(command_key, command_name, &params, keycontext);
// add the shortcut to the menuitems with this
// command (this is only visual, the "manager_msg_proc"
// is the only one that process keyboard shortcuts)
if (first_shortcut)
applyShortcutToMenuitemsWithCommand(m_rootMenu, command, &params, accel);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load keyboard shortcuts for tools
PRINTF(" - Loading tools keyboard shortcuts from \"%s\"...\n", path);
// <gui><keyboard><tools><key>
xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard")
.FirstChild("tools")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_id = xmlKey->Attribute("tool");
const char* tool_key = getShortcut(xmlKey);
if (tool_id && tool_key) {
tools::Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
PRINTF(" - Shortcut for tool `%s': <%s>\n", tool_id, tool_key);
add_keyboard_shortcut_to_change_tool(tool_key, tool);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load keyboard shortcuts for quicktools
// <gui><keyboard><quicktools><key>
xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard")
.FirstChild("quicktools")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_id = xmlKey->Attribute("tool");
const char* tool_key = getShortcut(xmlKey);
if (tool_id && tool_key) {
tools::Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
PRINTF(" - Shortcut for quicktool `%s': <%s>\n", tool_id, tool_key);
add_keyboard_shortcut_to_quicktool(tool_key, tool);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load special keyboard shortcuts for sprite editor customization
// <gui><keyboard><spriteeditor>
xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard")
.FirstChild("spriteeditor")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_action = xmlKey->Attribute("action");
const char* tool_key = getShortcut(xmlKey);
if (tool_action && tool_key) {
PRINTF(" - Shortcut for sprite editor `%s': <%s>\n", tool_action, tool_key);
add_keyboard_shortcut_to_spriteeditor(tool_key, tool_action);
}
xmlKey = xmlKey->NextSiblingElement();
// 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);
}
}
@ -377,7 +276,12 @@ Widget* AppMenus::createInvalidVersionMenuitem()
return menuitem;
}
void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command *command, Params* params, Accelerator* accel)
void AppMenus::applyShortcutToMenuitemsWithCommand(Command* command, Params* params, Key* key)
{
applyShortcutToMenuitemsWithCommand(m_rootMenu, command, params, key);
}
void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command, Params* params, Key* key)
{
UI_FOREACH_WIDGET(menu->getChildren(), it) {
Widget* child = *it;
@ -393,32 +297,14 @@ void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command *command,
ustricmp(mi_command->short_name(), command->short_name()) == 0 &&
((mi_params && *mi_params == *params) ||
(Params() == *params))) {
// Set the accelerator to be shown in this menu-item
menuitem->setAccel(new Accelerator(*accel));
// Set the keyboard shortcut to be shown in this menu-item
menuitem->setKey(key);
}
if (Menu* submenu = menuitem->getSubmenu())
applyShortcutToMenuitemsWithCommand(submenu, command, params, accel);
applyShortcutToMenuitemsWithCommand(submenu, command, params, key);
}
}
}
const char* AppMenus::getShortcut(TiXmlElement* elem)
{
const char* shortcut = NULL;
#if defined ALLEGRO_WINDOWS
if (!shortcut) shortcut = elem->Attribute("win");
#elif defined ALLEGRO_MACOSX
if (!shortcut) shortcut = elem->Attribute("mac");
#elif defined ALLEGRO_UNIX
if (!shortcut) shortcut = elem->Attribute("linux");
#endif
if (!shortcut)
shortcut = elem->Attribute("shortcut");
return shortcut;
}
} // namespace app

View File

@ -28,11 +28,8 @@
class TiXmlElement;
class TiXmlHandle;
namespace ui {
class Accelerator;
}
namespace app {
class Key;
class Command;
class Params;
@ -59,13 +56,14 @@ namespace app {
Menu* getCelPopupMenu() { return m_celPopupMenu; }
Menu* getCelMovementPopupMenu() { return m_celMovementPopupMenu; }
void applyShortcutToMenuitemsWithCommand(Command* command, Params* params, Key* key);
private:
Menu* loadMenuById(TiXmlHandle& handle, const char *id);
Menu* convertXmlelemToMenu(TiXmlElement* elem);
Widget* convertXmlelemToMenuitem(TiXmlElement* elem);
Widget* createInvalidVersionMenuitem();
void applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command, Params* params, Accelerator* accel);
const char* getShortcut(TiXmlElement* elem);
void applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command, Params* params, Key* key);
base::UniquePtr<Menu> m_rootMenu;
MenuItem* m_recentListMenuitem;

View File

@ -25,7 +25,7 @@
#include "app/find_widget.h"
#include "app/ini_file.h"
#include "app/load_widget.h"
#include "app/modules/gui.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "ui/ui.h"
@ -74,19 +74,14 @@ void AdvancedModeCommand::onExecute(Context* context)
if (oldMode == MainWindow::NormalMode &&
get_config_bool("AdvancedMode", "Warning", true)) {
Accelerator* accel = get_accel_to_execute_command(short_name());
if (accel != NULL) {
char warning[1024];
char buf[1024];
Key* key = KeyboardShortcuts::instance()->command(short_name());
if (!key->accels().empty()) {
base::UniquePtr<Window> window(app::load_widget<Window>("advanced_mode.xml", "advanced_mode_warning"));
Widget* warning_label = app::find_widget<Widget>(window, "warning_label");
Widget* donot_show = app::find_widget<Widget>(window, "donot_show");
strcpy(warning, "You can back pressing the \"%s\" key.");
std::sprintf(buf, warning, accel->toString().c_str());
warning_label->setText(buf);
warning_label->setTextf("You can go back pressing \"%s\" key.",
key->accels().front().toString().c_str());
window->openWindowInForeground();

View File

@ -48,7 +48,7 @@ private:
CancelCommand::CancelCommand()
: Command("Cancel",
"Cancel",
"Cancel Current Operation",
CmdUIOnlyFlag)
, m_type(NoOp)
{

View File

@ -48,6 +48,7 @@ public:
protected:
void onLoadParams(Params* params);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
};
ChangeBrushCommand::ChangeBrushCommand()
@ -96,6 +97,30 @@ void ChangeBrushCommand::onExecute(Context* context)
}
}
std::string ChangeBrushCommand::onGetFriendlyName() const
{
std::string text = "Brush";
switch (m_change) {
case None:
break;
case IncrementSize:
text += ": Increment Size";
break;
case DecrementSize:
text += ": Decrement Size";
break;
case IncrementAngle:
text += ": Increment Angle";
break;
case DecrementAngle:
text += ": Decrement Angle";
break;
}
return text;
}
Command* CommandFactory::createChangeBrushCommand()
{
return new ChangeBrushCommand;

View File

@ -50,6 +50,7 @@ public:
protected:
void onLoadParams(Params* params);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
};
ChangeColorCommand::ChangeColorCommand()
@ -108,6 +109,29 @@ void ChangeColorCommand::onExecute(Context* context)
colorbar->setFgColor(color);
}
std::string ChangeColorCommand::onGetFriendlyName() const
{
std::string text = "Color";
switch (m_change) {
case None:
return text;
case IncrementIndex:
text += ": Increment";
break;
case DecrementIndex:
text += ": Decrement";
break;
}
if (m_background)
text += " Background Index";
else
text += " Foreground Index";
return text;
}
Command* CommandFactory::createChangeColorCommand()
{
return new ChangeColorCommand;

View File

@ -49,7 +49,7 @@ protected:
DeveloperConsoleCommand::DeveloperConsoleCommand()
: Command("DeveloperConsole",
"DeveloperConsole",
"Developer Console",
CmdUIOnlyFlag)
{
m_devConsole = NULL;

View File

@ -188,6 +188,23 @@ void FlipCommand::onExecute(Context* context)
update_screen_for_document(document);
}
std::string FlipCommand::onGetFriendlyName() const
{
std::string text = "Flip";
if (m_flipMask)
text += " Selection";
else
text += " Canvas";
if (m_flipType == raster::algorithm::FlipHorizontal)
text += " Horizontal";
else
text += " Vertical";
return text;
}
Command* CommandFactory::createFlipCommand()
{
return new FlipCommand;

View File

@ -36,6 +36,7 @@ namespace app {
void onLoadParams(Params* params);
bool onEnabled(Context* context);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
private:
bool m_flipMask;

View File

@ -58,7 +58,7 @@ class GotoFirstFrameCommand : public GotoCommand {
public:
GotoFirstFrameCommand()
: GotoCommand("GotoFirstFrame",
"Goto First Frame") { }
"Go to First Frame") { }
Command* clone() const override { return new GotoFirstFrameCommand(*this); }
protected:
@ -71,7 +71,7 @@ class GotoPreviousFrameCommand : public GotoCommand {
public:
GotoPreviousFrameCommand()
: GotoCommand("GotoPreviousFrame",
"Goto Previous Frame") { }
"Go to Previous Frame") { }
Command* clone() const override { return new GotoPreviousFrameCommand(*this); }
protected:
@ -88,7 +88,7 @@ protected:
class GotoNextFrameCommand : public GotoCommand {
public:
GotoNextFrameCommand() : GotoCommand("GotoNextFrame",
"Goto Next Frame") { }
"Go to Next Frame") { }
Command* clone() const override { return new GotoNextFrameCommand(*this); }
protected:
@ -104,7 +104,7 @@ protected:
class GotoLastFrameCommand : public GotoCommand {
public:
GotoLastFrameCommand() : GotoCommand("GotoLastFrame",
"Goto Last Frame") { }
"Go to Last Frame") { }
Command* clone() const override { return new GotoLastFrameCommand(*this); }
protected:
@ -116,7 +116,7 @@ protected:
class GotoFrameCommand : public GotoCommand {
public:
GotoFrameCommand() : GotoCommand("GotoFrame",
"Goto Frame")
"Go to Frame")
, m_frame(0) { }
Command* clone() const override { return new GotoFrameCommand(*this); }

View File

@ -59,7 +59,7 @@ protected:
GotoPreviousLayerCommand::GotoPreviousLayerCommand()
: GotoCommand("GotoPreviousLayer",
"Goto Previous Layer",
"Go to Previous Layer",
CmdUIOnlyFlag)
{
}
@ -101,7 +101,7 @@ protected:
GotoNextLayerCommand::GotoNextLayerCommand()
: GotoCommand("GotoNextLayer",
"Goto Next Layer",
"Go to Next Layer",
CmdUIOnlyFlag)
{
}

View File

@ -39,7 +39,7 @@ protected:
GotoNextTabCommand::GotoNextTabCommand()
: Command("GotoNextTab",
"Goto Next Tab",
"Go to Next Tab",
CmdUIOnlyFlag)
{
}
@ -60,7 +60,7 @@ protected:
GotoPreviousTabCommand::GotoPreviousTabCommand()
: Command("GotoPreviousTab",
"Goto Previous tab",
"Go to Previous tab",
CmdRecordableFlag)
{
}

View File

@ -0,0 +1,481 @@
/* Aseprite
* Copyright (C) 2001-2014 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_menus.h"
#include "app/commands/command.h"
#include "app/context.h"
#include "app/file_selector.h"
#include "app/modules/gui.h"
#include "app/resource_finder.h"
#include "app/tools/tool.h"
#include "app/ui/app_menuitem.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/select_accelerator.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/path.h"
#include "ui/graphics.h"
#include "ui/listitem.h"
#include "ui/paint_event.h"
#include "ui/preferred_size_event.h"
#include "ui/resize_event.h"
#include "generated_keyboard_shortcuts.h"
#define KEYBOARD_FILENAME_EXTENSION "aseprite-keys"
namespace app {
using namespace ui;
using namespace skin;
static int g_sep = 0;
class KeyItem : public ListItem {
public:
KeyItem(const std::string& text, Key* key, AppMenuItem* menuitem, int level)
: ListItem(text)
, m_key(key)
, m_keyOrig(key ? new Key(*key): NULL)
, m_menuitem(menuitem)
, m_level(level)
, m_changeButton(NULL)
, m_addButton(NULL)
, m_hotAccel(-1) {
this->border_width.t = this->border_width.b = 0;
}
~KeyItem() {
destroyButtons();
}
void restoreKeys() {
if (m_key) {
if (m_keyOrig)
*m_key = *m_keyOrig;
else if (m_menuitem)
m_menuitem->setKey(NULL);
}
}
private:
void onChangeAccel() {
Accelerator origAccel = m_key->accels()[m_hotAccel];
SelectAccelerator window(origAccel, true);
window.openWindowInForeground();
if (window.isModified()) {
m_key->disableAccel(origAccel);
m_key->add(window.accel(), KeySource::UserDefined);
}
else if (window.isDeleted()) {
m_key->disableAccel(origAccel);
}
getRoot()->layout();
}
void onAddAccel() {
ui::Accelerator accel;
SelectAccelerator window(accel, false);
window.openWindowInForeground();
if (window.isModified()) {
if (!m_key) {
ASSERT(m_menuitem);
if (!m_menuitem)
return;
m_key = app::KeyboardShortcuts::instance()->command(
m_menuitem->getCommand()->short_name(),
m_menuitem->getParams());
m_menuitem->setKey(m_key);
}
m_key->add(window.accel(), KeySource::UserDefined);
}
getRoot()->layout();
}
void onPreferredSize(PreferredSizeEvent& ev) override {
gfx::Size size = getTextSize();
size.w = this->border_width.l + size.w + this->border_width.r;
size.h = this->border_width.t + size.h + this->border_width.b
+ 4*jguiscale();
if (m_key && !m_key->accels().empty()) {
size_t combos = m_key->accels().size();
if (combos > 1)
size.h *= combos;
}
ev.setPreferredSize(size);
}
void onPaint(PaintEvent& ev) override {
Graphics* g = ev.getGraphics();
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
gfx::Rect bounds = getClientBounds();
gfx::Color fg, bg;
if (isSelected()) {
fg = theme->getColor(ThemeColor::ListItemSelectedText);
bg = theme->getColor(ThemeColor::ListItemSelectedFace);
}
else {
fg = theme->getColor(ThemeColor::ListItemNormalText);
bg = theme->getColor(ThemeColor::ListItemNormalFace);
}
g->fillRect(bg, bounds);
bounds.shrink(getBorder());
g->drawUIString(getText(), fg, bg,
gfx::Point(
bounds.x + m_level*16 * jguiscale(),
bounds.y + 2*jguiscale()));
if (m_key && !m_key->accels().empty()) {
std::string buf;
int y = bounds.y;
int dh = getTextSize().h + 4*jguiscale();
int i = 0;
for (const Accelerator& accel : m_key->accels()) {
if (i != m_hotAccel || !m_changeButton) {
g->drawString(accel.toString(), fg, bg,
gfx::Point(bounds.x + g_sep, y + 2*jguiscale()));
}
y += dh;
++i;
}
}
}
void onResize(ResizeEvent& ev) override {
ListItem::onResize(ev);
destroyButtons();
}
bool onProcessMessage(Message* msg) override {
switch (msg->type()) {
case kMouseLeaveMessage: {
m_hotAccel = -1;
destroyButtons();
invalidate();
break;
}
case kMouseMoveMessage: {
gfx::Rect bounds = getBounds();
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
int hotAccel = -1;
const Accelerators* accels = (m_key ? &m_key->accels() : NULL);
int y = bounds.y;
int dh = getTextSize().h + 4*jguiscale();
int maxi = (accels && accels->size() > 1 ? accels->size(): 1);
for (int i=0; i<maxi; ++i, y += dh) {
int w = Graphics::measureUIStringLength(
(accels && i < (int)accels->size() ? (*accels)[i].toString().c_str(): ""),
getFont());
gfx::Rect itemBounds(bounds.x + g_sep, y, w, dh);
if (mouseMsg->position().y >= bounds.y &&
mouseMsg->position().y < bounds.y+bounds.h) {
itemBounds = itemBounds.enlarge(
gfx::Border(
4*jguiscale(), 0,
6*jguiscale(), 1*jguiscale()));
if (accels && i < (int)accels->size() &&
itemBounds.contains(mouseMsg->position())) {
hotAccel = i;
if (!m_changeButton) {
m_changeButton = new Button("");
m_changeButton->Click.connect(Bind<void>(&KeyItem::onChangeAccel, this));
setup_mini_look(m_changeButton);
addChild(m_changeButton);
}
m_changeButton->setBgColor(gfx::ColorNone);
m_changeButton->setBounds(itemBounds);
m_changeButton->setText((*accels)[i].toString());
invalidate();
}
if (i == 0 && (!m_menuitem || m_menuitem->getCommand())) {
if (!m_addButton) {
m_addButton = new Button("");
m_addButton->Click.connect(Bind<void>(&KeyItem::onAddAccel, this));
setup_mini_look(m_addButton);
addChild(m_addButton);
}
itemBounds.w = 8*jguiscale() + Graphics::measureUIStringLength("Add", getFont());
itemBounds.x -= itemBounds.w + 2*jguiscale();
m_addButton->setBgColor(gfx::ColorNone);
m_addButton->setBounds(itemBounds);
m_addButton->setText("Add");
invalidate();
}
}
}
if (m_hotAccel != hotAccel) {
if (hotAccel == -1)
destroyButtons();
m_hotAccel = hotAccel;
invalidate();
}
break;
}
}
return ListItem::onProcessMessage(msg);
}
void destroyButtons() {
delete m_changeButton;
delete m_addButton;
m_changeButton = NULL;
m_addButton = NULL;
}
Key* m_key;
Key* m_keyOrig;
AppMenuItem* m_menuitem;
int m_level;
ui::Accelerators m_newAccels;
ui::Button* m_changeButton;
ui::Button* m_addButton;
int m_hotAccel;
};
class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
public:
KeyboardShortcutsWindow() {
setAutoRemap(false);
section()->addItem("Menus");
section()->addItem("Commands");
section()->addItem("Tools");
section()->addItem("Action Modifiers");
section()->Change.connect(Bind<void>(&KeyboardShortcutsWindow::onSectionChange, this));
importButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onImport, this));
exportButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onExport, this));
resetButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onReset, this));
fillAllLists();
}
void restoreKeys() {
for (KeyItem* keyItem : m_allKeyItems) {
keyItem->restoreKeys();
}
}
private:
void deleteAllKeyItems() {
while (menus()->getLastChild())
menus()->removeChild(menus()->getLastChild());
while (commands()->getLastChild())
commands()->removeChild(commands()->getLastChild());
while (tools()->getLastChild())
tools()->removeChild(tools()->getLastChild());
while (actions()->getLastChild())
actions()->removeChild(actions()->getLastChild());
for (KeyItem* keyItem : m_allKeyItems) {
delete keyItem;
}
m_allKeyItems.clear();
}
void fillAllLists() {
deleteAllKeyItems();
// Load keyboard shortcuts
fillList(this->menus(), AppMenus::instance()->getRootMenu(), 0);
for (Key* key : *app::KeyboardShortcuts::instance()) {
std::string text = key->triggerString();
if (key->keycontext() == KeyContext::Selection)
text += " (w/selection)";
KeyItem* keyItem = new KeyItem(text, key, NULL, 0);
ListBox* listBox = NULL;
switch (key->type()) {
case KeyType::Command:
listBox = this->commands();
break;
case KeyType::Tool:
case KeyType::Quicktool: {
listBox = this->tools();
break;
}
case KeyType::Action:
listBox = this->actions();
break;
}
ASSERT(listBox);
if (listBox) {
m_allKeyItems.push_back(keyItem);
listBox->addChild(keyItem);
}
}
this->commands()->sortItems();
this->tools()->sortItems();
this->actions()->sortItems();
onSectionChange();
}
void onSectionChange() {
int section = this->section()->getSelectedItemIndex();
menusView()->setVisible(section == 0);
commandsView()->setVisible(section == 1);
toolsView()->setVisible(section == 2);
actionsView()->setVisible(section == 3);
layout();
}
void onImport() {
std::string filename = app::show_file_selector("Import Keyboard Shortcuts", "",
KEYBOARD_FILENAME_EXTENSION);
if (filename.empty())
return;
app::KeyboardShortcuts::instance()->importFile(filename.c_str(), KeySource::UserDefined);
fillAllLists();
layout();
}
void onExport() {
again:
std::string filename = app::show_file_selector("Export Keyboard Shortcuts", "",
KEYBOARD_FILENAME_EXTENSION);
if (!filename.empty()) {
if (base::is_file(filename)) {
int ret = Alert::show("Warning<<File exists, overwrite it?<<%s||&Yes||&No||&Cancel",
base::get_file_name(filename).c_str());
if (ret == 2)
goto again;
else if (ret != 1)
return;
}
app::KeyboardShortcuts::instance()->exportFile(filename.c_str());
}
}
void onReset() {
if (Alert::show("Warning"
"<<Do you want to restore all keyboard shortcuts"
"<<to their original default settings?"
"||&Yes||&No") == 1) {
app::KeyboardShortcuts::instance()->reset();
layout();
}
}
void fillList(ListBox* listbox, Menu* menu, int level) {
for (Widget* child : menu->getChildren()) {
if (AppMenuItem* menuItem = dynamic_cast<AppMenuItem*>(child)) {
if (menuItem == AppMenus::instance()->getRecentListMenuitem())
continue;
KeyItem* keyItem = new KeyItem(
menuItem->getText().c_str(),
menuItem->getKey(), menuItem, level);
listbox->addChild(keyItem);
if (menuItem->hasSubmenu())
fillList(listbox, menuItem->getSubmenu(), level+1);
}
}
}
std::vector<KeyItem*> m_allKeyItems;
};
class KeyboardShortcutsCommand : public Command {
public:
KeyboardShortcutsCommand();
Command* clone() const override { return new KeyboardShortcutsCommand(*this); }
protected:
void onExecute(Context* context);
};
KeyboardShortcutsCommand::KeyboardShortcutsCommand()
: Command("KeyboardShortcuts",
"Keyboard Shortcuts",
CmdUIOnlyFlag)
{
}
void KeyboardShortcutsCommand::onExecute(Context* context)
{
KeyboardShortcutsWindow window;
window.setBounds(gfx::Rect(0, 0, ui::display_w()*3/4, ui::display_h()*3/4));
g_sep = window.getBounds().w / 2;
window.centerWindow();
window.setVisible(true);
window.openWindowInForeground();
if (window.getKiller() == window.ok()) {
// Save keyboard shortcuts in configuration file
{
ResourceFinder rf;
rf.includeUserDir("user." KEYBOARD_FILENAME_EXTENSION);
std::string fn = rf.getFirstOrCreateDefault();
KeyboardShortcuts::instance()->exportFile(fn);
}
}
else {
window.restoreKeys();
}
}
Command* CommandFactory::createKeyboardShortcutsCommand()
{
return new KeyboardShortcutsCommand;
}
} // namespace app

View File

@ -32,6 +32,7 @@
#include "app/ui/editor/editor.h"
#include "app/undo_transaction.h"
#include "app/undoers/set_mask_position.h"
#include "base/convert_to.h"
#include "raster/mask.h"
#include "raster/sprite.h"
#include "ui/view.h"
@ -146,6 +147,65 @@ void MoveMaskCommand::onExecute(Context* context)
}
}
std::string MoveMaskCommand::onGetFriendlyName() const
{
std::string text = "Move";
switch (m_target) {
case Boundaries: {
text += " Selection Boundaries";
break;
}
case Content: {
text += " Selection Content";
break;
}
}
text += " " + base::convert_to<std::string>(m_quantity);
switch (m_units) {
case Pixel:
text += " pixel";
break;
case TileWidth:
text += " horizontal tile";
break;
case TileHeight:
text += " vertical tile";
break;
case ZoomedPixel:
text += " zoomed pixel";
break;
case ZoomedTileWidth:
text += " zoomed horizontal tile";
break;
case ZoomedTileHeight:
text += " zoomed vertical tile";
break;
case ViewportWidth:
text += " viewport width";
break;
case ViewportHeight:
text += " viewport height";
break;
}
if (m_quantity != 1)
text += "s";
switch (m_direction) {
case Left: text += " left"; break;
case Right: text += " right"; break;
case Up: text += " up"; break;
case Down: text += " down"; break;
}
return text;
}
Command* CommandFactory::createMoveMaskCommand()
{
return new MoveMaskCommand;

View File

@ -48,6 +48,7 @@ namespace app {
void onLoadParams(Params* params);
bool onEnabled(Context* context);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
private:
Target m_target;

View File

@ -29,7 +29,6 @@
namespace app {
using namespace ui;
using namespace gfx;
class ShowOnionSkinCommand : public Command {

View File

@ -159,7 +159,7 @@ private:
PaletteEditorCommand::PaletteEditorCommand()
: Command("PaletteEditor",
"PaletteEditor",
"Palette Editor",
CmdRecordableFlag)
{
m_open = true;

View File

@ -28,10 +28,10 @@
#include "app/context.h"
#include "app/modules/editors.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/settings/document_settings.h"
#include "app/settings/settings.h"
#include "app/ui/editor/editor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/status_bar.h"
#include "app/util/render.h"
#include "raster/conversion_she.h"
@ -127,7 +127,8 @@ protected:
case kKeyDownMessage: {
KeyMessage* keyMsg = static_cast<KeyMessage*>(msg);
Command* command = NULL;
get_command_from_key_message(msg, &command, NULL);
KeyboardShortcuts::instance()
->getCommandFromKeyMessage(msg, &command, NULL);
// Change frame
if (command != NULL &&

View File

@ -33,6 +33,7 @@
#include "app/ui/timeline.h"
#include "app/undo_transaction.h"
#include "app/util/range_utils.h"
#include "base/convert_to.h"
#include "raster/cel.h"
#include "raster/image.h"
#include "raster/mask.h"
@ -53,6 +54,7 @@ protected:
void onLoadParams(Params* params);
bool onEnabled(Context* context);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
private:
bool m_flipMask;
@ -235,6 +237,20 @@ void RotateCommand::onExecute(Context* context)
update_screen_for_document(reader.document());
}
std::string RotateCommand::onGetFriendlyName() const
{
std::string text = "Rotate";
if (m_flipMask)
text += " Selection";
else
text += " Sprite";
text += " " + base::convert_to<std::string>(m_angle) + "\xc2\xb0";
return text;
}
Command* CommandFactory::createRotateCommand()
{
return new RotateCommand;

View File

@ -27,6 +27,7 @@
#include "app/settings/document_settings.h"
#include "app/settings/settings.h"
#include "app/ui/editor/editor.h"
#include "base/convert_to.h"
#include "ui/view.h"
namespace app {
@ -52,6 +53,7 @@ protected:
void onLoadParams(Params* params);
bool onEnabled(Context* context);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
private:
Direction m_direction;
@ -142,6 +144,49 @@ void ScrollCommand::onExecute(Context* context)
current_editor->setEditorScroll(scroll.x+dx, scroll.y+dy, true);
}
std::string ScrollCommand::onGetFriendlyName() const
{
std::string text = "Scroll " + base::convert_to<std::string>(m_quantity);
switch (m_units) {
case Pixel:
text += " pixel";
break;
case TileWidth:
text += " horizontal tile";
break;
case TileHeight:
text += " vertical tile";
break;
case ZoomedPixel:
text += " zoomed pixel";
break;
case ZoomedTileWidth:
text += " zoomed horizontal tile";
break;
case ZoomedTileHeight:
text += " zoomed vertical tile";
break;
case ViewportWidth:
text += " viewport width";
break;
case ViewportHeight:
text += " viewport height";
break;
}
if (m_quantity != 1)
text += "s";
switch (m_direction) {
case Left: text += " left"; break;
case Right: text += " right"; break;
case Up: text += " up"; break;
case Down: text += " down"; break;
}
return text;
}
Command* CommandFactory::createScrollCommand()
{
return new ScrollCommand;

View File

@ -39,7 +39,7 @@ protected:
SwitchColorsCommand::SwitchColorsCommand()
: Command("SwitchColors",
"SwitchColors",
"Switch Colors",
CmdUIOnlyFlag)
{
}

View File

@ -24,6 +24,7 @@
#include "app/commands/params.h"
#include "app/modules/editors.h"
#include "app/ui/editor/editor.h"
#include "base/convert_to.h"
namespace app {
@ -38,6 +39,7 @@ protected:
void onLoadParams(Params* params);
bool onEnabled(Context* context);
void onExecute(Context* context);
std::string onGetFriendlyName() const;
private:
Action m_action;
@ -98,6 +100,25 @@ void ZoomCommand::onExecute(Context* context)
current_editor->setEditorZoom(zoom);
}
std::string ZoomCommand::onGetFriendlyName() const
{
std::string text = "Zoom";
switch (m_action) {
case In:
text += " in";
break;
case Out:
text += " out";
break;
case Set:
text += " " + base::convert_to<std::string>(m_percentage) + "%";
break;
}
return text;
}
Command* CommandFactory::createZoomCommand()
{
return new ZoomCommand;

View File

@ -37,6 +37,11 @@ Command::~Command()
{
}
std::string Command::friendlyName() const
{
return onGetFriendlyName();
}
void Command::loadParams(Params* params)
{
onLoadParams(params);
@ -101,4 +106,9 @@ void Command::onExecute(Context* context)
// Do nothing
}
std::string Command::onGetFriendlyName() const
{
return m_friendly_name;
}
} // namespace app

View File

@ -21,6 +21,7 @@
#pragma once
#include "app/commands/command_factory.h"
#include <string>
namespace app {
class Context;
@ -44,7 +45,7 @@ namespace app {
virtual Command* clone() const { return new Command(*this); }
const char* short_name() const { return m_short_name; }
const char* friendly_name() const { return m_friendly_name; }
std::string friendlyName() const;
void loadParams(Params* params);
bool isEnabled(Context* context);
@ -56,6 +57,7 @@ namespace app {
virtual bool onEnabled(Context* context);
virtual bool onChecked(Context* context);
virtual void onExecute(Context* context);
virtual std::string onGetFriendlyName() const;
};
} // namespace app

View File

@ -61,6 +61,7 @@ FOR_EACH_COMMAND(GridSettings)
FOR_EACH_COMMAND(ImportSpriteSheet)
FOR_EACH_COMMAND(InvertColor)
FOR_EACH_COMMAND(InvertMask)
FOR_EACH_COMMAND(KeyboardShortcuts)
FOR_EACH_COMMAND(Launch)
FOR_EACH_COMMAND(LayerFromBackground)
FOR_EACH_COMMAND(LayerProperties)

View File

@ -26,58 +26,58 @@
namespace app {
class Params {
std::map<std::string, std::string> m_params;
public:
typedef std::map<std::string, std::string> Map;
typedef Map::iterator iterator;
typedef Map::const_iterator const_iterator;
iterator begin() { return m_params.begin(); }
iterator end() { return m_params.end(); }
const_iterator begin() const { return m_params.begin(); }
const_iterator end() const { return m_params.end(); }
Params() { }
Params(const Params& copy) : m_params(copy.m_params) { }
virtual ~Params() { }
Params* clone()
{
Params* clone() const {
return new Params(*this);
}
bool empty() const
{
bool empty() const {
return m_params.empty();
}
bool has_param(const char* name) const
{
bool has_param(const char* name) const {
return m_params.find(name) != m_params.end();
}
bool operator==(const Params& params) const
{
bool operator==(const Params& params) const {
return m_params == params.m_params;
}
bool operator!=(const Params& params) const
{
bool operator!=(const Params& params) const {
return m_params != params.m_params;
}
std::string& set(const char* name, const char* value)
{
std::string& set(const char* name, const char* value) {
return m_params[name] = value;
}
std::string& get(const char* name)
{
std::string& get(const char* name) {
return m_params[name];
}
template<typename T>
T get_as(const char* name)
{
T get_as(const char* name) {
std::istringstream stream(m_params[name]);
T value;
stream >> value;
return value;
}
private:
Map m_params;
};
} // namespace app

View File

@ -36,6 +36,7 @@
#include "app/tools/ink.h"
#include "app/tools/tool_box.h"
#include "app/ui/editor/editor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_menu_bar.h"
#include "app/ui/main_menu_bar.h"
#include "app/ui/main_window.h"
@ -63,19 +64,12 @@
#include <list>
#include <vector>
#ifdef ALLEGRO_WINDOWS
#include <winalleg.h>
#endif
#define SPRITEDITOR_ACTION_COPYSELECTION "CopySelection"
#define SPRITEDITOR_ACTION_SNAPTOGRID "SnapToGrid"
#define SPRITEDITOR_ACTION_ANGLESNAP "AngleSnap"
#define SPRITEDITOR_ACTION_MAINTAINASPECTRATIO "MaintainAspectRatio"
#define SPRITEDITOR_ACTION_LOCKAXIS "LockAxis"
#define SPRITEDITOR_ACTION_ADDSEL "AddSelection"
#define SPRITEDITOR_ACTION_SUBSEL "SubtractSelection"
namespace app {
using namespace gfx;
@ -95,38 +89,6 @@ static struct {
//////////////////////////////////////////////////////////////////////
struct Shortcut {
enum class Type {
ExecuteCommand,
ChangeTool,
EditorQuicktool,
SpriteEditor
};
Accelerator* accel;
Type type;
Command* command;
KeyContext keycontext;
tools::Tool* tool;
std::string action;
Params* params;
Shortcut(Type type);
~Shortcut();
void add_shortcut(const char* shortcut_string);
bool is_pressed(Message* msg);
bool is_pressed_from_key_array();
};
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params);
static Shortcut* get_keyboard_shortcut_for_tool(tools::Tool* tool);
static Shortcut* get_keyboard_shortcut_for_quicktool(tools::Tool* tool);
static Shortcut* get_keyboard_shortcut_for_spriteeditor(const char* action_name);
//////////////////////////////////////////////////////////////////////
class CustomizedGuiManager : public Manager
, public LayoutIO
{
@ -144,8 +106,6 @@ static she::Clipboard* main_clipboard = NULL;
static CustomizedGuiManager* manager = NULL;
static Theme* ase_theme = NULL;
static std::vector<Shortcut*>* shortcuts = NULL;
// Default GUI screen configuration
static int screen_scaling;
@ -155,25 +115,9 @@ static void reload_default_font();
static void load_gui_config(int& w, int& h, bool& maximized);
static void save_gui_config();
static KeyContext get_current_keycontext()
{
app::Context* ctx = UIContext::instance();
DocumentLocation location = ctx->activeLocation();
Document* doc = location.document();
if (doc &&
doc->isMaskVisible() &&
ctx->settings()->getCurrentTool()->getInk(0)->isSelection())
return KeyContext::Selection;
else
return KeyContext::Normal;
}
// Initializes GUI.
int init_module_gui()
{
shortcuts = new std::vector<Shortcut*>;
int w, h;
bool maximized;
load_gui_config(w, h, maximized);
@ -233,13 +177,6 @@ void exit_module_gui()
{
save_gui_config();
// destroy shortcuts
ASSERT(shortcuts != NULL);
for (Shortcut* shortcut : *shortcuts)
delete shortcut;
delete shortcuts;
shortcuts = NULL;
delete manager;
// Now we can destroy theme
@ -470,295 +407,6 @@ CheckBox* check_button_new(const char *text, int b1, int b2, int b3, int b4)
return widget;
}
//////////////////////////////////////////////////////////////////////
// Keyboard shortcuts
//////////////////////////////////////////////////////////////////////
Accelerator* add_keyboard_shortcut_to_execute_command(const char* shortcut_string,
const char* command_name, Params* params, KeyContext keyContext)
{
Shortcut* shortcut = get_keyboard_shortcut_for_command(command_name, params);
if (!shortcut) {
shortcut = new Shortcut(Shortcut::Type::ExecuteCommand);
shortcut->command = CommandsModule::instance()->getCommandByName(command_name);
shortcut->params = params ? params->clone(): new Params;
shortcut->keycontext = keyContext;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
Accelerator* add_keyboard_shortcut_to_change_tool(const char* shortcut_string, tools::Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_tool(tool);
if (!shortcut) {
shortcut = new Shortcut(Shortcut::Type::ChangeTool);
shortcut->tool = tool;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
Accelerator* add_keyboard_shortcut_to_quicktool(const char* shortcut_string, tools::Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_quicktool(tool);
if (!shortcut) {
shortcut = new Shortcut(Shortcut::Type::EditorQuicktool);
shortcut->tool = tool;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
Accelerator* add_keyboard_shortcut_to_spriteeditor(const char* shortcut_string, const char* action_name)
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(action_name);
if (!shortcut) {
shortcut = new Shortcut(Shortcut::Type::SpriteEditor);
shortcut->action = action_name;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
bool get_command_from_key_message(Message* msg, Command** command, Params** params)
{
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->type == Shortcut::Type::ExecuteCommand &&
shortcut->is_pressed(msg)) {
if (command) *command = shortcut->command;
if (params) *params = shortcut->params;
return true;
}
}
return false;
}
Accelerator* get_accel_to_execute_command(const char* command_name, Params* params)
{
Shortcut* shortcut = get_keyboard_shortcut_for_command(command_name, params);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_change_tool(tools::Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_tool(tool);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_copy_selection()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_COPYSELECTION);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_snap_to_grid()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_SNAPTOGRID);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_angle_snap()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_ANGLESNAP);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_maintain_aspect_ratio()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_MAINTAINASPECTRATIO);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_lock_axis()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_LOCKAXIS);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_add_selection()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_ADDSEL);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Accelerator* get_accel_to_subtract_selection()
{
Shortcut* shortcut = get_keyboard_shortcut_for_spriteeditor(SPRITEDITOR_ACTION_SUBSEL);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
tools::Tool* get_selected_quicktool(tools::Tool* currentTool)
{
if (currentTool && currentTool->getInk(0)->isSelection()) {
Accelerator* copyselection_accel = get_accel_to_copy_selection();
if (copyselection_accel && copyselection_accel->checkFromAllegroKeyArray())
return NULL;
}
tools::ToolBox* toolbox = App::instance()->getToolBox();
// Iterate over all tools
for (tools::ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) {
Shortcut* shortcut = get_keyboard_shortcut_for_quicktool(*it);
// Collect all tools with the pressed keyboard-shortcut
if (shortcut && shortcut->is_pressed_from_key_array()) {
return *it;
}
}
return NULL;
}
Shortcut::Shortcut(Shortcut::Type type)
{
this->type = type;
this->accel = new Accelerator;
this->command = NULL;
this->keycontext = KeyContext::Any;
this->tool = NULL;
this->params = NULL;
}
Shortcut::~Shortcut()
{
delete params;
delete accel;
}
void Shortcut::add_shortcut(const char* shortcut_string)
{
this->accel->addKeysFromString(shortcut_string);
}
bool Shortcut::is_pressed(Message* msg)
{
bool res = false;
if (accel) {
res = accel->check(msg->keyModifiers(),
static_cast<KeyMessage*>(msg)->scancode(),
static_cast<KeyMessage*>(msg)->unicodeChar());
if (res &&
keycontext != KeyContext::Any &&
keycontext != get_current_keycontext()) {
res = false;
}
}
return res;
}
bool Shortcut::is_pressed_from_key_array()
{
bool res = false;
if (accel) {
res = accel->checkFromAllegroKeyArray();
if (res &&
keycontext != KeyContext::Any &&
keycontext != get_current_keycontext()) {
res = false;
}
}
return res;
}
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params)
{
Command* command = CommandsModule::instance()->getCommandByName(command_name);
if (!command)
return NULL;
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->type == Shortcut::Type::ExecuteCommand &&
shortcut->command == command &&
((!params && shortcut->params->empty()) ||
(params && *shortcut->params == *params))) {
return shortcut;
}
}
return NULL;
}
static Shortcut* get_keyboard_shortcut_for_tool(tools::Tool* tool)
{
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->type == Shortcut::Type::ChangeTool &&
shortcut->tool == tool) {
return shortcut;
}
}
return NULL;
}
static Shortcut* get_keyboard_shortcut_for_quicktool(tools::Tool* tool)
{
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->type == Shortcut::Type::EditorQuicktool &&
shortcut->tool == tool) {
return shortcut;
}
}
return NULL;
}
static Shortcut* get_keyboard_shortcut_for_spriteeditor(const char* action_name)
{
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->type == Shortcut::Type::SpriteEditor &&
shortcut->action == action_name) {
return shortcut;
}
}
return NULL;
}
// Manager event handler.
bool CustomizedGuiManager::onProcessMessage(Message* msg)
{
@ -809,26 +457,24 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
break;
}
for (Shortcut* shortcut : *shortcuts) {
if (shortcut->is_pressed(msg)) {
for (const Key* key : *KeyboardShortcuts::instance()) {
if (key->isPressed(msg)) {
// Cancel menu-bar loops (to close any popup menu)
App::instance()->getMainWindow()->getMenuBar()->cancelMenuLoop();
switch (shortcut->type) {
switch (key->type()) {
case Shortcut::Type::ChangeTool: {
case KeyType::Tool: {
tools::Tool* current_tool = UIContext::instance()->settings()->getCurrentTool();
tools::Tool* select_this_tool = shortcut->tool;
tools::Tool* select_this_tool = key->tool();
tools::ToolBox* toolbox = App::instance()->getToolBox();
std::vector<tools::Tool*> possibles;
// Iterate over all tools
for (tools::ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) {
Shortcut* shortcut = get_keyboard_shortcut_for_tool(*it);
// Collect all tools with the pressed keyboard-shortcut
if (shortcut && shortcut->is_pressed(msg))
possibles.push_back(*it);
// Collect all tools with the pressed keyboard-shortcut
for (tools::Tool* tool : *toolbox) {
Key* key = KeyboardShortcuts::instance()->tool(tool);
if (key && key->isPressed(msg))
possibles.push_back(tool);
}
if (possibles.size() >= 2) {
@ -859,8 +505,8 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
return true;
}
case Shortcut::Type::ExecuteCommand: {
Command* command = shortcut->command;
case KeyType::Command: {
Command* command = key->command();
// Commands are executed only when the main window is
// the current window running at foreground.
@ -875,16 +521,17 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
else if (child->isDesktop() && child == App::instance()->getMainWindow()) {
// OK, so we can execute the command represented
// by the pressed-key in the message...
UIContext::instance()->executeCommand(command, shortcut->params);
UIContext::instance()->executeCommand(
command, key->params());
return true;
}
}
break;
}
case Shortcut::Type::EditorQuicktool: {
case KeyType::Quicktool: {
// Do nothing, it is used in the editor through the
// get_selected_quicktool() function.
// KeyboardShortcuts::getCurrentQuicktool() function.
break;
}

View File

@ -22,10 +22,7 @@
#include "app/ui/skin/skin_property.h"
#include "base/exception.h"
#include "ui/accelerator.h"
#include "ui/base.h"
#include <list>
#include <string>
namespace ui {
class ButtonBase;
@ -45,12 +42,6 @@ namespace app {
class Tool;
}
enum class KeyContext {
Any,
Normal,
Selection,
};
int init_module_gui();
void exit_module_gui();
@ -78,29 +69,6 @@ namespace app {
ui::CheckBox* check_button_new(const char* text, int b1, int b2, int b3, int b4);
//////////////////////////////////////////////////////////////////////
// Keyboard shortcuts
ui::Accelerator* add_keyboard_shortcut_to_execute_command(const char* shortcut,
const char* command_name, Params* params, KeyContext keyContext);
ui::Accelerator* add_keyboard_shortcut_to_change_tool(const char* shortcut, tools::Tool* tool);
ui::Accelerator* add_keyboard_shortcut_to_quicktool(const char* shortcut, tools::Tool* tool);
ui::Accelerator* add_keyboard_shortcut_to_spriteeditor(const char* shortcut, const char* action_name);
bool get_command_from_key_message(ui::Message* msg, Command** command, Params** params);
ui::Accelerator* get_accel_to_execute_command(const char* command, Params* params = NULL);
ui::Accelerator* get_accel_to_change_tool(tools::Tool* tool);
ui::Accelerator* get_accel_to_copy_selection();
ui::Accelerator* get_accel_to_snap_to_grid();
ui::Accelerator* get_accel_to_angle_snap();
ui::Accelerator* get_accel_to_maintain_aspect_ratio();
ui::Accelerator* get_accel_to_lock_axis();
ui::Accelerator* get_accel_to_add_selection();
ui::Accelerator* get_accel_to_subtract_selection();
tools::Tool* get_selected_quicktool(tools::Tool* currentTool);
} // namespace app
#endif

View File

@ -25,9 +25,12 @@
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/modules/gui.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h"
#include "ui/accelerator.h"
#include "ui/menu.h"
#include "ui/message.h"
#include "ui/preferred_size_event.h"
#include "ui/widget.h"
#include <stdarg.h>
@ -38,8 +41,9 @@ namespace app {
using namespace ui;
AppMenuItem::AppMenuItem(const char* text, Command* command, Params* params)
AppMenuItem::AppMenuItem(const char* text, Command* command, const Params* params)
: MenuItem(text)
, m_key(NULL)
, m_command(command)
, m_params(params ? params->clone(): NULL)
{
@ -82,6 +86,31 @@ bool AppMenuItem::onProcessMessage(Message* msg)
return MenuItem::onProcessMessage(msg);
}
void AppMenuItem::onPreferredSize(PreferredSizeEvent& ev)
{
gfx::Size size(0, 0);
if (hasText()) {
size.w =
+ this->border_width.l
+ getTextWidth()
+ (inBar() ? this->child_spacing/4: this->child_spacing)
+ this->border_width.r;
size.h =
+ this->border_width.t
+ getTextHeight()
+ this->border_width.b;
if (m_key && !m_key->accels().empty()) {
size.w += Graphics::measureUIStringLength(
m_key->accels().front().toString().c_str(), getFont());
}
}
ev.setPreferredSize(size);
}
void AppMenuItem::onClick()
{
MenuItem::onClick();

View File

@ -23,6 +23,7 @@
#include "ui/menu.h"
namespace app {
class Key;
class Command;
class Params;
@ -33,17 +34,22 @@ namespace app {
// used to check the availability of the command).
class AppMenuItem : public ui::MenuItem {
public:
AppMenuItem(const char* text, Command* command, Params* params);
AppMenuItem(const char* text, Command* command, const Params* params);
~AppMenuItem();
Key* getKey() { return m_key; }
void setKey(Key* key) { m_key = key; }
Command* getCommand() { return m_command; }
Params* getParams() { return m_params; }
protected:
bool onProcessMessage(ui::Message* msg) override;
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
void onClick() override;
private:
Key* m_key;
Command* m_command;
Params* m_params;
};

View File

@ -24,11 +24,11 @@
#include "app/app.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/editor_customization_delegate.h"
#include "app/ui/editor/editor_view.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/mini_editor.h"
#include "app/ui/workspace.h"
@ -77,61 +77,42 @@ public:
// EditorCustomizationDelegate implementation
tools::Tool* getQuickTool(tools::Tool* currentTool) override {
return get_selected_quicktool(currentTool);
return KeyboardShortcuts::instance()
->getCurrentQuicktool(currentTool);
}
bool isCopySelectionKeyPressed() override {
Accelerator* accel = get_accel_to_copy_selection();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::CopySelection);
}
bool isSnapToGridKeyPressed() override {
Accelerator* accel = get_accel_to_snap_to_grid();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::SnapToGrid);
}
bool isAngleSnapKeyPressed() override {
Accelerator* accel = get_accel_to_angle_snap();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::AngleSnap);
}
bool isMaintainAspectRatioKeyPressed() override {
Accelerator* accel = get_accel_to_maintain_aspect_ratio();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::MaintainAspectRatio);
}
bool isLockAxisKeyPressed() override {
Accelerator* accel = get_accel_to_lock_axis();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::LockAxis);
}
bool isAddSelectionPressed() override {
Accelerator* accel = get_accel_to_add_selection();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
return isKeyActionPressed(KeyAction::AddSelection);
}
bool isSubtractSelectionPressed() override {
Accelerator* accel = get_accel_to_subtract_selection();
if (accel)
return accel->checkFromAllegroKeyArray();
return isKeyActionPressed(KeyAction::SubtractSelection);
}
private:
bool isKeyActionPressed(KeyAction action) {
if (Key* key = KeyboardShortcuts::instance()->action(action))
return key->checkFromAllegroKeyArray();
else
return false;
}

View File

@ -25,13 +25,13 @@
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/modules/gui.h"
#include "app/tools/controller.h"
#include "app/tools/ink.h"
#include "app/tools/tool.h"
#include "app/tools/tool_loop.h"
#include "app/tools/tool_loop_manager.h"
#include "app/ui/editor/editor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h"
#include "raster/blend.h"
#include "ui/message.h"
@ -178,7 +178,8 @@ bool DrawingState::onKeyDown(Editor* editor, KeyMessage* msg)
Command* command = NULL;
Params* params = NULL;
if (get_command_from_key_message(msg, &command, &params)) {
if (KeyboardShortcuts::instance()
->getCommandFromKeyMessage(msg, &command, &params)) {
// We accept zoom commands.
if (strcmp(command->short_name(), CommandId::Zoom) == 0) {
UIContext::instance()->executeCommand(command, params);

View File

@ -28,7 +28,6 @@
#include "app/commands/cmd_move_mask.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/modules/gui.h"
#include "app/settings/settings.h"
#include "app/tools/ink.h"
#include "app/tools/tool.h"
@ -38,6 +37,7 @@
#include "app/ui/editor/pixels_movement.h"
#include "app/ui/editor/standby_state.h"
#include "app/ui/editor/transform_handles.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
@ -326,7 +326,8 @@ bool MovingPixelsState::onKeyDown(Editor* editor, KeyMessage* msg)
else {
Command* command = NULL;
Params* params = NULL;
if (get_command_from_key_message(msg, &command, &params)) {
if (KeyboardShortcuts::instance()
->getCommandFromKeyMessage(msg, &command, &params)) {
// We accept zoom commands.
if (strcmp(command->short_name(), CommandId::Zoom) == 0) {
UIContext::instance()->executeCommand(command, params);

View File

@ -0,0 +1,589 @@
/* Aseprite
* Copyright (C) 2001-2014 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/ui/keyboard_shortcuts.h"
#include "app/app.h"
#include "app/app_menus.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/document.h"
#include "app/document_location.h"
#include "app/settings/settings.h"
#include "app/tools/ink.h"
#include "app/tools/tool.h"
#include "app/tools/tool_box.h"
#include "app/ui_context.h"
#include "app/xml_document.h"
#include "app/xml_exception.h"
#include "ui/accelerator.h"
#include "ui/message.h"
#include <algorithm>
#define XML_KEYBOARD_FILE_VERSION "1"
namespace {
static struct {
const char* name;
const char* userfriendly;
app::KeyAction action;
} actions[] = {
{ "CopySelection" , "Copy Selection" , app::KeyAction::CopySelection },
{ "SnapToGrid" , "Snap To Grid" , app::KeyAction::SnapToGrid },
{ "AngleSnap" , "Angle Snap" , app::KeyAction::AngleSnap },
{ "MaintainAspectRatio" , "Maintain Aspect Ratio", app::KeyAction::MaintainAspectRatio },
{ "LockAxis" , "Lock Axis" , app::KeyAction::LockAxis },
{ "AddSelection" , "Add Selection" , app::KeyAction::AddSelection },
{ "SubtractSelection" , "Subtract Selection" , app::KeyAction::SubtractSelection },
{ NULL , NULL , app::KeyAction::None }
};
const char* get_shortcut(TiXmlElement* elem)
{
const char* shortcut = NULL;
#if defined ALLEGRO_WINDOWS
if (!shortcut) shortcut = elem->Attribute("win");
#elif defined ALLEGRO_MACOSX
if (!shortcut) shortcut = elem->Attribute("mac");
#elif defined ALLEGRO_UNIX
if (!shortcut) shortcut = elem->Attribute("linux");
#endif
if (!shortcut)
shortcut = elem->Attribute("shortcut");
return shortcut;
}
std::string get_user_friendly_string_for_keyaction(app::KeyAction action)
{
for (int c=0; actions[c].name; ++c) {
if (action == actions[c].action)
return actions[c].userfriendly;
}
return "";
}
} // anonymous namespace
namespace base {
template<> app::KeyAction convert_to(const std::string& from) {
app::KeyAction action = app::KeyAction::None;
for (int c=0; actions[c].name; ++c) {
if (from == actions[c].name)
return actions[c].action;
}
return action;
}
template<> std::string convert_to(const app::KeyAction& from) {
for (int c=0; actions[c].name; ++c) {
if (from == actions[c].action)
return actions[c].name;
}
return "";
}
} // namespace base
namespace app {
using namespace ui;
Key::Key(Command* command, const Params* params, KeyContext keyContext)
: m_type(KeyType::Command)
, m_useUsers(false)
, m_keycontext(keyContext)
, m_command(command)
, m_params(params ? params->clone(): NULL)
{
}
Key::Key(KeyType type, tools::Tool* tool)
: m_type(type)
, m_useUsers(false)
, m_keycontext(KeyContext::Any)
, m_tool(tool)
{
}
Key::Key(KeyAction action)
: m_type(KeyType::Action)
, m_useUsers(false)
, m_keycontext(KeyContext::Any)
, m_action(action)
{
}
void Key::setUserAccels(const Accelerators& accels)
{
m_useUsers = true;
m_users = accels;
}
void Key::add(const ui::Accelerator& accel, KeySource source)
{
Accelerators* accels = &m_accels;
if (source == KeySource::UserDefined) {
if (!m_useUsers) {
m_useUsers = true;
m_users = m_accels;
}
accels = &m_users;
KeyboardShortcuts::instance()->disableAccel(accel);
}
accels->push_back(accel);
}
bool Key::isPressed(Message* msg) const
{
ASSERT(dynamic_cast<KeyMessage*>(msg) != NULL);
for (const Accelerator& accel : accels()) {
if (accel.check(msg->keyModifiers(),
static_cast<KeyMessage*>(msg)->scancode(),
static_cast<KeyMessage*>(msg)->unicodeChar()) &&
(m_keycontext == KeyContext::Any ||
m_keycontext == KeyboardShortcuts::instance()->getCurrentKeyContext())) {
return true;
}
}
return false;
}
bool Key::checkFromAllegroKeyArray()
{
for (const Accelerator& accel : this->accels()) {
if (accel.checkFromAllegroKeyArray())
return true;
}
return false;
}
bool Key::hasAccel(const ui::Accelerator& accel) const
{
return (std::find(accels().begin(), accels().end(), accel) != accels().end());
}
void Key::disableAccel(const ui::Accelerator& accel)
{
if (!m_useUsers) {
m_useUsers = true;
m_users = m_accels;
}
Accelerators::iterator it = std::find(m_users.begin(), m_users.end(), accel);
if (it != m_users.end())
m_users.erase(it);
}
void Key::reset()
{
m_users.clear();
m_useUsers = false;
}
std::string Key::triggerString() const
{
switch (m_type) {
case KeyType::Command:
if (m_params)
m_command->loadParams(m_params);
return m_command->friendlyName();
case KeyType::Tool:
case KeyType::Quicktool: {
std::string text = m_tool->getText();
if (m_type == KeyType::Quicktool)
text += " (quick)";
return text;
}
case KeyType::Action:
return get_user_friendly_string_for_keyaction(m_action);
}
return "Unknown";
}
KeyboardShortcuts* KeyboardShortcuts::instance()
{
static KeyboardShortcuts* singleton = NULL;
if (!singleton)
singleton = new KeyboardShortcuts();
return singleton;
}
KeyboardShortcuts::KeyboardShortcuts()
{
}
KeyboardShortcuts::~KeyboardShortcuts()
{
clear();
}
void KeyboardShortcuts::clear()
{
for (Key* key : m_keys) {
delete key;
}
m_keys.clear();
}
void KeyboardShortcuts::importFile(TiXmlElement* rootElement, KeySource source)
{
// <keyboard><commands><key>
TiXmlHandle handle(rootElement);
TiXmlElement* xmlKey = handle
.FirstChild("commands")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* command_name = xmlKey->Attribute("command");
const char* command_key = get_shortcut(xmlKey);
if (command_name && command_key) {
Command* command = CommandsModule::instance()->getCommandByName(command_name);
if (command) {
// Read context
KeyContext keycontext = KeyContext::Any;
const char* keycontextstr = xmlKey->Attribute("context");
if (keycontextstr) {
if (strcmp(keycontextstr, "Selection") == 0)
keycontext = KeyContext::Selection;
else if (strcmp(keycontextstr, "Normal") == 0)
keycontext = KeyContext::Normal;
}
// Read params
Params params;
TiXmlElement* xmlParam = xmlKey->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();
}
PRINTF(" - Shortcut for command `%s' <%s>\n", command_name, command_key);
// add the keyboard shortcut to the command
Key* key = this->command(command_name, &params, keycontext);
if (key) {
key->add(Accelerator(command_key), source);
// Add the shortcut to the menuitems with this
// command (this is only visual, the "manager_msg_proc"
// is the only one that process keyboard shortcuts)
if (key->accels().size() == 1) {
AppMenus::instance()->applyShortcutToMenuitemsWithCommand(
command, &params, key);
}
}
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load keyboard shortcuts for tools
// <gui><keyboard><tools><key>
xmlKey = handle
.FirstChild("tools")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_id = xmlKey->Attribute("tool");
const char* tool_key = get_shortcut(xmlKey);
if (tool_id && tool_key) {
tools::Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
PRINTF(" - Shortcut for tool `%s': <%s>\n", tool_id, tool_key);
Key* key = this->tool(tool);
if (key)
key->add(Accelerator(tool_key), source);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load keyboard shortcuts for quicktools
// <gui><keyboard><quicktools><key>
xmlKey = handle
.FirstChild("quicktools")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_id = xmlKey->Attribute("tool");
const char* tool_key = get_shortcut(xmlKey);
if (tool_id && tool_key) {
tools::Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
PRINTF(" - Shortcut for quicktool `%s': <%s>\n", tool_id, tool_key);
Key* key = this->quicktool(tool);
if (key)
key->add(Accelerator(tool_key), source);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Load special keyboard shortcuts for sprite editor customization
// <gui><keyboard><spriteeditor>
xmlKey = handle
.FirstChild("actions")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_action = xmlKey->Attribute("action");
const char* tool_key = get_shortcut(xmlKey);
if (tool_action && tool_key) {
PRINTF(" - Shortcut for sprite editor `%s': <%s>\n", tool_action, tool_key);
KeyAction action = base::convert_to<KeyAction, std::string>(tool_action);
if (action != KeyAction::None) {
Key* key = this->action(action);
if (key)
key->add(Accelerator(tool_key), source);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
}
void KeyboardShortcuts::importFile(const std::string& filename, KeySource source)
{
XmlDocumentRef doc = app::open_xml(filename);
TiXmlHandle handle(doc);
TiXmlElement* xmlKey = handle.FirstChild("keyboard").ToElement();
importFile(xmlKey, source);
}
void KeyboardShortcuts::exportFile(const std::string& filename)
{
XmlDocumentRef doc(new TiXmlDocument());
TiXmlElement keyboard("keyboard");
TiXmlElement commands("commands");
TiXmlElement tools("tools");
TiXmlElement quicktools("quicktools");
TiXmlElement actions("actions");
keyboard.SetAttribute("version", XML_KEYBOARD_FILE_VERSION);
exportKeys(commands, KeyType::Command);
exportKeys(tools, KeyType::Tool);
exportKeys(quicktools, KeyType::Quicktool);
exportKeys(actions, KeyType::Action);
keyboard.InsertEndChild(commands);
keyboard.InsertEndChild(tools);
keyboard.InsertEndChild(quicktools);
keyboard.InsertEndChild(actions);
doc->InsertEndChild(keyboard);
save_xml(doc, filename);
}
void KeyboardShortcuts::exportKeys(TiXmlElement& parent, KeyType type)
{
for (Key* key : m_keys) {
// Save only user defined accelerators.
if (key->type() != type || key->userAccels().empty())
continue;
for (const ui::Accelerator& accel : key->userAccels()) {
TiXmlElement elem("key");
switch (key->type()) {
case KeyType::Command:
elem.SetAttribute("command", key->command()->short_name());
if (key->params()) {
for (const auto& param : *key->params()) {
if (param.second.empty())
continue;
TiXmlElement paramElem("param");
paramElem.SetAttribute("name", param.first.c_str());
paramElem.SetAttribute("value", param.second.c_str());
elem.InsertEndChild(paramElem);
}
}
break;
case KeyType::Tool:
case KeyType::Quicktool:
elem.SetAttribute("tool", key->tool()->getId().c_str());
break;
case KeyType::Action:
elem.SetAttribute("action",
base::convert_to<std::string>(key->action()).c_str());
break;
}
elem.SetAttribute("shortcut", accel.toString().c_str());
parent.InsertEndChild(elem);
}
}
}
void KeyboardShortcuts::reset()
{
for (Key* key : m_keys)
key->reset();
}
Key* KeyboardShortcuts::command(const char* commandName,
Params* params, KeyContext keyContext)
{
Command* command = CommandsModule::instance()->getCommandByName(commandName);
if (!command)
return NULL;
for (Key* key : m_keys) {
if (key->type() == KeyType::Command &&
key->keycontext() == keyContext &&
key->command() == command &&
((!params && key->params()->empty()) ||
(params && *key->params() == *params))) {
return key;
}
}
Key* key = new Key(command, params, keyContext);
m_keys.push_back(key);
return key;
}
Key* KeyboardShortcuts::tool(tools::Tool* tool)
{
for (Key* key : m_keys) {
if (key->type() == KeyType::Tool &&
key->tool() == tool) {
return key;
}
}
Key* key = new Key(KeyType::Tool, tool);
m_keys.push_back(key);
return key;
}
Key* KeyboardShortcuts::quicktool(tools::Tool* tool)
{
for (Key* key : m_keys) {
if (key->type() == KeyType::Quicktool &&
key->tool() == tool) {
return key;
}
}
Key* key = new Key(KeyType::Quicktool, tool);
m_keys.push_back(key);
return key;
}
Key* KeyboardShortcuts::action(KeyAction action)
{
for (Key* key : m_keys) {
if (key->type() == KeyType::Action &&
key->action() == action) {
return key;
}
}
Key* key = new Key(action);
m_keys.push_back(key);
return key;
}
void KeyboardShortcuts::disableAccel(const ui::Accelerator& accel)
{
for (Key* key : m_keys) {
if (key->hasAccel(accel))
key->disableAccel(accel);
}
}
KeyContext KeyboardShortcuts::getCurrentKeyContext()
{
app::Context* ctx = UIContext::instance();
DocumentLocation location = ctx->activeLocation();
Document* doc = location.document();
if (doc &&
doc->isMaskVisible() &&
ctx->settings()->getCurrentTool()->getInk(0)->isSelection())
return KeyContext::Selection;
else
return KeyContext::Normal;
}
bool KeyboardShortcuts::getCommandFromKeyMessage(Message* msg, Command** command, Params** params)
{
for (Key* key : m_keys) {
if (key->type() == KeyType::Command && key->isPressed(msg)) {
if (command) *command = key->command();
if (params) *params = key->params();
return true;
}
}
return false;
}
tools::Tool* KeyboardShortcuts::getCurrentQuicktool(tools::Tool* currentTool)
{
if (currentTool && currentTool->getInk(0)->isSelection()) {
Key* key = action(KeyAction::CopySelection);
if (key && key->checkFromAllegroKeyArray())
return NULL;
}
tools::ToolBox* toolbox = App::instance()->getToolBox();
// Iterate over all tools
for (tools::Tool* tool : *toolbox) {
Key* key = quicktool(tool);
// Collect all tools with the pressed keyboard-shortcut
if (key && key->checkFromAllegroKeyArray()) {
return tool;
}
}
return NULL;
}
} // namespace app

View File

@ -0,0 +1,175 @@
/* Aseprite
* Copyright (C) 2001-2014 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
*/
#ifndef APP_UI_KEYBOARD_SHORTCUTS_H_INCLUDED
#define APP_UI_KEYBOARD_SHORTCUTS_H_INCLUDED
#pragma once
#include "base/convert_to.h"
#include "base/disable_copying.h"
#include "ui/accelerator.h"
#include <vector>
class TiXmlElement;
namespace ui {
class Message;
}
namespace app {
class Command;
class Params;
namespace tools {
class Tool;
}
enum class KeyContext {
Any,
Normal,
Selection,
};
enum class KeySource {
Original,
UserDefined
};
enum class KeyType {
Command,
Tool,
Quicktool,
Action,
};
enum class KeyAction {
None,
CopySelection,
SnapToGrid,
AngleSnap,
MaintainAspectRatio,
LockAxis,
AddSelection,
SubtractSelection,
};
class Key {
public:
Key(Command* command, const Params* params, KeyContext keyContext);
Key(KeyType type, tools::Tool* tool);
explicit Key(KeyAction action);
KeyType type() const { return m_type; }
const ui::Accelerators& accels() const {
return (m_useUsers ? m_users: m_accels);
}
const ui::Accelerators& origAccels() const { return m_accels; }
const ui::Accelerators& userAccels() const { return m_users; }
void setUserAccels(const ui::Accelerators& accels);
void add(const ui::Accelerator& accel, KeySource source);
bool isPressed(ui::Message* msg) const;
bool checkFromAllegroKeyArray();
bool hasAccel(const ui::Accelerator& accel) const;
void disableAccel(const ui::Accelerator& accel);
// Resets user accelerators to the original ones.
void reset();
// for KeyType::Command
Command* command() const { return m_command; }
Params* params() const { return m_params; }
KeyContext keycontext() const { return m_keycontext; }
// for KeyType::Tool or Quicktool
tools::Tool* tool() const { return m_tool; }
// for KeyType::Action
KeyAction action() const { return m_action; }
std::string triggerString() const;
private:
KeyType m_type;
ui::Accelerators m_accels;
ui::Accelerators m_users;
bool m_useUsers;
KeyContext m_keycontext;
// for KeyType::Command
Command* m_command;
Params* m_params;
// for KeyType::Tool or Quicktool
tools::Tool* m_tool;
// for KeyType::Action
KeyAction m_action;
};
class KeyboardShortcuts {
public:
typedef std::vector<Key*> Keys;
typedef Keys::iterator iterator;
typedef Keys::const_iterator const_iterator;
static KeyboardShortcuts* instance();
~KeyboardShortcuts();
iterator begin() { return m_keys.begin(); }
iterator end() { return m_keys.end(); }
const_iterator begin() const { return m_keys.begin(); }
const_iterator end() const { return m_keys.end(); }
void clear();
void importFile(TiXmlElement* rootElement, KeySource source);
void importFile(const std::string& filename, KeySource source);
void exportFile(const std::string& filename);
void reset();
Key* command(const char* commandName,
Params* params = NULL, KeyContext keyContext = KeyContext::Any);
Key* tool(tools::Tool* tool);
Key* quicktool(tools::Tool* tool);
Key* action(KeyAction action);
void disableAccel(const ui::Accelerator& accel);
KeyContext getCurrentKeyContext();
bool getCommandFromKeyMessage(ui::Message* msg, Command** command, Params** params);
tools::Tool* getCurrentQuicktool(tools::Tool* currentTool);
private:
KeyboardShortcuts();
void exportKeys(TiXmlElement& parent, KeyType type);
Keys m_keys;
DISABLE_COPYING(KeyboardShortcuts);
};
} // namespace app
namespace base {
template<> app::KeyAction convert_to(const std::string& from);
template<> std::string convert_to(const app::KeyAction& from);
} // namespace base
#endif

View File

@ -0,0 +1,187 @@
/* Aseprite
* Copyright (C) 2001-2014 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/ui/select_accelerator.h"
#include "app/ui/keyboard_shortcuts.h"
#include "base/bind.h"
#include "base/signal.h"
namespace app {
using namespace ui;
class SelectAccelerator::KeyField : public ui::Entry {
public:
KeyField(const Accelerator& accel) : ui::Entry(256, "") {
setFocusMagnet(true);
setAccel(accel);
}
void setAccel(const Accelerator& accel) {
m_accel = accel;
updateText();
}
Signal1<void, const ui::Accelerator*> AccelChange;
protected:
bool onProcessMessage(Message* msg) override {
switch (msg->type()) {
case kKeyDownMessage:
if (hasFocus() && !isReadOnly()) {
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
m_accel = Accelerator(
keymsg->keyModifiers(),
keymsg->scancode(),
keymsg->unicodeChar() >= 32 ? keymsg->unicodeChar(): 0);
updateText();
AccelChange(&m_accel);
return true;
}
break;
}
return Entry::onProcessMessage(msg);
}
void updateText() {
setText(
Accelerator(
kKeyNoneModifier,
m_accel.scancode(),
m_accel.unicodeChar()).toString().c_str());
}
Accelerator m_accel;
};
SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel, bool canDelete)
: m_keyField(new KeyField(accel))
, m_accel(accel)
, m_deleted(false)
, m_modified(false)
{
updateModifiers();
updateAssignedTo();
keyPlaceholder()->addChild(m_keyField);
alt()->Click.connect(Bind<void>(&SelectAccelerator::onModifierChange, this, kKeyAltModifier, alt()));
cmd()->Click.connect(Bind<void>(&SelectAccelerator::onModifierChange, this, kKeyCmdModifier, cmd()));
ctrl()->Click.connect(Bind<void>(&SelectAccelerator::onModifierChange, this, kKeyCtrlModifier, ctrl()));
shift()->Click.connect(Bind<void>(&SelectAccelerator::onModifierChange, this, kKeyShiftModifier, shift()));
space()->Click.connect(Bind<void>(&SelectAccelerator::onModifierChange, this, kKeySpaceModifier, space()));
m_keyField->AccelChange.connect(&SelectAccelerator::onAccelChange, this);
clearButton()->Click.connect(Bind<void>(&SelectAccelerator::onClear, this));
okButton()->Click.connect(Bind<void>(&SelectAccelerator::onOK, this));
cancelButton()->Click.connect(Bind<void>(&SelectAccelerator::onCancel, this));
if (canDelete)
deleteButton()->Click.connect(Bind<void>(&SelectAccelerator::onDelete, this));
else
deleteButton()->setVisible(false);
}
void SelectAccelerator::onModifierChange(KeyModifiers modifier, CheckBox* checkbox)
{
bool state = (checkbox->isSelected());
m_accel = Accelerator(
(KeyModifiers)((m_accel.modifiers() & ~modifier) | (state ? modifier : 0)),
m_accel.scancode(),
m_accel.unicodeChar());
m_keyField->setAccel(m_accel);
updateAssignedTo();
}
void SelectAccelerator::onAccelChange(const ui::Accelerator* accel)
{
m_accel = *accel;
updateModifiers();
updateAssignedTo();
}
void SelectAccelerator::onClear()
{
m_accel = Accelerator(kKeyNoneModifier, kKeyNil, 0);
m_keyField->setAccel(m_accel);
updateModifiers();
updateAssignedTo();
m_keyField->requestFocus();
}
void SelectAccelerator::onOK()
{
m_modified = (m_origAccel != m_accel);
closeWindow(NULL);
}
void SelectAccelerator::onCancel()
{
closeWindow(NULL);
}
void SelectAccelerator::onDelete()
{
if (Alert::show("Warning"
"<<Do you really want to delete this keyboard shortcut?"
"||&Yes||&No") != 1)
return;
m_deleted = true;
closeWindow(NULL);
}
void SelectAccelerator::updateModifiers()
{
alt()->setSelected(m_accel.modifiers() & kKeyAltModifier ? true: false);
ctrl()->setSelected(m_accel.modifiers() & kKeyCtrlModifier ? true: false);
shift()->setSelected(m_accel.modifiers() & kKeyShiftModifier ? true: false);
space()->setSelected(m_accel.modifiers() & kKeySpaceModifier ? true: false);
#if __APPLE__
cmd()->setSelected(m_accel.modifiers() & kKeyCmdModifier ? true: false);
#else
cmd()->setVisible(false);
#endif
}
void SelectAccelerator::updateAssignedTo()
{
std::string res = "None";
for (Key* key : *KeyboardShortcuts::instance()) {
if (key->hasAccel(m_accel)) {
res = key->triggerString();
break;
}
}
assignedTo()->setText("Assigned to: " + res);
}
} // namespace app

View File

@ -0,0 +1,58 @@
/* Aseprite
* Copyright (C) 2001-2014 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
*/
#ifndef APP_UI_SELECT_ACCELERATOR_H_INCLUDED
#define APP_UI_SELECT_ACCELERATOR_H_INCLUDED
#pragma once
#include "ui/accelerator.h"
#include "generated_select_accelerator.h"
namespace app {
class SelectAccelerator : public app::gen::SelectAccelerator {
public:
explicit SelectAccelerator(const ui::Accelerator& accelerator, bool canDelete);
bool isDeleted() const { return m_deleted; }
bool isModified() const { return m_modified; }
const ui::Accelerator& accel() const { return m_accel; }
private:
void onModifierChange(ui::KeyModifiers modifier, ui::CheckBox* checkbox);
void onAccelChange(const ui::Accelerator* accel);
void onClear();
void onOK();
void onCancel();
void onDelete();
void updateModifiers();
void updateAssignedTo();
class KeyField;
KeyField* m_keyField;
ui::Accelerator m_origAccel;
ui::Accelerator m_accel;
bool m_deleted;
bool m_modified;
};
} // namespace app
#endif

View File

@ -22,6 +22,8 @@
#include "app/modules/gui.h"
#include "app/resource_finder.h"
#include "app/ui/app_menuitem.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/skin/button_icon_impl.h"
#include "app/ui/skin/skin_property.h"
#include "app/ui/skin/skin_slider_property.h"
@ -1242,7 +1244,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev)
{
int scale = jguiscale();
Graphics* g = ev.getGraphics();
MenuItem* widget = static_cast<MenuItem*>(ev.getSource());
AppMenuItem* widget = static_cast<AppMenuItem*>(ev.getSource());
gfx::Rect bounds = widget->getClientBounds();
gfx::Color fg, bg;
int c, bar;
@ -1324,13 +1326,13 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev)
}
}
// Draw the keyboard shortcut
else if (widget->getAccel()) {
else if (widget->getKey() && !widget->getKey()->accels().empty()) {
int old_align = widget->getAlign();
pos = bounds;
pos.w -= widget->child_spacing/4;
std::string buf = widget->getAccel()->toString();
std::string buf = widget->getKey()->accels().front().toString();
widget->setAlign(JI_RIGHT | JI_MIDDLE);
drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0);

View File

@ -33,6 +33,7 @@
#include "app/tools/tool.h"
#include "app/ui/color_button.h"
#include "app/ui/editor/editor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
@ -362,10 +363,10 @@ void StatusBar::showTool(int msecs, tools::Tool* tool)
std::string text = tool->getText();
// Tool shortcut
Accelerator* accel = get_accel_to_change_tool(tool);
if (accel) {
Key* key = KeyboardShortcuts::instance()->tool(tool);
if (key && !key->accels().empty()) {
text += ", Shortcut: ";
text += accel->toString();
text += key->accels().front().toString();
}
// Set text

View File

@ -27,9 +27,9 @@
#include "app/commands/commands.h"
#include "app/modules/editors.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/settings/settings.h"
#include "app/tools/tool_box.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/mini_editor.h"
#include "app/ui/skin/skin_theme.h"
@ -542,10 +542,10 @@ void ToolBar::openTipWindow(int group_index, Tool* tool)
}
// Tool shortcut
Accelerator* accel = get_accel_to_change_tool(tool);
if (accel) {
Key* key = KeyboardShortcuts::instance()->tool(tool);
if (key && !key->accels().empty()) {
tooltip += "\n\nShortcut: ";
tooltip += accel->toString();
tooltip += key->accels().front().toString();
}
}
else if (group_index == ConfigureToolIndex) {

View File

@ -46,4 +46,14 @@ XmlDocumentRef open_xml(const std::string& filename)
return doc;
}
void save_xml(XmlDocumentRef doc, const std::string& filename)
{
FileHandle file(open_file(filename, "wb"));
if (!file)
throw Exception("Error loading file: " + filename);
if (!doc->SaveFile(file))
throw XmlException(doc);
}
} // namespace app

View File

@ -32,6 +32,7 @@ namespace app {
typedef SharedPtr<TiXmlDocument> XmlDocumentRef;
XmlDocumentRef open_xml(const std::string& filename);
void save_xml(XmlDocumentRef doc, const std::string& filename);
} // namespace app

View File

@ -24,26 +24,28 @@
namespace ui {
void Accelerator::addKey(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
Accelerator::Accelerator()
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{
KeyCombo key;
key.modifiers = modifiers;
key.scancode = scancode;
key.unicodeChar = unicodeChar;
m_combos.push_back(key);
}
void Accelerator::addKeysFromString(const std::string& str)
Accelerator::Accelerator(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
: m_modifiers(modifiers)
, m_scancode(scancode)
, m_unicodeChar(unicodeChar)
{
KeyModifiers modifiers = kKeyNoneModifier;
KeyScancode scancode = kKeyNil;
int unicodeChar = 0;
}
Accelerator::Accelerator(const std::string& str)
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{
// Special case: plus sign
if (str == "+") {
addKey(kKeyNoneModifier, kKeyNil, '+');
m_unicodeChar = '+';
return;
}
@ -52,137 +54,143 @@ void Accelerator::addKeysFromString(const std::string& str)
for (std::string tok : tokens) {
tok = base::string_to_lower(tok);
if (scancode == kKeySpace) {
modifiers = (KeyModifiers)((int)modifiers | (int)kKeySpaceModifier);
scancode = kKeyNil;
if (m_scancode == kKeySpace) {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeySpaceModifier);
m_scancode = kKeyNil;
}
// Modifiers
if (tok == "shift") {
modifiers = (KeyModifiers)((int)modifiers | (int)kKeyShiftModifier);
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyShiftModifier);
}
else if (tok == "alt") {
modifiers = (KeyModifiers)((int)modifiers | (int)kKeyAltModifier);
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyAltModifier);
}
else if (tok == "ctrl") {
modifiers = (KeyModifiers)((int)modifiers | (int)kKeyCtrlModifier);
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCtrlModifier);
}
else if (tok == "cmd") {
modifiers = (KeyModifiers)((int)modifiers | (int)kKeyCmdModifier);
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCmdModifier);
}
// Scancode
// word with one character
// Word with one character
else if (tok.size() == 1) {
if ((tok[0] >= 'a') && (tok[0] <= 'z')) {
unicodeChar = tok[0];
m_unicodeChar = tok[0];
}
else {
unicodeChar = tok[0];
m_unicodeChar = tok[0];
}
if ((tok[0] >= 'a') && (tok[0] <= 'z'))
scancode = (KeyScancode)((int)kKeyA + tolower(tok[0]) - 'a');
m_scancode = (KeyScancode)((int)kKeyA + tolower(tok[0]) - 'a');
else if ((tok[0] >= '0') && (tok[0] <= '9'))
scancode = (KeyScancode)((int)kKey0 + tok[0] - '0');
m_scancode = (KeyScancode)((int)kKey0 + tok[0] - '0');
else {
switch (tok[0]) {
case '~': scancode = kKeyTilde; break;
case '-': scancode = kKeyMinus; break;
case '=': scancode = kKeyEquals; break;
case '[': scancode = kKeyOpenbrace; break;
case ']': scancode = kKeyClosebrace; break;
case ';': scancode = kKeyColon; break;
case '\'': scancode = kKeyQuote; break;
case '\\': scancode = kKeyBackslash; break;
case ',': scancode = kKeyComma; break;
case '.': scancode = kKeyStop; break;
case '/': scancode = kKeySlash; break;
case '*': scancode = kKeyAsterisk; break;
case '~': m_scancode = kKeyTilde; break;
case '-': m_scancode = kKeyMinus; break;
case '=': m_scancode = kKeyEquals; break;
case '[': m_scancode = kKeyOpenbrace; break;
case ']': m_scancode = kKeyClosebrace; break;
case ';': m_scancode = kKeyColon; break;
case '\'': m_scancode = kKeyQuote; break;
case '\\': m_scancode = kKeyBackslash; break;
case ',': m_scancode = kKeyComma; break;
case '.': m_scancode = kKeyStop; break;
case '/': m_scancode = kKeySlash; break;
case '*': m_scancode = kKeyAsterisk; break;
}
}
}
/* other ones */
// Other ones
else {
/* F1, F2, ..., F11, F12 */
// F1, F2, ..., F11, F12
if (tok[0] == 'f' && (tok.size() <= 3)) {
int num = strtol(tok.c_str()+1, NULL, 10);
if ((num >= 1) && (num <= 12))
scancode = (KeyScancode)((int)kKeyF1 + num - 1);
m_scancode = (KeyScancode)((int)kKeyF1 + num - 1);
}
else if ((tok == "escape") || (tok == "esc"))
scancode = kKeyEsc;
m_scancode = kKeyEsc;
else if (tok == "backspace")
scancode = kKeyBackspace;
m_scancode = kKeyBackspace;
else if (tok == "tab")
scancode = kKeyTab;
m_scancode = kKeyTab;
else if (tok == "enter")
scancode = kKeyEnter;
m_scancode = kKeyEnter;
else if (tok == "space")
scancode = kKeySpace;
m_scancode = kKeySpace;
else if ((tok == "insert") || (tok == "ins"))
scancode = kKeyInsert;
m_scancode = kKeyInsert;
else if ((tok == "delete") || (tok == "del"))
scancode = kKeyDel;
m_scancode = kKeyDel;
else if (tok == "home")
scancode = kKeyHome;
m_scancode = kKeyHome;
else if (tok == "end")
scancode = kKeyEnd;
m_scancode = kKeyEnd;
else if ((tok == "page up") || (tok == "pgup"))
scancode = kKeyPageUp;
m_scancode = kKeyPageUp;
else if ((tok == "page down") || (tok == "pgdn"))
scancode = kKeyPageDown;
m_scancode = kKeyPageDown;
else if (tok == "left")
scancode = kKeyLeft;
m_scancode = kKeyLeft;
else if (tok == "right")
scancode = kKeyRight;
m_scancode = kKeyRight;
else if (tok == "up")
scancode = kKeyUp;
m_scancode = kKeyUp;
else if (tok == "down")
scancode = kKeyDown;
m_scancode = kKeyDown;
else if (tok == "0 pad")
scancode = kKey0Pad;
m_scancode = kKey0Pad;
else if (tok == "1 pad")
scancode = kKey1Pad;
m_scancode = kKey1Pad;
else if (tok == "2 pad")
scancode = kKey2Pad;
m_scancode = kKey2Pad;
else if (tok == "3 pad")
scancode = kKey3Pad;
m_scancode = kKey3Pad;
else if (tok == "4 pad")
scancode = kKey4Pad;
m_scancode = kKey4Pad;
else if (tok == "5 pad")
scancode = kKey5Pad;
m_scancode = kKey5Pad;
else if (tok == "6 pad")
scancode = kKey6Pad;
m_scancode = kKey6Pad;
else if (tok == "7 pad")
scancode = kKey7Pad;
m_scancode = kKey7Pad;
else if (tok == "8 pad")
scancode = kKey8Pad;
m_scancode = kKey8Pad;
else if (tok == "9 pad")
scancode = kKey9Pad;
m_scancode = kKey9Pad;
else if (tok == "slash pad")
scancode = kKeySlashPad;
m_scancode = kKeySlashPad;
else if (tok == "asterisk")
scancode = kKeyAsterisk;
m_scancode = kKeyAsterisk;
else if (tok == "minus pad")
scancode = kKeyMinusPad;
m_scancode = kKeyMinusPad;
else if (tok == "plus pad")
scancode = kKeyPlusPad;
m_scancode = kKeyPlusPad;
else if (tok == "del pad")
scancode = kKeyDelPad;
m_scancode = kKeyDelPad;
else if (tok == "enter pad")
scancode = kKeyEnterPad;
m_scancode = kKeyEnterPad;
}
}
addKey(modifiers, scancode, unicodeChar);
}
std::string Accelerator::KeyCombo::toString()
bool Accelerator::isEmpty() const
{
return
(m_modifiers == kKeyNoneModifier &&
m_scancode == kKeyNil &&
m_unicodeChar == 0);
}
std::string Accelerator::toString() const
{
// Same order that Allegro scancodes
static const char *table[] = {
static const char* table[] = {
NULL,
"A",
"B",
@ -287,34 +295,29 @@ std::string Accelerator::KeyCombo::toString()
"KEY_COLON2",
"Kanji",
};
static size_t table_size = sizeof(table) / sizeof(table[0]);
std::string buf;
// Shifts
if (this->modifiers & kKeyCtrlModifier) buf += "Ctrl+";
if (this->modifiers & kKeyCmdModifier) buf += "Cmd+";
if (this->modifiers & kKeyAltModifier) buf += "Alt+";
if (this->modifiers & kKeyShiftModifier) buf += "Shift+";
if (this->modifiers & kKeySpaceModifier) buf += "Space+";
if (m_modifiers & kKeyCtrlModifier) buf += "Ctrl+";
if (m_modifiers & kKeyCmdModifier) buf += "Cmd+";
if (m_modifiers & kKeyAltModifier) buf += "Alt+";
if (m_modifiers & kKeyShiftModifier) buf += "Shift+";
if (m_modifiers & kKeySpaceModifier) buf += "Space+";
// Key
if (this->unicodeChar)
buf += (wchar_t)toupper(this->unicodeChar);
else if (this->scancode)
buf += table[this->scancode];
if (m_unicodeChar)
buf += (wchar_t)toupper(m_unicodeChar);
else if (m_scancode && m_scancode > 0 && m_scancode < (int)table_size)
buf += table[m_scancode];
else if (!buf.empty() && buf[buf.size()-1] == '+')
buf.erase(buf.size()-1);
return buf;
}
std::string Accelerator::toString()
{
ASSERT(!m_combos.empty());
return m_combos.front().toString();
}
bool Accelerator::check(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
bool Accelerator::check(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const
{
#ifdef REPORT_KEYS
char buf[256];
@ -373,40 +376,29 @@ bool Accelerator::check(KeyModifiers modifiers, KeyScancode scancode, int unicod
#endif
#ifdef REPORT_KEYS
{
base::UniquePtr<Accelerator> a2(new Accelerator);
a2->addKey(modifiers, scancode, unicodeChar);
buf2 = a2->getString();
}
printf("%3d==%3d %3d==%3d %s==%s ",
m_scancode, scancode,
m_unicodeChar, unicodeChar,
toString().c_str(),
Accelerator(modifiers, scancode, unicodeChar).toString().c_str());
#endif
for (KeyCombos::iterator it = m_combos.begin(), end = m_combos.end();
it != end; ++it) {
if ((m_modifiers == modifiers) &&
((m_scancode != kKeyNil && m_scancode == scancode) ||
(m_unicodeChar && m_unicodeChar == unicodeChar))) {
#ifdef REPORT_KEYS
printf("%3d==%3d %3d==%3d %s==%s ",
it->scancode, scancode, it->unicodeChar, unicodeChar,
it->getString().c_str(), buf2.c_str();
#endif
if ((it->modifiers == modifiers) &&
((it->scancode != kKeyNil && it->scancode == scancode) ||
(it->unicodeChar && it->unicodeChar == unicodeChar))) {
#ifdef REPORT_KEYS
printf("true\n");
#endif
return true;
}
#ifdef REPORT_KEYS
printf("false\n");
printf("true\n");
#endif
return true;
}
#ifdef REPORT_KEYS
printf("false\n");
#endif
return false;
}
bool Accelerator::checkFromAllegroKeyArray()
bool Accelerator::checkFromAllegroKeyArray() const
{
KeyModifiers modifiers = kKeyNoneModifier;
@ -417,14 +409,8 @@ bool Accelerator::checkFromAllegroKeyArray()
if (key[KEY_ALT] ) modifiers = (KeyModifiers)((int)modifiers | (int)kKeyAltModifier);
if (key[KEY_COMMAND ]) modifiers = (KeyModifiers)((int)modifiers | (int)kKeyCmdModifier);
for (KeyCombos::iterator it = m_combos.begin(), end = m_combos.end();
it != end; ++it) {
if ((it->scancode == 0 || key[it->scancode]) &&
(it->modifiers == modifiers)) {
return true;
}
}
return false;
return ((m_scancode == 0 || key[m_scancode]) &&
(m_modifiers == modifiers));
}
} // namespace ui

View File

@ -15,33 +15,42 @@
namespace ui {
class Accelerator
{
class Accelerator {
public:
void addKey(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar);
Accelerator();
Accelerator(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar);
// Convert string like "Ctrl+Q" or "Alt+X" into an accelerator.
explicit Accelerator(const std::string& str);
// Adds keys from strings like "Ctrl+Q" or "Alt+X"
void addKeysFromString(const std::string& str);
bool isEmpty() const;
std::string toString() const;
bool isEmpty() const { return m_combos.empty(); }
std::string toString();
bool check(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const;
bool checkFromAllegroKeyArray() const;
bool check(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar);
bool checkFromAllegroKeyArray();
bool operator==(const Accelerator& other) const {
return
(m_modifiers == other.m_modifiers &&
m_scancode == other.m_scancode &&
m_unicodeChar == other.m_unicodeChar);
}
bool operator!=(const Accelerator& other) const {
return !operator==(other);
}
KeyModifiers modifiers() const { return m_modifiers; }
KeyScancode scancode() const { return m_scancode; }
int unicodeChar() const { return m_unicodeChar; }
private:
struct KeyCombo {
KeyModifiers modifiers;
KeyScancode scancode;
int unicodeChar;
std::string toString();
};
typedef std::vector<KeyCombo> KeyCombos;
KeyCombos m_combos;
KeyModifiers m_modifiers;
KeyScancode m_scancode;
int m_unicodeChar;
};
typedef std::vector<Accelerator> Accelerators;
} // namespace ui
#endif

View File

@ -106,7 +106,7 @@ size_t ListBox::getItemsCount() const
return getChildren().size();
}
/* setup the scroll to center the selected item in the viewport */
// Setup the scroll to center the selected item in the viewport
void ListBox::centerScroll()
{
View* view = View::getView(this);
@ -123,6 +123,23 @@ void ListBox::centerScroll()
}
}
inline bool sort_by_text(Widget* a, Widget* b) {
return a->getText() < b->getText();
}
void ListBox::sortItems()
{
WidgetsList widgets = getChildren();
std::sort(widgets.begin(), widgets.end(), &sort_by_text);
// Remove all children and add then again.
while (!getChildren().empty())
removeChild(getChildren().back());
for (Widget* child : widgets)
addChild(child);
}
bool ListBox::onProcessMessage(Message* msg)
{
switch (msg->type()) {

View File

@ -15,8 +15,7 @@ namespace ui {
class ListItem;
class ListBox : public Widget
{
class ListBox : public Widget {
public:
ListBox();
@ -29,6 +28,7 @@ namespace ui {
size_t getItemsCount() const;
void centerScroll();
void sortItems();
Signal0<void> ChangeSelectedItem;
Signal0<void> DoubleClickItem;

View File

@ -195,7 +195,6 @@ void MenuBar::setExpandOnMouseover(bool state)
MenuItem::MenuItem(const std::string& text)
: Widget(kMenuItemWidget)
{
m_accel = NULL;
m_highlighted = false;
m_submenu = NULL;
m_submenu_menubox = NULL;
@ -206,11 +205,7 @@ MenuItem::MenuItem(const std::string& text)
MenuItem::~MenuItem()
{
if (m_accel)
delete m_accel;
if (m_submenu)
delete m_submenu;
delete m_submenu;
}
Menu* MenuBox::getMenu()
@ -233,11 +228,6 @@ Menu* MenuItem::getSubmenu()
return m_submenu;
}
Accelerator* MenuItem::getAccel()
{
return m_accel;
}
void MenuBox::setMenu(Menu* menu)
{
if (Menu* oldMenu = getMenu())
@ -262,21 +252,6 @@ void MenuItem::setSubmenu(Menu* menu)
}
}
/**
* Changes the keyboard shortcuts (accelerators) for the specified
* widget (a menu-item).
*
* @warning The specified @a accel will be freed automatically when
* the menu-item is deleted.
*/
void MenuItem::setAccel(Accelerator* accel)
{
if (m_accel)
delete m_accel;
m_accel = accel;
}
bool MenuItem::isHighlighted() const
{
return m_highlighted;
@ -909,10 +884,6 @@ void MenuItem::onPreferredSize(PreferredSizeEvent& ev)
+ this->border_width.t
+ getTextHeight()
+ this->border_width.b;
if (m_accel && !m_accel->isEmpty()) {
size.w += Graphics::measureUIStringLength(m_accel->toString().c_str(), getFont());
}
}
ev.setPreferredSize(size);

View File

@ -15,7 +15,6 @@
namespace ui {
class Accelerator;
class MenuItem;
class Timer;
struct MenuBaseData;
@ -104,9 +103,6 @@ namespace ui {
Menu* getSubmenu();
void setSubmenu(Menu* submenu);
Accelerator* getAccel();
void setAccel(Accelerator* accel);
bool isHighlighted() const;
void setHighlighted(bool state);
@ -133,15 +129,15 @@ namespace ui {
virtual void onPreferredSize(PreferredSizeEvent& ev) override;
virtual void onClick();
private:
bool inBar();
private:
void openSubmenu(bool select_first);
void closeSubmenu(bool last_of_close_chain);
void startTimer();
void stopTimer();
void executeClick();
Accelerator* m_accel; // Hot-key
bool m_highlighted; // Is it highlighted?
Menu* m_submenu; // The sub-menu
MenuBox* m_submenu_menubox; // The opened menubox for this menu-item