1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #include "CKeyBindingsDialog.h"
22 
23 #include "settings.h"
24 #include "CStatusComm.h"
25 
26 #include <ctype.h>
27 #include <stdio.h>
28 
29 #include <CNestedDataFile/CNestedDataFile.h>
30 
31 CKeyBindingsDialog *gKeyBindingsDialog=NULL;
32 
33 
34 #if REZ_FOX_VERSION<10501
35 FXString unparseAccel(FXHotKey key);
36 #endif
37 
parseItemText(const string itemText,string & name,string & key)38 void parseItemText(const string itemText,string &name,string &key)
39 {
40 	size_t tab_pos=itemText.find("\t");
41 	name=itemText.substr(0,tab_pos);
42 	key=itemText.substr(tab_pos+1);
43 }
44 
45 // -----------------------------------------
46 
47 class CAssignPopup : public FXDialogBox
48 {
49 	FXDECLARE(CAssignPopup);
50 public:
CAssignPopup(FXWindow * owner)51 	CAssignPopup(FXWindow *owner) :
52 		FXDialogBox(owner,_("Assign Hotkey"),DECOR_TITLE|DECOR_BORDER, 200,50)
53 	{
54 
55 		FXVerticalFrame *t=new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 5,5,5,5, 5,5);
56 
57 		new FXLabel(t,_("Press a Key Combination"),NULL, LABEL_NORMAL|LAYOUT_CENTER_X);
58 		new FXButton(t,_("Assign No Key"),NULL,this,ID_NOKEY_BUTTON, BUTTON_NORMAL|LAYOUT_CENTER_X);
59 		new FXButton(t,_("Cancel"),NULL,this,ID_CANCEL, BUTTON_NORMAL|LAYOUT_CENTER_X);
60 	}
61 
~CAssignPopup()62 	virtual ~CAssignPopup()
63 	{
64 	}
65 
66 	FXuint key;
showIt()67 	FXuint showIt()
68 	{
69 		if(execute())
70 			return key;
71 		return 0;
72 	}
73 
onNoKeyButton(FXObject * sender,FXSelector sel,void * ptr)74 	long onNoKeyButton(FXObject *sender,FXSelector sel,void *ptr)
75 	{
76 		key=1;
77 		FXDialogBox::handle(this,MKUINT(ID_ACCEPT,SEL_COMMAND),ptr);
78 		return 1;
79 	}
80 
onKeyRelease(FXObject * sender,FXSelector sel,void * ptr)81 	long onKeyRelease(FXObject *sender,FXSelector sel,void *ptr)
82 	{
83 		FXEvent *ev=(FXEvent *)ptr;
84 		if(ev->code>=KEY_Shift_L && ev->code<=KEY_Hyper_R)
85 		{ // in fox these are contiguously defined, and we won't get key release events for these keys.. they're just modifiers
86 			return 1;
87 		}
88 
89 		key=MKUINT(tolower(ev->code),ev->state);
90 		FXDialogBox::handle(this,MKUINT(ID_ACCEPT,SEL_COMMAND),ptr);
91 		return 1;
92 	}
93 
94 	enum
95 	{
96 		ID_NOKEY_BUTTON=FXDialogBox::ID_LAST,
97 		ID_LAST
98 	};
99 
CAssignPopup()100 	CAssignPopup() {}
101 
102 };
103 
104 FXDEFMAP(CAssignPopup) CAssignPopupMap[]=
105 {
106 //	Message_Type			ID					Message_Handler
107 	FXMAPFUNC(SEL_COMMAND,		CAssignPopup::ID_NOKEY_BUTTON,		CAssignPopup::onNoKeyButton),
108 	FXMAPFUNC(SEL_KEYRELEASE,	0,					CAssignPopup::onKeyRelease),
109 };
110 
111 FXIMPLEMENT(CAssignPopup,FXDialogBox,CAssignPopupMap,ARRAYNUMBER(CAssignPopupMap))
112 
113 // -----------------------------------------
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 FXDEFMAP(CKeyBindingsDialog) CKeyBindingsDialogMap[]=
124 {
125 //	Message_Type			ID					Message_Handler
126 	FXMAPFUNC(SEL_DOUBLECLICKED,	CKeyBindingsDialog::ID_HOTKEY_LIST,	CKeyBindingsDialog::onDefineKeyBinding),
127 	FXMAPFUNC(SEL_COMMAND,		CKeyBindingsDialog::ID_ASSIGN_BUTTON,	CKeyBindingsDialog::onDefineKeyBinding)
128 };
129 
130 
FXIMPLEMENT(CKeyBindingsDialog,FXModalDialogBox,CKeyBindingsDialogMap,ARRAYNUMBER (CKeyBindingsDialogMap))131 FXIMPLEMENT(CKeyBindingsDialog,FXModalDialogBox,CKeyBindingsDialogMap,ARRAYNUMBER(CKeyBindingsDialogMap))
132 
133 
134 FXint sortfunc(const FXIconItem *a,const FXIconItem *b)
135 {
136 	return strcasecmp(a->getText().text(), b->getText().text());
137 }
138 
139 
140 // ----------------------------------------
141 
CKeyBindingsDialog(FXWindow * mainWindow)142 CKeyBindingsDialog::CKeyBindingsDialog(FXWindow *mainWindow) :
143 	FXModalDialogBox(mainWindow,N_("Hot Keys"),600,550,FXModalDialogBox::ftVertical)
144 {
145 
146 	assignPopup=new CAssignPopup(this);
147 
148 	new FXLabel(getFrame(),_("Assign Hot Keys"));
149 	//new FXHorizontalSeparator(getFrame(),SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_BOTTOM);
150 
151 	FXPacker *t;
152 
153 	// build hot keys list
154 	t=new FXPacker(getFrame(),LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 4,4,2,3, 0,0);
155 		t=new FXPacker(t,LAYOUT_FILL_X|LAYOUT_FILL_Y | FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0, 0,0);
156 			hotkeyList=new FXIconList(t,this,ID_HOTKEY_LIST,HSCROLLER_NEVER|ICONLIST_BROWSESELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y);
157 
158 			/*
159 				soundListFont=getApp()->getNormalFont();
160 				shuttleFont->getFontDesc(d);
161 				d.weight=FONTWEIGHT_NORMAL;
162 				d.size=80;
163 				soundListFont=new FXFont(getApp(),d);
164 
165 				soundList->setFont(soundListFont);
166 
167 				soundListHeaderFont=getApp()->getNormalFont();
168 				shuttleFont->getFontDesc(d);
169 				d.weight=FONTWEIGHT_BOLD;
170 				d.size=80;
171 				soundListHeaderFont=new FXFont(getApp(),d);
172 			*/
173 
174 				//soundList->getHeader()->setFont(soundListHeaderFont);
175 				hotkeyList->getHeader()->setPadLeft(2);
176 				hotkeyList->getHeader()->setPadRight(2);
177 				hotkeyList->getHeader()->setPadTop(0);
178 				hotkeyList->getHeader()->setPadBottom(0);
179 
180 				hotkeyList->appendHeader(_("Action"),NULL,400);
181 				hotkeyList->appendHeader(_("Hot Key"),NULL,9999);
182 
183 				hotkeyList->setSortFunc(sortfunc);
184 
185 	new FXButton(getFrame(),_("Assign\tOr Double-Click An Item"),NULL,this,ID_ASSIGN_BUTTON);
186 
187 }
188 
~CKeyBindingsDialog()189 CKeyBindingsDialog::~CKeyBindingsDialog()
190 {
191 	delete assignPopup;
192 }
193 
onDefineKeyBinding(FXObject * sender,FXSelector sel,void * ptr)194 long CKeyBindingsDialog::onDefineKeyBinding(FXObject *sender,FXSelector sel,void *ptr)
195 {
196 	FXint index=hotkeyList->getCurrentItem();
197 	if(index>=0)
198 	{
199 		string name,key;
200 		parseItemText(hotkeyList->getItemText(index).text(),name,key);
201 
202 		FXuint keycode=assignPopup->showIt();
203 		if(keycode>0)
204 		{
205 			if(keycode==1)
206 			{ // special return value meaning to unassign
207 				key="";
208 			}
209 			else
210 			{
211 				string tmp_key=unparseAccel(keycode).text();
212 				if(tmp_key=="")
213 					Error(_("Unhandled Key Combination"));
214 				else
215 				{
216 					// verify if it's already been assigned
217 					for(FXint t=0;t<hotkeyList->getNumItems();t++)
218 					{
219 						if(t==index)
220 							continue; // skip the one we're assigning to
221 
222 						string name,key;
223 						parseItemText(hotkeyList->getItemText(t).text(),name,key);
224 
225 						if(key==tmp_key)
226 						{
227 							if(Question("'"+tmp_key+"'"+_(" is already assigned to: ")+name+"\n\n"+_("Do you want to override the existing key assignment?"),yesnoQues)==yesAns)
228 							{
229 								hotkeyList->setItemText(t,name.c_str());
230 								break;
231 							}
232 							else
233 								return 1; // cancel the assignment
234 						}
235 					}
236 
237 					// ok make the assignment
238 					key=tmp_key;
239 				}
240 			}
241 
242 			hotkeyList->setItemText(index,(name+"\t"+key).c_str());
243 		}
244 
245 	}
246 
247 	return 1;
248 }
249 
showIt(const map<string,FXMenuCommand * > & keyBindingRegistry)250 bool CKeyBindingsDialog::showIt(const map<string,FXMenuCommand *> &keyBindingRegistry)
251 {
252 	hotkeyList->clearItems();
253 
254 	for(map<string,FXMenuCommand *>::const_iterator i=keyBindingRegistry.begin(); i!=keyBindingRegistry.end(); i++)
255 	{
256 		const string name=i->first;
257 
258 		string key="";
259 		if(gKeyBindingsStore->keyExists(name))
260 			key=gKeyBindingsStore->getValue<string>(name);
261 
262 		hotkeyList->appendItem( (i->first+"\t"+key).c_str() );
263 	}
264 
265 	hotkeyList->sortItems();
266 
267 	if(execute())
268 	{
269 		for(FXint t=0;t<hotkeyList->getNumItems();t++)
270 		{
271 			string name,key;
272 			parseItemText(hotkeyList->getItemText(t).text(),name,key);
273 
274 			gKeyBindingsStore->setValue<string>(name,key);
275 		}
276 		gKeyBindingsStore->save();
277 
278 
279 		return true;
280 	}
281 	return false;
282 }
283 
284 #if REZ_FOX_VERSION<10501
fxunparseAccel(FXHotKey key)285 FXString fxunparseAccel(FXHotKey key)
286 {
287 	FXString s;
288 
289 	FXuint code=key&0xffff;
290 	FXuint mods=(key&0xffff0000)>>16;
291 
292 	// handle modifier keys
293 	if(mods&CONTROLMASK)
294 		s+="ctrl+";
295 	if(mods&ALTMASK)
296 		s+="alt+";
297 	if(mods&SHIFTMASK)
298 		s+="shift+";
299 	if(mods&METAMASK)
300 		s+="meta+";
301 
302 
303 	// handle some special keys
304 	switch(code)
305 	{
306 		case KEY_Home:
307 			s+="Home";
308 			break;
309 
310 		case KEY_End:
311 			s+="End";
312 			break;
313 
314 		case KEY_Page_Up:
315 			s+="PgUp";
316 			break;
317 
318 		case KEY_Page_Down:
319 			s+="PgDn";
320 			break;
321 
322 		case KEY_Left:
323 			s+="Left";
324 			break;
325 
326 		case KEY_Right:
327 			s+="Right";
328 			break;
329 
330 		case KEY_Up:
331 			s+="Up";
332 			break;
333 
334 		case KEY_Down:
335 			s+="Down";
336 			break;
337 
338 		case KEY_Insert:
339 			s+="Ins";
340 			break;
341 
342 		case KEY_Delete:
343 			s+="Del";
344 			break;
345 
346 		case KEY_Escape:
347 			s+="Esc";
348 			break;
349 
350 		case KEY_Tab:
351 			s+="Tab";
352 			break;
353 
354 		case KEY_Return:
355 			s+="Return";
356 			break;
357 
358 		case KEY_BackSpace:
359 			s+="Back";
360 			break;
361 
362 		case KEY_space:
363 			s+="Space";
364 			break;
365 
366 		case KEY_F1:
367 		case KEY_F2:
368 		case KEY_F3:
369 		case KEY_F4:
370 		case KEY_F5:
371 		case KEY_F6:
372 		case KEY_F7:
373 		case KEY_F8:
374 		case KEY_F9:
375 		case KEY_F10:
376 		case KEY_F11:
377 		case KEY_F12:
378 		case KEY_F13:
379 		case KEY_F14:
380 		case KEY_F15:
381 		case KEY_F16:
382 		case KEY_F17:
383 		case KEY_F18:
384 		case KEY_F19:
385 		case KEY_F20:
386 		case KEY_F21:
387 		case KEY_F22:
388 		case KEY_F23:
389 		case KEY_F24:
390 		case KEY_F25:
391 		case KEY_F26:
392 		case KEY_F27:
393 		case KEY_F28:
394 		case KEY_F29:
395 		case KEY_F30:
396 		case KEY_F31:
397 		case KEY_F32:
398 		case KEY_F33:
399 		case KEY_F34:
400 		case KEY_F35:
401 		{
402 			char buffer[64];
403 			sprintf(buffer,"F%d",code-KEY_F1+1);
404 			s+=buffer;
405 		}
406 			break;
407 
408 		default:
409 			if(isprint(code))
410 			{
411 				if(mods&SHIFTMASK)
412 					s+=(char)toupper(code);
413 				else
414 					s+=(char)tolower(code);
415 			}
416 			else
417 			{
418 				return ""; // return that it's unknown
419 				/* would like to do this, but fxparseAccel wouldn't handle it inversly .. can I get this changed?
420 				char buffer[64];
421 				sprintf(buffer,"#%d",code);
422 				s+=buffer;
423 				*/
424 			}
425 			break;
426 	}
427 
428 	return s;
429 }
430 #endif
431