Fix hang if we press Cmd-S two or more times quickly to launch File > Save

With this we avoid to unintentional execute two or more times a
command when we are already in a command execution.
This commit is contained in:
David Capello 2019-06-03 22:57:20 -03:00
parent 2ab27ade9b
commit 79795b97a9
14 changed files with 49 additions and 39 deletions

View File

@ -730,7 +730,7 @@ void AppMenus::createNativeMenus()
about.execute = [native]{ about.execute = [native]{
if (can_call_global_shortcut(&native)) { if (can_call_global_shortcut(&native)) {
Command* cmd = Commands::instance()->byId(CommandId::About()); Command* cmd = Commands::instance()->byId(CommandId::About());
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
} }
}; };
about.validate = [native](os::MenuItem* item){ about.validate = [native](os::MenuItem* item){
@ -743,7 +743,7 @@ void AppMenus::createNativeMenus()
preferences.execute = [native]{ preferences.execute = [native]{
if (can_call_global_shortcut(&native)) { if (can_call_global_shortcut(&native)) {
Command* cmd = Commands::instance()->byId(CommandId::Options()); Command* cmd = Commands::instance()->byId(CommandId::Options());
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
} }
}; };
preferences.validate = [native](os::MenuItem* item){ preferences.validate = [native](os::MenuItem* item){

View File

@ -18,7 +18,9 @@
#include "app/console.h" #include "app/console.h"
#include "app/doc.h" #include "app/doc.h"
#include "app/site.h" #include "app/site.h"
#include "base/scoped_value.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "ui/system.h"
#include <algorithm> #include <algorithm>
#include <stdexcept> #include <stdexcept>
@ -93,24 +95,33 @@ void Context::notifyActiveSiteChanged()
notify_observers<const Site&>(&ContextObserver::onActiveSiteChange, site); notify_observers<const Site&>(&ContextObserver::onActiveSiteChange, site);
} }
void Context::executeCommand(const char* commandName) void Context::executeCommandFromMenuOrShortcut(Command* command, const Params& params)
{ {
Command* cmd = Commands::instance()->byId(commandName); ui::assert_ui_thread();
if (cmd)
executeCommand(cmd); // With this we avoid executing a command when we are inside another
else // command (e.g. if we press Cmd-S quickly the program can enter two
throw std::runtime_error("Invalid command name"); // times in the File > Save command and hang).
static Command* executingCommand = nullptr;
if (executingCommand) { // Ignore command execution
LOG(VERBOSE, "CTXT: Ignoring command %s because we are inside %s\n",
command->id().c_str(), executingCommand->id().c_str());
return;
}
base::ScopedValue<Command*> commandGuard(executingCommand,
command, nullptr);
executeCommand(command, params);
} }
void Context::executeCommand(Command* command, const Params& params) void Context::executeCommand(Command* command, const Params& params)
{ {
Console console; ASSERT(command);
if (!command)
ASSERT(command != NULL);
if (command == NULL)
return; return;
LOG(VERBOSE) << "CTXT: Executing command " << command->id() << "\n"; Console console;
LOG(VERBOSE, "CTXT: Executing command %s\n", command->id().c_str());
try { try {
m_flags.update(this); m_flags.update(this);
@ -122,14 +133,14 @@ void Context::executeCommand(Command* command, const Params& params)
BeforeCommandExecution(ev); BeforeCommandExecution(ev);
if (ev.isCanceled()) { if (ev.isCanceled()) {
LOG(VERBOSE) << "CTXT: Command " << command->id() << " was canceled/simulated.\n"; LOG(VERBOSE, "CTXT: Command %s was canceled/simulated.\n", command->id().c_str());
} }
else if (command->isEnabled(this)) { else if (command->isEnabled(this)) {
command->execute(this); command->execute(this);
LOG(VERBOSE) << "CTXT: Command " << command->id() << " executed successfully\n"; LOG(VERBOSE, "CTXT: Command %s executed successfully\n", command->id().c_str());
} }
else { else {
LOG(VERBOSE) << "CTXT: Command " << command->id() << " is disabled\n"; LOG(VERBOSE, "CTXT: Command %s is disabled\n", command->id().c_str());
} }
AfterCommandExecution(ev); AfterCommandExecution(ev);
@ -139,20 +150,19 @@ void Context::executeCommand(Command* command, const Params& params)
app_rebuild_documents_tabs(); app_rebuild_documents_tabs();
} }
catch (base::Exception& e) { catch (base::Exception& e) {
LOG(ERROR) << "CTXT: Exception caught executing " << command->id() << " command\n" LOG(ERROR, "CTXT: Exception caught executing %s command\n%s\n",
<< e.what() << "\n"; command->id().c_str(), e.what());
Console::showException(e); Console::showException(e);
} }
catch (std::exception& e) { catch (std::exception& e) {
LOG(ERROR) << "CTXT: std::exception caught executing " << command->id() << " command\n" LOG(ERROR, "CTXT: std::exception caught executing %s command\n%s\n",
<< e.what() << "\n"; command->id().c_str(), e.what());
console.printf("An error ocurred executing the command.\n\nDetails:\n%s", e.what()); console.printf("An error ocurred executing the command.\n\nDetails:\n%s", e.what());
} }
#ifdef NDEBUG #ifdef NDEBUG
catch (...) { catch (...) {
LOG(ERROR) << "CTXT: Unknown exception executing " << command->id() << " command\n"; LOG(ERROR, "CTXT: Unknown exception executing %s command\n",
command->id().c_str());
console.printf("An unknown error ocurred executing the command.\n" console.printf("An unknown error ocurred executing the command.\n"
"Please save your work, close the program, try it\n" "Please save your work, close the program, try it\n"

View File

@ -88,7 +88,7 @@ namespace app {
bool hasModifiedDocuments() const; bool hasModifiedDocuments() const;
void notifyActiveSiteChanged(); void notifyActiveSiteChanged();
void executeCommand(const char* commandName); void executeCommandFromMenuOrShortcut(Command* command, const Params& params = Params());
virtual void executeCommand(Command* command, const Params& params = Params()); virtual void executeCommand(Command* command, const Params& params = Params());
virtual DocView* getFirstDocView(Doc* document) const { virtual DocView* getFirstDocView(Doc* document) const {

View File

@ -502,7 +502,7 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
if (getForegroundWindow() == App::instance()->mainWindow()) { if (getForegroundWindow() == App::instance()->mainWindow()) {
// OK, so we can execute the command represented // OK, so we can execute the command represented
// by the pressed-key in the message... // by the pressed-key in the message...
UIContext::instance()->executeCommand( UIContext::instance()->executeCommandFromMenuOrShortcut(
command, key->params()); command, key->params());
return true; return true;
} }

View File

@ -141,7 +141,7 @@ void AppMenuItem::onClick()
UIContext* context = UIContext::instance(); UIContext* context = UIContext::instance();
if (m_command->isEnabled(context)) { if (m_command->isEnabled(context)) {
context->executeCommand(m_command, params); context->executeCommandFromMenuOrShortcut(m_command, params);
} }
} }
} }

View File

@ -232,7 +232,7 @@ bool DrawingState::onKeyDown(Editor* editor, KeyMessage* msg)
->getCommandFromKeyMessage(msg, &command, &params)) { ->getCommandFromKeyMessage(msg, &command, &params)) {
// We accept zoom commands. // We accept zoom commands.
if (command->id() == CommandId::Zoom()) { if (command->id() == CommandId::Zoom()) {
UIContext::instance()->executeCommand(command, params); UIContext::instance()->executeCommandFromMenuOrShortcut(command, params);
return true; return true;
} }
} }

View File

@ -436,7 +436,7 @@ bool MovingPixelsState::onKeyDown(Editor* editor, KeyMessage* msg)
// The escape key drop pixels and deselect the mask. // The escape key drop pixels and deselect the mask.
if (msg->scancode() == kKeyEsc) { // TODO make this key customizable if (msg->scancode() == kKeyEsc) { // TODO make this key customizable
Command* cmd = Commands::instance()->byId(CommandId::DeselectMask()); Command* cmd = Commands::instance()->byId(CommandId::DeselectMask());
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
} }
return true; return true;

View File

@ -406,7 +406,7 @@ bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg)
else if (int(editor->getToolLoopModifiers()) & int(tools::ToolLoopModifiers::kIntersectSelection)) else if (int(editor->getToolLoopModifiers()) & int(tools::ToolLoopModifiers::kIntersectSelection))
params.set("mode", "intersect"); params.set("mode", "intersect");
UIContext::instance()->executeCommand(selectTileCmd, params); UIContext::instance()->executeCommandFromMenuOrShortcut(selectTileCmd, params);
return true; return true;
} }
// Show slice properties when we double-click it // Show slice properties when we double-click it
@ -416,7 +416,7 @@ bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg)
Command* cmd = Commands::instance()->byId(CommandId::SliceProperties()); Command* cmd = Commands::instance()->byId(CommandId::SliceProperties());
Params params; Params params;
params.set("id", base::convert_to<std::string>(hit.slice()->id()).c_str()); params.set("id", base::convert_to<std::string>(hit.slice()->id()).c_str());
UIContext::instance()->executeCommand(cmd, params); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd, params);
return true; return true;
} }
} }

