1 /*
2   FXiTe - The Free eXtensIble Text Editor
3   Copyright (c) 2009-2013 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4 
5   This program is free software; you can redistribute it and/or modify it
6   under the terms of the GNU General Public License version 3 as
7   published by the Free Software Foundation.
8 
9   This software is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 #include <fx.h>
20 #include <fxkeys.h>
21 
22 #ifdef WIN32
23 # include <windows.h>
24 #endif
25 
26 #include "appwin.h"
27 #include "menuspec.h"
28 #include "intl.h"
29 
30 #include "cmd_utils.h"
31 
32 
33 static const char* dont_freeze_me = "don't taze me, bro!";
34 
35 
Freeze(FXWindow * win,bool frozen)36 void CommandUtils::Freeze(FXWindow*win, bool frozen)
37 {
38   FXWindow*w;
39   for (w=win->getFirst(); w; w=w->getNext()) {
40     if (w->getUserData()==dont_freeze_me) { continue; }
41     if (frozen) {
42       w->disable();
43       w->repaint();
44     } else {w->enable();}
45     Freeze(w,frozen);
46   }
47 }
48 
49 
50 
DontFreezeMe()51 const char* CommandUtils::DontFreezeMe()
52 {
53   return dont_freeze_me;
54 }
55 
56 
57 
SetKillKey(FXHotKey k)58 void CommandUtils::SetKillKey(FXHotKey k)
59 {
60   killkey=k;
61 #ifdef WIN32
62   winkey=VkKeyScan(FXSELID(killkey));
63 #endif
64 }
65 
66 
67 
InitKillKey()68 void CommandUtils::InitKillKey()
69 {
70   MenuSpec*killcmd=MenuMgr::LookupMenu(TopWindow::ID_KILL_COMMAND);
71   SetKillKey(parseAccel(killcmd->accel));
72   if (killkey && FXSELID(killkey)) {
73     temp_accels=new FXAccelTable();
74     temp_accels->addAccel(killkey,this,FXSEL(SEL_COMMAND,TopWindow::ID_KILL_COMMAND),0);
75    } else {
76     FXMessageBox::warning(tw->getApp(), MBOX_OK, _("Configuration error"),
77       "%s \"%s\"\n%s",
78       _("Failed to parse accelerator for"),
79       killcmd->pref,
80       _("disabling support for macros and external commands.")
81       );
82     temp_accels=NULL;
83   }
84 }
85 
86 
87 
CommandUtils(TopWindowBase * win)88 CommandUtils::CommandUtils(TopWindowBase*win)
89 {
90   saved_accels=NULL;
91   temp_accels=NULL;
92   command_busy=false;
93   tw=(TopWindow*)win;
94   app=tw->getApp();
95   InitKillKey();
96 }
97 
98 
99 
~CommandUtils()100 CommandUtils::~CommandUtils()
101 {
102   delete temp_accels;
103   delete saved_accels;
104 }
105 
106 
107 
SetKillCommandAccelKey(FXHotKey acckey)108 void CommandUtils::SetKillCommandAccelKey(FXHotKey acckey)
109 {
110   SetKillKey(acckey);
111   delete temp_accels;
112   temp_accels=new FXAccelTable();
113   temp_accels->addAccel(acckey,this,FXSEL(SEL_COMMAND,TopWindow::ID_KILL_COMMAND),0);
114 }
115 
116 
117 
DisableUI(bool disabled)118 void CommandUtils::DisableUI(bool disabled)
119 {
120   if (tw->Destroying()) { return; }
121   if (disabled) {
122     saved_accels=tw->getAccelTable();
123     tw->setAccelTable(temp_accels);
124     temp_accels=NULL;
125   } else {
126     temp_accels=tw->getAccelTable();
127     tw->setAccelTable(saved_accels);
128     saved_accels=NULL;
129   }
130   CommandUtils::Freeze(tw,disabled);
131 }
132 
133 
134 
IsCommandReady()135 bool CommandUtils::IsCommandReady()
136 {
137   if (command_busy) {
138     FXMessageBox::error(tw, MBOX_OK, _("Command error"),
139       _("Multiple commands cannot be executed at the same time."));
140     return false;
141   }
142   if (!temp_accels) {
143     FXMessageBox::error(tw, MBOX_OK, _("Command support disabled"),
144       _("Support for running macros and external commands has been\n"
145         "disabled, because the interrupt key sequence is invalid.\n\n"
146         "To fix this, go to:\n"
147         "  Edit->Preferences->Keybindings\n"
148         "and enter a valid setting for \"%s\""),
149       MenuMgr::LookupMenu(TopWindow::ID_KILL_COMMAND)->pref
150     );
151     return false;
152   }
153   return true;
154 }
155 
156 
157 
SetShellEnv(const char * file,long line)158 void CommandUtils::SetShellEnv(const char*file, long line)
159 {
160   char linenum[8]="\0\0\0\0\0\0\0";
161   snprintf(linenum,sizeof(linenum)-1, "%ld", line+1);
162 #ifdef WIN32
163   FXSystem::setEnvironment("l",linenum);
164   FXSystem::setEnvironment("f",file);
165 #else
166   setenv("l",linenum,1);
167   setenv("f",file,1);
168 #endif
169 }
170 
171 
172 
FixUpCmdLineEnv(const FXString & command)173 const FXString CommandUtils::FixUpCmdLineEnv(const FXString&command)
174 {
175 #ifdef WIN32
176   FXString cmd=command;
177   cmd.substitute("%F%", FXSystem::getEnvironment("f"), true);
178   cmd.substitute("%f%", FXSystem::getEnvironment("f"), true);
179   cmd.substitute("%L%", FXSystem::getEnvironment("l"), true);
180   cmd.substitute("%l%", FXSystem::getEnvironment("l"), true);
181   return cmd;
182 #else
183   return command;
184 #endif
185 }
186 
187 
188 
189 #ifdef WIN32
190 # define kill_key_down()  ( GetKeyState(winkey) & 0x8000 )
191 # define ctrl_key_down()  ( GetKeyState(VK_CONTROL)   & 0x8000 )
192 # define alt_key_down()   ( GetKeyState(VK_MENU)      & 0x8000 )
193 # define shift_key_down() ( GetKeyState(VK_SHIFT)     & 0x8000 )
194 #else
195 # define kill_key_down()  ( app->getKeyState(FXSELID(killkey)) )
196 # define ctrl_key_down()  ( app->getKeyState(KEY_Control_L) || app->getKeyState(KEY_Control_R) )
197 # define alt_key_down()   ( app->getKeyState(KEY_Alt_L)     || app->getKeyState(KEY_Alt_R) )
198 # define shift_key_down() ( app->getKeyState(KEY_Shift_L)   || app->getKeyState(KEY_Shift_R) )
199 #endif
200 
201 /*
202   Usually, the application will catch the kill command key sequence by itself,
203   but if the event queue gets really full e.g. when appending large amounts
204   of data to the output window, the key event may get buried underneath
205   everything else that's happening. This function "manually" checks for
206   the key sequence and returns true if the user is trying to cancel.
207 */
IsMacroCancelled(bool & command_timeout)208 bool CommandUtils::IsMacroCancelled(bool &command_timeout)
209 {
210   if (command_timeout) { return true; }
211   if (kill_key_down()) {
212     FXushort mods=FXSELTYPE(killkey);
213     if ( (mods&CONTROLMASK) && (!ctrl_key_down())  ) { return false; }
214     if ( (mods&SHIFTMASK)   && (!shift_key_down()) ) { return false; }
215     if ( (mods&ALTMASK)     && (!alt_key_down())   ) { return false; }
216     command_timeout=true;
217   }
218   return command_timeout;
219 }
220 
221