View File

@ -137,13 +137,13 @@ void HomeView::onWorkspaceViewSelected()
void HomeView::onNewFile() void HomeView::onNewFile()
{ {
Command* command = Commands::instance()->byId(CommandId::NewFile()); Command* command = Commands::instance()->byId(CommandId::NewFile());
UIContext::instance()->executeCommand(command); UIContext::instance()->executeCommandFromMenuOrShortcut(command);
} }
void HomeView::onOpenFile() void HomeView::onOpenFile()
{ {
Command* command = Commands::instance()->byId(CommandId::OpenFile()); Command* command = Commands::instance()->byId(CommandId::OpenFile());
UIContext::instance()->executeCommand(command); UIContext::instance()->executeCommandFromMenuOrShortcut(command);
} }
void HomeView::onResize(ui::ResizeEvent& ev) void HomeView::onResize(ui::ResizeEvent& ev)

View File

@ -452,7 +452,7 @@ void MainWindow::onTabsContainerDoubleClicked(Tabs* tabs)
Doc* oldDoc = UIContext::instance()->activeDocument(); Doc* oldDoc = UIContext::instance()->activeDocument();
Command* command = Commands::instance()->byId(CommandId::NewFile()); Command* command = Commands::instance()->byId(CommandId::NewFile());
UIContext::instance()->executeCommand(command); UIContext::instance()->executeCommandFromMenuOrShortcut(command);
Doc* newDoc = UIContext::instance()->activeDocument(); Doc* newDoc = UIContext::instance()->activeDocument();
if (newDoc != oldDoc) { if (newDoc != oldDoc) {

View File

@ -111,7 +111,7 @@ void PalettePopup::onLoadPal()
SetPaletteCommand* cmd = static_cast<SetPaletteCommand*>( SetPaletteCommand* cmd = static_cast<SetPaletteCommand*>(
Commands::instance()->byId(CommandId::SetPalette())); Commands::instance()->byId(CommandId::SetPalette()));
cmd->setPalette(palette); cmd->setPalette(palette);
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
m_paletteListBox.requestFocus(); m_paletteListBox.requestFocus();
} }

View File

@ -308,7 +308,7 @@ void RecentFilesListBox::onClick(const std::string& path)
Command* command = Commands::instance()->byId(CommandId::OpenFile()); Command* command = Commands::instance()->byId(CommandId::OpenFile());
Params params; Params params;
params.set("filename", path.c_str()); params.set("filename", path.c_str());
UIContext::instance()->executeCommand(command, params); UIContext::instance()->executeCommandFromMenuOrShortcut(command, params);
} }
void RecentFilesListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths, void RecentFilesListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
@ -346,7 +346,7 @@ void RecentFoldersListBox::onClick(const std::string& path)
Command* command = Commands::instance()->byId(CommandId::OpenFile()); Command* command = Commands::instance()->byId(CommandId::OpenFile());
Params params; Params params;
params.set("folder", path.c_str()); params.set("folder", path.c_str());
UIContext::instance()->executeCommand(command, params); UIContext::instance()->executeCommandFromMenuOrShortcut(command, params);
} }
void RecentFoldersListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths, void RecentFoldersListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,

View File

@ -515,7 +515,7 @@ public:
Command* cmd = Commands::instance()->byId(CommandId::GotoFrame()); Command* cmd = Commands::instance()->byId(CommandId::GotoFrame());
Params params; Params params;
params.set("frame", text().c_str()); params.set("frame", text().c_str());
UIContext::instance()->executeCommand(cmd, params); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd, params);
// Select the text again // Select the text again
selectAllText(); selectAllText();
@ -813,7 +813,7 @@ void StatusBar::onPixelFormatChanged(DocEvent& ev)
void StatusBar::newFrame() void StatusBar::newFrame()
{ {
Command* cmd = Commands::instance()->byId(CommandId::NewFrame()); Command* cmd = Commands::instance()->byId(CommandId::NewFrame());
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
} }
void StatusBar::onChangeZoom(const render::Zoom& zoom) void StatusBar::onChangeZoom(const render::Zoom& zoom)

View File

@ -83,7 +83,7 @@ void AniControls::onClickButton()
Command* cmd = Commands::instance()->byId(getCommandId(item)); Command* cmd = Commands::instance()->byId(getCommandId(item));
if (cmd) { if (cmd) {
UIContext::instance()->executeCommand(cmd); UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
updateUsingEditor(current_editor); updateUsingEditor(current_editor);
} }
} }