1 /*
2 Copyright (C) 2005-2008 Remon Sijrier
3 
4 This file is part of Traverso
5 
6 Traverso is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
19 
20 */
21 
22 #include "InputEngine.h"
23 
24 #include "ContextItem.h"
25 #include "ContextPointer.h"
26 #include "Information.h"
27 #include "Command.h"
28 #include <CommandPlugin.h>
29 #include "Utils.h"
30 
31 #include <QTime>
32 #include <QFile>
33 #include <QTextStream>
34 #include <QDomDocument>
35 #include <QMetaMethod>
36 #include <QCoreApplication>
37 #include <QPluginLoader>
38 #include <QDir>
39 
40 // Always put me below _all_ includes, this is needed
41 // in case we run with memory leak detection enabled!
42 #include "Debugger.h"
43 
44 
45 #define MAX_TIME_DIFFERENCE_FOR_DOUBLE_KEY_CONSIDERATION 50
46 #define MouseScrollHorizontalLeft -1
47 #define MouseScrollHorizontalRight -2
48 #define MouseScrollVerticalUp -3
49 #define MouseScrollVerticalDown -4
50 
51 
52 /**
53  * \class InputEngine
54  * \brief Processes keyboard/mouse events, dispatches the result, and handles the returned Command objects
55  *
56 	InputEngine forms, together with ViewPort, Command, ContextPointer, ContextItem <br />
57 	and Qt's Undo Framework, the framework that makes up the the Contextual <br />
58 	Interaction Interface, with analog type of actions, and un/redo (aka History) support.
59 
60 
61 	<b>Dispatching key facts to ContextItem objects</b>
62 
63 	InputEngine parses the keyboard/mouse events generated by the pointed ViewPort<br />
64 	If the keysequence matches that of any given in the keymap file, it call's<br />
65 	broadcast_action(). A list of pointed ContextItem objects is retrieved then <br />
66 	from ContextPointer. This list represents all (gui) ContextItem objects with their <br />
67 	corresponding 'core' ContextItem objects, stacked, with the topmost gui object on top.
68 
69 	For each ContextItem in the list, the class name is retreived, and looked up in the keymap <br />
70 	if for the detected key fact an object was supplied with the exact same name.<br />
71 	If this is the case, the function name, also given in the keymap file by the given object name <br />
72 	is used to call the ContextItem's function. If succesfull, the InputEngine will stop iterating <br />
73 	over the list, and start handling the returned Command object.
74 
75 	If the keymap specified that the object's doesn't have a function (slot) to be called, but instead<br />
76 	uses a CommandPlugin, the list of loaded CommandPlugins is searched to find a match for the <br />
77 	plugin name supplied in the keymap file, if there is a match, the Plugin is used to create <br />
78 	the Command object, and the same routine is used to handle this Command object.
79 
80 	If the Command object returned no error during the handling, it'll be placed on it's <br />
81 	historystack. If no historystack was available, it's do_action() will be called, and <br />
82 	deleted afterwards.
83 
84 
85  *	\sa Command, ContextPointer, ViewPort, CommandPlugin
86  */
87 
set_hexcode(int & variable,const QString & text)88 static void set_hexcode(int & variable, const QString& text)
89 {
90 	variable = 0;
91 	QString s;
92 	int x  = 0;
93 	if ((text != QString::null) && (text.length() > 0) ) {
94 		s="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
95 		x = s.indexOf(text);
96 		if (x>=0) {
97 			variable = Qt::Key_A + x;
98 		} else {
99 			s="|ESC     |TAB     |BACKTAB |BKSPACE |RETURN  |ENTER   |INSERT  |DELETE  "
100 			"|PAUSE   |PRINT   |SYSREQ  |CLEAR   ";
101 			x = s.indexOf("|" + text);
102 			if (x>=0)
103 				variable = Qt::Key_Escape + (x/9);
104 			else {
105 				s="|HOME    |END     |LARROW  |UARROW  |RARROW  "
106 				"|DARROW  |PRIOR   |NEXT    ";
107 				x = s.indexOf("|" + text);
108 				if (x>=0)
109 					variable = Qt::Key_Home + (x/9);
110 				else {
111 					s="|SHIFT   |CTRL    |META    |ALT     |CAPS    "
112 					"|NUMLOCK |SCROLL  ";
113 					x = s.indexOf("|" + text);
114 					if (x>=0)
115 						variable = Qt::Key_Shift + (x/9);
116 					else {
117 						s="F1 F2 F3 F4 F5 F6 F7 F8 F9 F10F11F12";
118 						x=s.indexOf(text);
119 						if (x>=0) {
120 							variable = Qt::Key_F1 + (x/3);
121 						} else if (text=="SPACE") {
122 							variable = Qt::Key_Space;
123 						} else if (text == "MouseButtonLeft") {
124 							variable = Qt::LeftButton;
125 						} else if (text == "MouseButtonRight") {
126 							variable = Qt::RightButton;
127 						} else if (text == "MouseButtonMiddle") {
128 							variable = Qt::MidButton;
129 						} else if (text == "MouseButtonX1") {
130 							variable = Qt::XButton1;
131 						} else if (text == "MouseButtonX2") {
132 							variable = Qt::XButton2;
133 						} else if (text == "MouseScrollHorizontalLeft") {
134 							variable = MouseScrollHorizontalLeft;
135 						} else if (text =="MouseScrollHorizontalRight") {
136 							variable = MouseScrollHorizontalRight;
137 						} else if (text == "MouseScrollVerticalUp") {
138 							variable = MouseScrollVerticalUp;
139 						} else if( text == "MouseScrollVerticalDown") {
140 							variable = MouseScrollVerticalDown;
141 						} else {
142 							PERROR("No HEX code found for %s", QS_C(text));
143 						}
144 					}
145 				}
146 			}
147 		}
148 
149 
150 
151 	}
152 	PMESG3("HEXCODE FOR %s=%d", QS_C(text), variable);
153 }
154 
155 
ie()156 InputEngine& ie()
157 {
158 	static InputEngine inputengine;
159 	return inputengine;
160 }
161 
InputEngine()162 InputEngine::InputEngine()
163 {
164 	PENTERCONS;
165 	holdingCommand = 0;
166 	// holdEvenCode MUST be a value != ANY key code!
167 	// when set to 'not matching any key!!!!!!
168 	holdEventCode = -100;
169 	isJogging = false;
170 	reset();
171 
172 	clearTime = 2000;
173 	assumeHoldTime = 200; // it will wait a release for 200 ms. Otherwise it will assume a hold
174 	doubleFactWaitTime = 200;
175 	collectedNumber = -1;
176 	sCollectedNumber = "-1";
177 	activate();
178 
179 //#define profile
180 
181 #if defined (profile)
182 	trav_time_t starttime = get_microseconds();
183 #endif
184 
185 	foreach (QObject* obj, QPluginLoader::staticInstances()) {
186 		CommandPlugin* plug = qobject_cast<CommandPlugin*>(obj);
187 		m_commandplugins.insert(plug->metaObject()->className(), plug);
188 	}
189 
190 #if !defined (STATIC_BUILD)
191 	QDir pluginsDir("lib/commandplugins");
192 	foreach (const QString &fileName, pluginsDir.entryList(QDir::Files)) {
193 		QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
194 		CommandPlugin* plug = qobject_cast<CommandPlugin*>(loader.instance());
195 		if (plug) {
196 			m_commandplugins.insert(plug->metaObject()->className(), plug);
197 			printf("InputEngine:: Succesfully loaded plugin: %s\n", plug->metaObject()->className());
198 		} else {
199 			printf("InputEngine:: Plugin load failed with %s\n", QS_C(loader.errorString()));
200 		}
201 	}
202 
203 #endif
204 
205 #if defined (profile)
206 	int processtime = (int) (get_microseconds() - starttime);
207 	printf("InputEngine::Plugin load time: %d useconds\n\n", processtime);
208 #endif
209 }
210 
~InputEngine()211 InputEngine::~ InputEngine( )
212 {
213 	foreach(IEAction* action, m_ieActions) {
214 		delete action;
215 	}
216 }
217 
activate()218 void InputEngine::activate()
219 {
220 	PENTER3;
221 	isFirstFact=true;
222 	active=true;
223 }
224 
225 
suspend()226 void InputEngine::suspend()
227 {
228 	PENTER3;
229 	active=false;
230 	set_jogging(false);
231 }
232 
broadcast_action_from_contextmenu(const QString & keySequence)233 int InputEngine::broadcast_action_from_contextmenu(const QString& keySequence)
234 {
235 	PENTER2;
236 	IEAction* action = 0;
237 
238 	foreach(IEAction* ieaction, m_ieActions) {
239 		if (ieaction->keySequence == keySequence) {
240 			action = ieaction;
241 			break;
242 		}
243 	}
244 
245 	if (keySequence.contains("++")) {
246 		info().information(tr("Modifier key actions are not supported from Context Menu"));
247 		return -1;
248 	}
249 
250 	if (! action) {
251 		PERROR("ContextMenu keySequence doesn't apply to any InputEngine knows off!! (%s)", QS_C(keySequence));
252 		return -1;
253 	}
254 
255 	if ( action && ((action->type == HOLDKEY) || (action->type == HKEY2))) {
256 		info().information(tr("Hold actions are not supported from Context Menu"));
257 		return -1;
258 	}
259 
260 
261 	return broadcast_action(action, false, true);
262 }
263 
264 
broadcast_action(IEAction * action,bool autorepeat,bool fromContextMenu)265 int InputEngine::broadcast_action(IEAction* action, bool autorepeat, bool fromContextMenu)
266 {
267 	PENTER2;
268 
269 	Command* k = 0;
270 	QObject* item = 0;
271 	int useX=0, useY=0;
272 
273 	QList<QObject* > list;
274 
275 	if ( ! fromContextMenu ) {
276 		list = cpointer().get_context_items();
277 	} else {
278 		list = cpointer().get_contextmenu_items();
279 	}
280 
281 	QString slotsignature = "";
282 
283 	if (holdingCommand) {
284 		list.prepend(holdingCommand);
285 	}
286 
287 	PMESG("Trying to find IEAction for key sequence %s", action->keySequence.data());
288 
289 	for (int i=0; i < list.size(); ++i) {
290 		k = 0;
291 		m_broadcastResult = 0;
292 
293 		item = list.at(i);
294 
295 		if (!item) {
296 			PERROR("no item in cpointer()'s context item list ??");
297 			continue;
298 		}
299 
300 		IEAction::Data* data = action->objectUsingModifierKeys.value(QString(item->metaObject()->className()));
301 
302 		// A match was found for actions using the modifier key
303 		// let's see if it is valid for the current active modifier keys!
304 		if (data) {
305 			PMESG("found match in objectUsingModierKeys");
306 			bool modifierkeymatch = true;
307 
308 			if (data->modifierkeys.size()) {
309 				foreach(int key, data->modifierkeys) {
310 					if ( ! m_activeModifierKeys.contains(key)) {
311 						PMESG("m_activeModifierKeys doesn't contain code %d", key);
312 						modifierkeymatch = false;
313 						break;
314 					}
315 				}
316 			} else {
317 				modifierkeymatch = false;
318 			}
319 
320 			if (! modifierkeymatch) {
321 				data = 0;
322 			}
323 		}
324 
325 		// No match found for actions using a modifier key, let's see if there
326 		// is one in the 'normal' actions list.
327 		if (! data ) {
328 			// This test makes sure that we don't select an unmodified command
329 			// when the user is holding down modifier keys.
330 			if (m_activeModifierKeys.size() > 0) {
331 				continue;
332 			}
333 			data = action->objects.value(QString(item->metaObject()->className()));
334 
335 			if (! data ) {
336 				PMESG("No data found for object %s", item->metaObject()->className());
337 				continue;
338 			}
339 		}
340 
341 		// Now that we found a match, we still have to check if
342 		// the current mode is valid for this data!
343 		QString currentmode = m_modes.key(cpointer().get_current_mode());
344 		QString allmodes = m_modes.key(0);
345 		if ( (! data->modes.contains(currentmode)) && (! data->modes.contains(allmodes))) {
346 			PMESG("%s on %s is not valid for mode %s", action->keySequence.data(), item->metaObject()->className(), QS_C(currentmode));
347 			continue;
348 		}
349 
350 		PMESG("Data found for %s!", item->metaObject()->className());
351 		PMESG("setting slotsignature to %s", QS_C(data->slotsignature));
352 		PMESG("setting pluginname to %s", QS_C(data->pluginname));
353 		PMESG("setting plugincommand to %s", QS_C(data->commandname));
354 
355 		QString pluginname = "", commandname = "";
356 		slotsignature = data->slotsignature;
357 		pluginname = data->pluginname;
358 		commandname = data->commandname;
359 		useX = data->useX;
360 		useY = data->useY;
361 
362 
363 		if (item == holdingCommand) {
364 			if (QMetaObject::invokeMethod(item, QS_C(slotsignature), Qt::DirectConnection, Q_ARG(bool, autorepeat))) {
365 				PMESG("HIT, invoking %s::%s", holdingCommand->metaObject()->className(), QS_C(slotsignature));
366 				break;
367 			}
368 		}
369 
370 
371 		// We first try to find if there is a match in the loaded plugins.
372 		if ( ! holdingCommand ) {
373 
374 			if ( ! pluginname.isEmpty() ) {
375 				CommandPlugin* plug = m_commandplugins.value(pluginname);
376 				if (!plug) {
377 					info().critical(tr("Command Plugin %1 not found!").arg(pluginname));
378 				} else {
379 					if ( ! plug->implements(commandname) ) {
380 						info().critical(tr("Plugin %1 doesn't implement Command %2")
381 								.arg(pluginname).arg(commandname));
382 					} else {
383 						PMESG("InputEngine:: Using plugin %s for command %s",
384 								QS_C(pluginname), QS_C(data->commandname));
385 						k = plug->create(item, commandname, data->arguments);
386 					}
387 				}
388 			}
389 		}
390 
391 		// Either the plugins didn't have a match, or were holding.
392 		if ( ! k ) {
393 
394 			IEAction::Data* delegatingdata;
395 			QString delegatedobject;
396 
397 			if (holdingCommand) {
398 				delegatingdata = action->objects.value("HoldCommand");
399 				delegatedobject = "HoldCommand";
400 			} else {
401 				delegatedobject = item->metaObject()->className();
402 				if (m_activeModifierKeys.size() > 0) {
403 					delegatingdata = action->objectUsingModifierKeys.value(delegatedobject);
404 				} else {
405 					delegatingdata = action->objects.value(delegatedobject);
406 				}
407 				PMESG("delegatedobject is %s", QS_C(delegatedobject));
408 			}
409 
410 			if ( ! delegatingdata) {
411 				PMESG("No delegating data ? WEIRD");
412 				continue;
413 			}
414 
415 			QStringList strlist = delegatingdata->slotsignature.split("::");
416 
417 			if (strlist.size() == 2) {
418 				PMESG("Detected delegate action, checking if it is valid!");
419 				QString classname = strlist.at(0);
420 				QString slot = strlist.at(1);
421 				QObject* obj = 0;
422 				bool validobject = false;
423 
424 				for (int j=0; j < list.size(); ++j) {
425 					obj = list.at(j);
426 					if (obj->metaObject()->className() == classname) {
427 						PMESG("Found an item in the contextitem list that equals delegated object");
428 						validobject = true;
429 						break;
430 					}
431 				}
432 
433 				if (validobject) {
434 					if (QMetaObject::invokeMethod(obj, QS_C(slot),  Qt::DirectConnection, Q_RETURN_ARG(Command*, k))) {
435 						PMESG("HIT, invoking (delegated) %s::%s", QS_C(classname), QS_C(slot));
436 					} else {
437 						PMESG("Delegated object slot call didn't work out, sorry!");
438 						PMESG("%s::%s() --> %s::%s()", item->metaObject()->className(), QS_C(slot), QS_C(classname), QS_C(slot));
439 					}
440 				} else {
441 					PMESG("Delegated object %s was not found in the context items list!", QS_C(classname));
442 				}
443 			} else {
444 				if (QMetaObject::invokeMethod(item, QS_C(slotsignature), Qt::DirectConnection, Q_RETURN_ARG(Command*, k))) {
445 					PMESG("HIT, invoking %s::%s", item->metaObject()->className(), QS_C(slotsignature));
446 				} else {
447 					PMESG("nope %s wasn't the right one, next ...", item->metaObject()->className());
448 				}
449 			}
450 		}
451 
452 
453 		// Let's see if the ContextItem used either succes(), failure() or did_not_implement()
454 		// return functions, so we can detect to either return happily, the action was succesfull
455 		// but no command object needed to be returned, the action was not succesfull, and we
456 		// don't want to try lower level context items or the action was succesfull but we'd like
457 		// to give a lower level contextitem precedence over the current one.
458 		if (m_broadcastResult) {
459 			if (m_broadcastResult == SUCCES) {
460 				PMESG("Broadcast Result indicates succes, but no returned Command object");
461 				return 1;
462 			}
463 			if (m_broadcastResult == FAILURE) {
464 				PMESG("Broadcast Result indicates failure, and doesn't want lower level items to be processed");
465 				return 0;
466 			}
467 			if (m_broadcastResult == DIDNOTIMPLEMENT) {
468 				PMESG("Broadcast Result indicates succes, but didn't want to perform it's action,"
469 						 "so we continue traversing the contextitem list");
470 				continue;
471 			}
472 		}
473 
474 		if (k && (!isHolding)) {
475 			if (k->prepare_actions() != -1) {
476 				k->set_valid(true);
477 				if (k->push_to_history_stack() < 0) {
478 					// The command doesn't have a history stack, or wasn't
479 					// historable for some reason.... At least call do_action
480 					// since that still isn't done (should be done by QUndoStack...)
481 					k->do_action();
482 					delete k;
483 					k = 0;
484 				}
485 			} else {
486 				PWARN("prepare actions failed!");
487 				delete k;
488 				k = 0;
489 			}
490 		}
491 
492 		if (k && isHolding) {
493 			if (k->begin_hold() != -1) {
494 				k->set_valid(true);
495 				k->set_cursor_shape(useX, useY);
496 				holdingCommand = k;
497 				set_jogging(true);
498 			} else {
499 				PERROR("hold action begin_hold() failed!");
500 				// OOPSSS, something went wrong when making the Command
501 				// set following stuff to zero to make finish_hold do nothing
502 				delete k;
503 				k = 0;
504 				set_jogging( false );
505 				wholeMapIndex = -1;
506 			}
507 		}
508 
509 		break;
510 	}
511 
512 	return 1;
513 }
514 
succes()515 Command* InputEngine::succes()
516 {
517 	m_broadcastResult = SUCCES;
518 	return 0;
519 }
520 
failure()521 Command* InputEngine::failure()
522 {
523 	m_broadcastResult = FAILURE;
524 	return 0;
525 }
526 
did_not_implement()527 Command* InputEngine::did_not_implement()
528 {
529 	m_broadcastResult = DIDNOTIMPLEMENT;
530 	return 0;
531 }
532 
jog()533 void InputEngine::jog()
534 {
535 	PENTER3;
536 
537 	if (isJogging) {
538 		if (holdingCommand) {
539 			if (m_bypassJog) {
540 				QPoint diff = m_jogBypassPos - cpointer().pos();
541 				if (diff.manhattanLength() > m_unbypassJogDistance) {
542 					m_bypassJog = false;
543 				} else {
544 					return;
545 				}
546 				m_jogBypassPos = cpointer().pos();
547 			}
548 
549 			holdingCommand->jog();
550 		}
551 	}
552 }
553 
bypass_jog_until_mouse_movements_exceeded_manhattenlength(int length)554 void InputEngine::bypass_jog_until_mouse_movements_exceeded_manhattenlength(int length)
555 {
556 	m_unbypassJogDistance = length;
557 	m_bypassJog = true;
558 	m_jogBypassPos = cpointer().pos();
559 }
560 
set_jogging(bool jog)561 void InputEngine::set_jogging(bool jog)
562 {
563 	if (jog) {
564 		cpointer().jog_start();
565 	} else {
566 		cpointer().jog_finished();
567 	}
568 
569 	isJogging = jog;
570 }
571 
is_jogging()572 bool InputEngine::is_jogging()
573 {
574 	return isJogging;
575 }
576 
577 
578 
579 // JMB ENGINE : EVENT LEVEL HANDLING -------------------------------
580 
581 // reset the event stack and the press 'stack' (not exactly a stack)
reset()582 void InputEngine::reset()
583 {
584 	PENTER3;
585 	isFirstFact = true;
586 	isDoubleKey = false;
587 	fact1_k1 = 0;
588 	fact1_k2 = 0;
589 	isHolding = false;
590 	isPressEventLocked = false;
591 	m_cancelHold = false;
592 	stackIndex = 0;
593 	pressEventCounter = 0;
594 	fact2_k1 = 0;
595 	fact2_k2 = 0;
596 	wholeMapIndex = -1;
597 	m_bypassJog = false;
598 
599 	for (int i=0; i < STACK_SIZE; i++) {
600 		eventType[i] = 0;
601 		eventStack[i] = 0;
602 		eventTime[i] = 0;
603 	}
604 }
605 
clear_modifier_keys()606 void InputEngine::clear_modifier_keys()
607 {
608 	m_activeModifierKeys.clear();
609 }
610 
611 
612 // Everthing starts here. Catch event takes anything happen in the keyboard
613 // and pushes it into a stack.
catch_key_press(QKeyEvent * e)614 void InputEngine::catch_key_press(QKeyEvent * e )
615 {
616 	if (e->isAutoRepeat() && !isHolding) {
617 		return;
618 	}
619 	PENTER4;
620 	process_press_event(e->key(), e->isAutoRepeat());
621 }
622 
catch_key_release(QKeyEvent * e)623 void InputEngine::catch_key_release( QKeyEvent * e)
624 {
625 	if (e->isAutoRepeat()) {
626 		return;
627 	}
628 	PENTER4;
629 	process_release_event(e->key());
630 }
631 
catch_mousebutton_press(QMouseEvent * e)632 void InputEngine::catch_mousebutton_press( QMouseEvent * e )
633 {
634 	process_press_event(e->button());
635 }
636 
catch_mousebutton_release(QMouseEvent * e)637 void InputEngine::catch_mousebutton_release( QMouseEvent * e )
638 {
639 	process_release_event(e->button());
640 }
641 
catch_mousebutton_doubleclick(QMouseEvent * e)642 void InputEngine::catch_mousebutton_doubleclick( QMouseEvent * e )
643 {
644 	process_press_event(e->button());
645 	process_release_event(e->button());
646 	process_press_event(e->button());
647 	process_release_event(e->button());
648 }
649 
650 
catch_scroll(QWheelEvent * e)651 void InputEngine::catch_scroll(QWheelEvent* e)
652 {
653 	if (e->orientation() == Qt::Horizontal) {
654 		if (e->delta() > 0) {
655 		}
656 		if (e->delta() < 0) {
657 		}
658 	} else {
659 		if (e->delta() > 0) {
660 			process_press_event(MouseScrollVerticalUp);
661 			process_release_event(MouseScrollVerticalUp);
662 		}
663 		if (e->delta() < 0) {
664 			process_press_event(MouseScrollVerticalDown);
665 			process_release_event(MouseScrollVerticalDown);
666 		}
667 	}
668 }
669 
process_press_event(int eventcode,bool isAutoRepeat)670 void InputEngine::process_press_event(int eventcode, bool isAutoRepeat)
671 {
672 	if (eventcode == Qt::Key_Escape && is_holding()) {
673 		m_cancelHold = true;
674 		finish_hold();
675 		return;
676 	}
677 
678 	// first check if this fact is just a collected number
679 	if (check_number_collection(eventcode)) {
680 		// another digit was collected.
681 		return;
682 	}
683 
684 	if (is_modifier_keyfact(eventcode)) {
685 		if ( (! isAutoRepeat) && (! m_activeModifierKeys.contains(eventcode)) ) {
686 			m_activeModifierKeys.append(eventcode);
687 		}
688 		return;
689 	}
690 
691 	if (isFirstFact && !isHolding) {
692 		cpointer().inputengine_first_input_event();
693 		if (eventStack[0] == 0) {
694 			// Here we jump straight to the <K> command if "K" is unambiguously an FKEY
695 			int fkey_index = find_index_for_instant_fkey(eventcode);
696 			if (fkey_index >= 0) {
697 				catcher.holdTimer.stop(); // quit the holding check..
698 				IEAction* action = m_ieActions.at(fkey_index);
699 				broadcast_action(action, isAutoRepeat);
700 				conclusion();
701 				return;
702 			}
703 		} else {
704 			// Here we jump straight to the <KL> command if "KL" is unambiguously an FKEY2
705 			int fkey2_index = find_index_for_instant_fkey2(eventcode, eventStack[0]);
706 			if (fkey2_index >= 0) {
707 				catcher.holdTimer.stop(); // quit the holding check..
708 				IEAction* action = m_ieActions.at(fkey2_index);
709 				broadcast_action(action, isAutoRepeat);
710 				conclusion();
711 				return;
712 			}
713 		}
714 	}
715 
716 	if (isHolding) {
717 		int index = find_index_for_single_fact(FKEY, eventcode, 0);
718 		// PRE-CONDITION:
719 		// The eventcode must be bind to a single key fact AND
720 		// the eventcode must be != the current active holding
721 		// command's eventcode!
722 		if (index >= 0 && holdEventCode != eventcode) {
723 			IEAction* action = m_ieActions.at(index);
724 			broadcast_action(action, isAutoRepeat);
725 		}
726 		return;
727 	}
728 
729 	if (!isPressEventLocked) {
730 		if (pressEventCounter < 2) {
731 			pressEventCounter++;
732 			push_event(PRESS_EVENT, eventcode);
733 			press_checker();
734 			if (!catcher.holdTimer.isActive())
735 				catcher.holdTimer.start( assumeHoldTime); // single shot timer
736 		} else {
737 			isPressEventLocked = true;
738 		}
739 	}
740 }
741 
process_release_event(int eventcode)742 void InputEngine::process_release_event(int eventcode)
743 {
744 	if (is_modifier_keyfact(eventcode)) {
745 		m_activeModifierKeys.removeAll(eventcode);
746 		return;
747 	}
748 
749 	if (isHolding) {
750 		if (eventcode != holdEventCode) {
751 			PMESG("release event during hold action, but NOT for holdaction itself!!");
752 			return;
753 		} else {
754 			PMESG("release event for hold action detected!");
755 			holdEventCode = -100;
756 		}
757 	}
758 
759 	if ((!is_fake(eventcode)) && (stackIndex != 0)) {
760 		push_event(RELEASE_EVENT, eventcode);
761 		release_checker();
762 	}
763 }
764 
765 
766 // This pushes an event to the stack
push_event(int pType,int pCode)767 void InputEngine::push_event(  int pType,  int pCode )
768 {
769 	PENTER3;
770 	if (stackIndex < STACK_SIZE) {
771 		eventType[stackIndex] = pType;
772 		eventStack[stackIndex] = pCode;
773 		QTime currTime = QTime::currentTime();
774 		long ts=currTime.msec() + (currTime.second() * 1000) + (currTime.minute() * 1000 * 60);
775 		eventTime[stackIndex] = ts;
776 		PMESG3("Pushing EVENT %d (%s) key=%d at %ld",stackIndex,( pType==PRESS_EVENT ? "PRESS" : "RELEASE" ),pCode,ts);
777 		stackIndex++;
778 		for (int j=0; j<STACK_SIZE; j++) {
779 			PMESG4("eventStack[%d]=%d %s at timestamp=%ld\n",j,eventStack[j],(eventType[j]==PRESS_EVENT?"P":"R"),eventTime[j]);
780 		}
781 	}
782 }
783 
784 
785 // this is a intermediate step to push. Only non-fake events are allowed to be pushed
is_fake(int codeVal)786 bool InputEngine::is_fake(int codeVal)
787 {
788 	// A fake event is an event which is generated by a ignored PRESS_EVENT
789 	bool b =   ( codeVal!=eventStack[0] )
790 		&& ( codeVal!=eventStack[1] )
791 		&& ( codeVal!=eventStack[2] )
792 		&& ( codeVal!=eventStack[3] );
793 	return b;
794 }
795 
is_modifier_keyfact(int eventcode)796 bool InputEngine::is_modifier_keyfact(int eventcode)
797 {
798 	return m_modifierKeys.contains(eventcode);
799 }
800 
801 // this is the method that detects wether a fact is double key (KK) or single key (K)
802 // by measuring the time difference between the first 2 presses (in case event[0] AND
803 // event[1] are PRESS_EVENT's
press_checker()804 void InputEngine::press_checker()
805 {
806 	PENTER3;
807 	if (stackIndex==2) // first two events happend
808 	{
809 		PMESG3("Checking if 2 first events are PRESS'es and what is the time diff between them");
810 		if (     ( eventType[0]==PRESS_EVENT )
811 				&& ( eventType[1]==PRESS_EVENT )
812 		)
813 		{
814 			isDoubleKey = ( eventTime[1] - eventTime[0] < MAX_TIME_DIFFERENCE_FOR_DOUBLE_KEY_CONSIDERATION );
815 		}
816 		if (isDoubleKey)
817 		{
818 			PMESG3("Detected 2 initial PRESS almost together. It is a double key (KK) !");
819 		}
820 	}
821 }
822 
823 // This is the one who consolidate a fact. It can detect
824 // if the fact is K or KK, and also calls the push_fact handler
release_checker()825 void InputEngine::release_checker()
826 {
827 	PENTER3;
828 	if (isHolding) {
829 		finish_hold();
830 		return;
831 	}
832 	// If it is not fake, then lets take a look in the pairs
833 	// Remember that event 0 is always PRESS_EVENT
834 	if (eventType[1]==RELEASE_EVENT) {
835 		// event 0 and event 1 are a pair because event 1 must be the release of 0
836 		// since the event 1 was a release, then the first fact is finished
837 		// Now we must push_fact, but only if it is a release very fast (that
838 		// didnt cause a HOLD.
839 		if (!isHolding)
840 			push_fact(eventStack[0],0);
841 		return;
842 	}
843 	// event 1 is ALSO a PRESS_EVENT. so we must check for a double key
844 	if (isDoubleKey) // ( very short time difference between 2 first presses)
845 	{
846 		// Now we dont know if this is the event 2 or event 3
847 		if (stackIndex==3)
848 		{
849 			// So we are processing event (??), which is a release
850 			// So this can be release of event 0 or 1 , so..
851 			if (eventStack[2]==eventStack[0])
852 				pairOf2 = 0;
853 			else
854 				pairOf2 = 1;
855 			// the event is not finished yet but it must be avoided
856 			// any reentrance (any new PRESS_EVENT)
857 			isPressEventLocked = true;
858 			return;
859 		}
860 		// we are in the event 3 . the last!
861 		// the event is finished , surely. But we must check the pairs
862 		// we already know who event 2 is pair of. So ...
863 		if (pairOf2 == 0)
864 			pairOf3 = 1;
865 		else
866 			pairOf3 = 0;
867 		// The event is finished, and we know the pairs. So..
868 		// I must push_fact, but only if it is a release very fast (that
869 		// didnt cause a HOLD.
870 		if (!isHolding)
871 			push_fact(eventStack[0],eventStack[1]);
872 	} else {
873 		// although the sequence was P-P-R-R (press, press, release, release)
874 		// the time difference between two first presses was too long
875 		// so it CAN'T be a double key (KK).
876 		// So the only possible explanation is that this is a >K>K action
877 		// where user typed too fast, so the release of 1st P happend AFTER the
878 		// 2nd press. In this case, I have to assume this is a >K>K
879 		// action, and dispatch TWO facts.
880 		// Now forcing prematurally a 2 facts by splitting and syncronizing
881 		// these events into 2 different facts. Probably this is a >K>K
882 		// of course, this  >K>K assumption mentioned above can be made ONLY if it is the first fact.
883 		if (isFirstFact) {
884 			PMESG("Inital double key too slow. It must be a premature >K>K. Dispatching 2 individual <K> facts.");
885 			int f1_k1=eventStack[0];
886 			int f2_k1=eventStack[1];
887 			push_fact (f1_k1,0);
888 			push_fact (f2_k1,0);
889 		}
890 	}
891 
892 }
893 
894 
895 // ----------------------------- JMB ENGINE : PRESS LEVEL HANDLING -------------------------
896 
897 
push_fact(int k1,int k2)898 void InputEngine::push_fact(int k1,int k2)
899 {
900 	PENTER3;
901 	PMESG3("Pushing FACT : k1=%d k2=%d",k1,k2);
902 	catcher.holdTimer.stop(); // quit the holding check..
903 	if (isFirstFact) {
904 		// this is the first fact
905 		PMESG3("First fact detected !");
906 		// first try to find some action like k1k2
907 		fact1_k1 = k1;
908 		fact1_k2 = k2;
909 
910 		int mapIndex = identify_first_fact();
911 		if (mapIndex < 0) {
912 			PMESG3("First fact alone does not match anything in the map. Waiting for a second fact...");
913 			// Action not identified. Maybe is part of a double fact action. so...
914 			give_a_chance_for_second_fact();
915 			return;
916 		}
917 		PMESG3("First fact matches map in position %d",mapIndex);
918 		// there is a single-fact action which matches this !! now must check if is not an immediate action
919 
920 		if (!m_ieActions.at(mapIndex)->isInstantaneous) {
921 			PMESG3("Although this could be an SINGLE PRESS Action, it is not protected, so...");
922 			// action is not an immediate action, so...
923 			give_a_chance_for_second_fact();
924 			return;
925 		}
926 		// Action exists AND it is a immediate action. So
927 		// forces it to be a single fact action
928 		PMESG3("This is protected (immediate) action. It'll be treated as <K>");
929 		dispatch_action(mapIndex);
930 		conclusion();
931 	} else // ok . We are in the second fact.
932 	{
933 		catcher.secondChanceTimer.stop();
934 		fact2_k1 = k1;
935 		fact2_k2 = k2;
936 		if (fact2_k1!=0) {
937 			// this is the second press
938 			PMESG3("Second fact detected !");
939 		}
940 		// try to complement the first press.
941 		wholeMapIndex = identify_first_and_second_facts_together();
942 		if (wholeMapIndex >= 0) {
943 			PMESG3("First and second facts together matches with action %d !! Dispatching it...",wholeMapIndex );
944 			dispatch_action(wholeMapIndex);
945 		} else {
946 			PMESG3("Apparently, first and second facts together do not match any action. Sorry :-(");
947 		}
948 		conclusion();
949 	}
950 }
951 
952 
identify_first_fact()953 int InputEngine::identify_first_fact()
954 {
955 	PENTER3;
956 	fact1Type = 0;
957 	// First we need to know the first fact type.
958 	if (fact1_k2==0) // <K> or [K]
959 	{
960 		if (!isHolding)
961 		{
962 			PMESG3("Detected <K>");
963 			fact1Type = FKEY;
964 		} else
965 		{
966 			PMESG3("Detected [K]");
967 			fact1Type = HOLDKEY;
968 			holdEventCode = fact1_k1;
969 
970 		}
971 	}
972 	else // <KK> or [KK]
973 	{
974 		if (!isHolding) {
975 			PMESG3("Detected <KK>");
976 			fact1Type = FKEY2;
977 		} else {
978 			PMESG3("Detected [KK]");
979 			fact1Type = HKEY2;
980 			holdEventCode = fact1_k2;
981 		}
982 	}
983 
984 	// Fact 1 Type identified .
985 	int index = find_index_for_single_fact(fact1Type, fact1_k1, fact1_k2);
986 	if (index >= 0) {
987 		return index;
988 	}
989 
990 	PMESG3("No single fact candidate action found. Keep going, since a 2nd fact might come soon");
991 	give_a_chance_for_second_fact();
992 	return -1;
993 }
994 
995 
996 // Only return a valid index if there is an FKEY defined for key, and there are no
997 // other conflicting keyfacts (HOLDKEY, FKEY2, etc)
find_index_for_instant_fkey(int key)998 int InputEngine::find_index_for_instant_fkey( int key )
999 {
1000 	int fkey_index = find_index_for_single_fact(FKEY, key, 0);
1001 	if (fkey_index < 0) {
1002 		return -1;
1003 	}
1004 
1005 	foreach(IEAction* action, m_ieActions) {
1006 
1007 		if (action->type == FKEY)
1008 			continue;
1009 		if (action->fact1_key1==key || action->fact1_key2==key) {
1010 			PMESG3("Found a conflict (%s) for instantaneous keyfact key=%d", action->keySequence.data(), key);
1011 			return -1;
1012 		}
1013 	}
1014 
1015 	return fkey_index;
1016 }
1017 
1018 
1019 // Only return a valid index if there is an FKEY2 defined for key1, key2, and there are no
1020 // other conflicting keyfacts (HOLDKEY2, etc)
find_index_for_instant_fkey2(int key1,int key2)1021 int InputEngine::find_index_for_instant_fkey2( int key1, int key2 )
1022 {
1023 	int fkey2_index = find_index_for_single_fact(FKEY2, key1, key2);
1024 	if (fkey2_index < 0) {
1025 		return -1;
1026 	}
1027 
1028 	foreach(IEAction* action, m_ieActions) {
1029 
1030 		if (action->type == D_FKEY2 || action->type == HKEY2) {
1031 			if ( (action->fact1_key1==key1 && action->fact1_key2==key2) ||
1032 			(action->fact1_key1==key2 && action->fact1_key2==key1) ) {
1033 				PMESG3("Found a conflict (%s) for instantaneous keyfact keys=%d,%d", action->keySequence.data(), key1, key2);
1034 				return -1;
1035 			}
1036 		}
1037 	}
1038 	return fkey2_index;
1039 }
1040 
1041 
find_index_for_single_fact(int type,int key1,int key2)1042 int InputEngine::find_index_for_single_fact( int type, int key1, int key2 )
1043 {
1044 	foreach(IEAction* action, m_ieActions) {
1045 
1046 		if (action->type != type )
1047 			continue;
1048 		if (
1049 			(
1050 				((action->fact1_key1==key1) && ( action->fact1_key2==key2))
1051 				||
1052 				((action->fact1_key1==key2) && ( action->fact1_key2==key1))
1053 			)
1054 			&&
1055 			( action->fact2_key1 == 0 )
1056 			&&
1057 			( action->fact2_key2 == 0 )
1058 		) {
1059 			// 'i' is a candidate for first press
1060 			PMESG3("Found a match in map position %d, keyfact %s", m_ieActions.indexOf(action), action->keySequence.data());
1061 			return m_ieActions.indexOf(action);
1062 		}
1063 	}
1064 
1065 	return -1;
1066 }
1067 
1068 
1069 // This is called whenever a second fact happens right after the first
identify_first_and_second_facts_together()1070 int InputEngine::identify_first_and_second_facts_together()
1071 {
1072 	PENTER3;
1073 	PMESG3("Adding a 2nd fact %d,%d  to the 1st one  %d,%d",fact2_k1,fact2_k2,fact1_k1,fact1_k2);
1074 	if (fact2_k1!=0) {
1075 		if (!isHolding) {
1076 			if (fact1_k2==0) // first press is <K> (I know that its not [K] because if it was I'd never reach identify_first_and_second_facts_together())
1077 			{
1078 				if (fact2_k2==0)
1079 					if (fact1_k1 == fact2_k1)
1080 					{
1081 						PMESG3("Whole action is a <<K>>");
1082 						wholeActionType = D_FKEY;  // <<K>>
1083 					} else
1084 					{
1085 						PMESG3("Whole action is a >K>K");
1086 						wholeActionType = S_FKEY_FKEY; // >K>K
1087 					}
1088 				else
1089 				{
1090 					PMESG3("Whole action is a >K>KK");
1091 					wholeActionType = S_FKEY_FKEY2;  // >K>KK
1092 				}
1093 			}
1094 
1095 			else {
1096 				if (fact2_k2==0) {
1097 					PMESG3("Whole action is a >KK>K");
1098 					wholeActionType = S_FKEY2_FKEY;  // >KK>K
1099 				} else {
1100 					if (
1101 						((fact1_k1==fact2_k1) && (fact1_k2==fact2_k2)) ||
1102 						((fact1_k1==fact2_k2) && (fact1_k2==fact2_k1))
1103 					) {
1104 						PMESG3("Whole action is a <<KK>>");
1105 						wholeActionType = D_FKEY2;
1106 					} else {
1107 						PMESG3("Whole action is a >KK>KK");
1108 						wholeActionType = S_FKEY2_FKEY2;
1109 					}
1110 				}
1111 			}
1112 		} else {
1113 			if (fact1_k2==0) // first press is <K> (I know that its not [K] because if it was I'd never reach identify_first_and_second_facts_together())
1114 			{
1115 				if (fact2_k2==0)
1116 					if (fact1_k1 == fact2_k1)
1117 					{
1118 						PMESG3("Whole action is a <[K]>");
1119 						wholeActionType = FHKEY;
1120 					} else
1121 					{
1122 						PMESG3("Whole action is a >K[K]");
1123 						wholeActionType = S_FKEY_HKEY;
1124 					}
1125 				else
1126 				{
1127 					PMESG3("Whole action is a >K[KK]");
1128 					wholeActionType = S_FKEY_HKEY2;
1129 				}
1130 			}
1131 			else {
1132 				if (fact2_k2==0) {
1133 					PMESG3("Whole action is a KK[K]");
1134 					wholeActionType = S_FKEY2_HKEY;
1135 				} else {
1136 					PMESG3("Whole action is a >KK[KK]");
1137 					wholeActionType = S_FKEY2_HKEY2;
1138 				}
1139 			}
1140 		}
1141 	} else {
1142 		PMESG3("Second fact is null (0,0). Assuming wholeActionType is %d", fact1Type);
1143 		wholeActionType = fact1Type;
1144 	}
1145 
1146 	// whole action type identified .
1147 	PMESG3("Searching for a %d action that matches %d,%d,%d,%d ", wholeActionType, fact1_k1, fact1_k2, fact2_k1, fact2_k2);
1148 	foreach(IEAction* action, m_ieActions) {
1149 		if ( action->type != wholeActionType )
1150 			continue;
1151 		int ap1k1 = action->fact1_key1;
1152 		int ap1k2 = action->fact1_key2;
1153 		int ap2k1 = action->fact2_key1;
1154 		int ap2k2 = action->fact2_key2;
1155 		PMESG4("COMPARING %d,%d,%d,%d  \tWITH  %d,%d,%d,%d",ap1k1,ap1k2,ap2k1,ap2k2,fact1_k1,fact1_k2,fact2_k1,fact2_k2);
1156 		if (
1157 			(
1158 				((ap1k1==fact1_k1) && (ap1k2==fact1_k2))
1159 				||
1160 				((ap1k1==fact1_k2) && (ap1k2==fact1_k1))
1161 			)
1162 			&&
1163 			(
1164 				((ap2k1==fact2_k1) && (ap2k2==fact2_k2))
1165 				||
1166 				((ap2k1==fact2_k2) && (ap2k2==fact2_k1))
1167 			)
1168 		) {
1169 			// 'i' is a candidate the whole action
1170 			PMESG3("Found a match : action %s", action->keySequence.data());
1171 			return m_ieActions.indexOf(action);
1172 		}
1173 	}
1174 	PMESG3("No candidates found :-(");
1175 	return -1;
1176 }
1177 
1178 
give_a_chance_for_second_fact()1179 void InputEngine::give_a_chance_for_second_fact()
1180 {
1181 	PENTER3;
1182 	PMESG3("Waiting %d ms for second fact ...",doubleFactWaitTime );
1183 	catcher.secondChanceTimer.start( doubleFactWaitTime );
1184 	isFirstFact=false;
1185 	isHolding = false;
1186 	isPressEventLocked = false;
1187 	stackIndex = 0;
1188 	pressEventCounter = 0;
1189 	fact2_k1 = 0;
1190 	fact2_k2 = 0;
1191 	wholeMapIndex = -1;
1192 	for (int i=0; i < STACK_SIZE; i++) {
1193 		eventType[i] = 0;
1194 		eventStack[i] = 0;
1195 		eventTime[i] = 0;
1196 	}
1197 }
1198 
1199 
1200 
1201 // ----------------------------- JMB ENGINE : ACTION LEVEL HANDLING -------------------------
dispatch_action(int mapIndex)1202 void InputEngine::dispatch_action(int mapIndex)
1203 {
1204 	PENTER2;
1205 	broadcast_action(m_ieActions.at(mapIndex));
1206 }
1207 
1208 
1209 
1210 // This is called by
dispatch_hold()1211 void InputEngine::dispatch_hold()
1212 {
1213 	PENTER2;
1214 	catcher.clearOutputTimer.stop();
1215 	isHoldingOutput=false;
1216 
1217 	wholeMapIndex = -1;
1218 	if (isFirstFact) {
1219 		fact1_k1 = eventStack[0];
1220 		fact1_k2 = eventStack[1];
1221 		wholeMapIndex = identify_first_fact(); // I can consider first press the last because there is nothing after a []
1222 	} else {
1223 		fact2_k1 = eventStack[0];
1224 		fact2_k2 = eventStack[1];
1225 		wholeMapIndex = identify_first_and_second_facts_together();
1226 	}
1227 
1228 	if (wholeMapIndex>=0) {
1229 		broadcast_action(m_ieActions.at(wholeMapIndex));
1230 	}
1231 
1232 	stop_collecting();
1233 	// note that we dont call conclusion() here :-)
1234 }
1235 
1236 
1237 
1238 
finish_hold()1239 void InputEngine::finish_hold()
1240 {
1241 	PENTER3;
1242 	PMESG("Finishing hold action %d",wholeMapIndex);
1243 
1244 	isHolding = false;
1245 
1246 	if (m_cancelHold) {
1247 		PMESG("Canceling this hold command");
1248 		if (holdingCommand) {
1249 			holdingCommand->cancel_action();
1250 			delete holdingCommand;
1251 			holdingCommand = 0;
1252 		}
1253 		cpointer().reset_cursor();
1254 	} else if (holdingCommand) {
1255 
1256 		cpointer().reset_cursor();
1257 
1258 		int holdFinish = holdingCommand->finish_hold();
1259 		int holdprepare = -1;
1260 
1261 		if (holdFinish > 0) {
1262 			holdprepare = holdingCommand->prepare_actions();
1263 			if (holdprepare > 0) {
1264 				PMESG("holdingCommand->prepare_actions() returned succes!");
1265 				holdingCommand->set_valid(true);
1266 			} else {
1267 				PMESG("holdingCommand->prepare_actions() returned <= 0, so either it failed, or nothing happened!");
1268 				holdingCommand->set_valid( false );
1269 			}
1270 		} else {
1271 			PMESG("holdingCommand->finish_hold() returned <= 0, so either it failed, or nothing happened!");
1272 			holdingCommand->set_valid( false );
1273 		}
1274 
1275 		if (holdingCommand->push_to_history_stack() < 0) {
1276 			if (holdprepare == 1) {
1277 				holdingCommand->do_action();
1278 			}
1279 			delete holdingCommand;
1280 		}
1281 
1282 		holdingCommand = 0;
1283 	}
1284 
1285 	set_jogging(false);
1286 	conclusion();
1287 }
1288 
1289 
conclusion()1290 void InputEngine::conclusion()
1291 {
1292 	PENTER3;
1293 	reset();
1294 	hold_output();
1295 }
1296 
1297 
hold_output()1298 void InputEngine::hold_output()
1299 {
1300 	PENTER3;
1301 	if (!isHoldingOutput) {
1302 		catcher.clearOutputTimer.start(clearTime);
1303 		isHoldingOutput=true;
1304 	}
1305 }
1306 
1307 
init_map(const QString & keymap)1308 int InputEngine::init_map(const QString& keymap)
1309 {
1310 	PENTER;
1311 
1312 	QString filename = ":/keymaps/" + keymap + ".xml";
1313 	if ( ! QFile::exists(filename)) {
1314 		filename = QDir::homePath() + "/.traverso/keymaps/" + keymap + ".xml";
1315 	}
1316 
1317 	QDomDocument doc("keymap");
1318 	QFile file(filename);
1319 	if (!file.open(QIODevice::ReadOnly))
1320 		return -1;
1321 	if (!doc.setContent(&file)) {
1322 		file.close();
1323 		return -1;
1324 	}
1325 	file.close();
1326 
1327 	PMESG("Using keymap: %s", QS_C(keymap));
1328 
1329 	foreach(IEAction* action, m_ieActions) {
1330 		delete action;
1331 	}
1332 
1333 	m_ieActions.clear();
1334 	m_modifierKeys.clear();
1335 	m_modes.clear();
1336 
1337 
1338 	QDomElement root = doc.documentElement();
1339 
1340 
1341 	QDomNode modifierKeysNode = root.firstChildElement("ModifierKeys");
1342 	QDomNode modifierKeyNode = modifierKeysNode.firstChild();
1343 
1344 	int keycode;
1345 	QString key;
1346 
1347 	while( !modifierKeyNode.isNull() ) {
1348 		QDomElement e = modifierKeyNode.toElement();
1349 
1350 		key = e.attribute( "key", "");
1351 
1352 		set_hexcode(keycode, key);
1353 		m_modifierKeys.append(keycode);
1354 
1355 		modifierKeyNode = modifierKeyNode.nextSibling();
1356 	}
1357 
1358 
1359 
1360 	QDomNode modesNode = root.firstChildElement("Modes");
1361 	QDomNode modeNode = modesNode.firstChild();
1362 
1363 	int id;
1364 	QString modename;
1365 
1366 	while( !modeNode.isNull() ) {
1367 		QDomElement e = modeNode.toElement();
1368 
1369 		modename = e.attribute("name", "");
1370 		id = e.attribute("id", "-1").toInt();
1371 
1372 		m_modes.insert(modename, id);
1373 
1374 		modeNode = modeNode.nextSibling();
1375 	}
1376 
1377 
1378 
1379 
1380 	QDomNode keyfactsNode = root.firstChildElement("Keyfacts");
1381 	QDomNode keyfactNode = keyfactsNode.firstChild();
1382 
1383 	QString keyFactType;
1384 	QString key1, key2, key3, key4, mouseHint, modifierKeys;
1385 	IEAction::Data* data;
1386 
1387 	while( !keyfactNode.isNull() ) {
1388 		QDomElement e = keyfactNode.toElement();
1389 
1390 		if( e.isNull() ) {
1391 			continue;
1392 		}
1393 
1394 		if( ! (e.tagName() == "keyfact" ) ) {
1395 			PERROR("Detected wrong tagname, misspelled: keyfact !!");
1396 			continue;
1397 		}
1398 
1399 		IEAction* action = new IEAction();
1400 
1401 		keyFactType = e.attribute( "type", "" );
1402 		key1 = e.attribute( "key1", "");
1403 		key2 = e.attribute( "key2", "" );
1404 
1405 		if (keyFactType == "FKEY")
1406 			action->type = FKEY;
1407 		else if (keyFactType == "FKEY2")
1408 			action->type = FKEY2;
1409 		else if (keyFactType == "HKEY")
1410 			action->type = HOLDKEY;
1411 		else if (keyFactType == "HKEY2")
1412 			action->type = HKEY2;
1413 		else if (keyFactType == "D_FKEY")
1414 			action->type = D_FKEY;
1415 		else if (keyFactType == "D_FKEY2")
1416 			action->type = D_FKEY2;
1417 		else if (keyFactType == "S_FKEY_FKEY")
1418 			action->type = S_FKEY_FKEY;
1419 		else {
1420 			PWARN("keyFactType not supported!");
1421 		}
1422 
1423 		set_hexcode(action->fact1_key1, key1);
1424 		set_hexcode(action->fact1_key2, key2);
1425 		set_hexcode(action->fact2_key1, key3);
1426 		set_hexcode(action->fact2_key2, key4);
1427 
1428 
1429 		// Fix the keyCode positions
1430 		if  (( action->type == D_FKEY  ) || ( action->type == FHKEY  )) {
1431 			action->fact2_key1=action->fact1_key1;
1432 		} else if (( action->type == D_FKEY2 ) || ( action->type == FHKEY  )) {
1433 			action->fact2_key1=action->fact1_key1;
1434 			action->fact2_key2=action->fact1_key2;
1435 		} else if (( action->type == S_FKEY_FKEY ) || ( action->type == S_FKEY_HKEY )) {
1436 			action->fact2_key1=action->fact1_key2;
1437 			action->fact1_key2=0;
1438 		}
1439 
1440 
1441 		QDomElement objectsNode = e.firstChildElement("Objects");
1442 		QDomNode objectNode = objectsNode.firstChild();
1443 
1444 		while(!objectNode.isNull()) {
1445 			data = new IEAction::Data;
1446 
1447 			QDomElement e = objectNode.toElement();
1448 
1449 			QString objectname = e.attribute("objectname", "");
1450 
1451 			data->slotsignature = e.attribute("slotsignature", "");
1452 			data->modes = e.attribute("modes", "").split(";");
1453 			data->pluginname = e.attribute( "pluginname", "");
1454 			data->commandname = e.attribute( "commandname", "");
1455 			data->submenu = e.attribute("submenu", "");
1456 			data->sortorder = e.attribute( "sortorder", "0").toInt();
1457 			mouseHint = e.attribute( "mousehint", "" );
1458 			QString args = e.attribute("arguments", "");
1459 			modifierKeys = e.attribute("modifierkeys", "");
1460 
1461 			if ( ! args.isEmpty() ) {
1462 				QStringList arglist = args.split(";");
1463 				for (int i=0; i<arglist.size(); ++i) {
1464 					data->arguments.append(arglist.at(i));
1465 				}
1466 			}
1467 
1468 			if (! modifierKeys.isEmpty()) {
1469 				QStringList modifierlist = modifierKeys.split(";");
1470 				for (int i=0; i<modifierlist.size(); ++i) {
1471 					int keycode;
1472 					set_hexcode(keycode, modifierlist.at(i));
1473 					data->modifierkeys.append(keycode);
1474 				}
1475 			}
1476 
1477 			data->useX = data->useY = false;
1478 
1479 			if (mouseHint == "LR") {
1480 				data->useX = true;
1481 			}
1482 			if (mouseHint == "UD") {
1483 				data->useY = true;
1484 			}
1485 			if (mouseHint == "LRUD") {
1486 				data->useX = data->useY = true;
1487 			}
1488 
1489 			if (QString(objectname) == "") {
1490 				PERROR("no objectname given in keyaction %s", QS_C(keyFactType));
1491 			}
1492 			if (data->slotsignature.isEmpty() && data->pluginname.isEmpty()) {
1493 				PERROR("no slotsignature given in keyaction %s, object %s", QS_C(keyFactType), QS_C(objectname));
1494 			}
1495 			if (QString(data->modes.join(";")) == "") {
1496 				PERROR("no modes given in keyaction %s, object %s", QS_C(keyFactType), QS_C(objectname));
1497 			}
1498 
1499 			if (modifierKeys.isEmpty()) {
1500 				action->objects.insert(objectname, data);
1501 			} else {
1502 				action->objectUsingModifierKeys.insert(objectname, data);
1503 			}
1504 
1505 			objectNode = objectNode.nextSibling();
1506 		}
1507 
1508 		action->isInstantaneous = false;
1509 		action->render_key_sequence(key1, key2);
1510 
1511 		bool exists = false;
1512 		for (int i=0; i<m_ieActions.size(); ++i) {
1513 		 	IEAction* existingaction = m_ieActions.at(i);
1514 			if ( 	(action->fact1_key1 == existingaction->fact1_key1) &&
1515 				(action->fact1_key2 == existingaction->fact1_key2) &&
1516 				(action->fact2_key1 == existingaction->fact2_key1) &&
1517 				(action->fact2_key2 == existingaction->fact2_key2) &&
1518 				(action->type == existingaction->type) ) {
1519 				exists = true;
1520 				QString errorstring = QString("InputEngine:: keyfact with: type=%1, key1='%2', key2='%3' already exists!\n"
1521 						"You should only define keyfact types one time!!\n").arg(keyFactType).arg(key1).arg(key2);
1522 				printf(QS_C(errorstring));
1523 				info().warning(errorstring);
1524 				break;
1525 			}
1526 
1527 		}
1528 
1529 		if (!exists) {
1530 			m_ieActions.append(action);
1531 			PMESG2("ADDED action: type=%d keys=%d,%d,%d,%d useX=%d useY=%d, slot=%s", action->type, action->fact1_key1,action->fact1_key2,action->fact2_key1,action->fact2_key2,data->useX,data->useY, QS_C(data->slotsignature));
1532 		}
1533 
1534 		keyfactNode = keyfactNode.nextSibling();
1535 	}
1536 
1537 
1538 	PMESG2("Optimizing map for best performance ...");
1539 	int optimizedActions=0;
1540 	foreach(IEAction* action, m_ieActions) {
1541 		int c1A = action->fact1_key1;
1542 		int c2A = action->fact1_key2;
1543 
1544 		if ( (action->type == FKEY) ) {
1545 			bool alone = true;
1546 			foreach(IEAction* aloneAction, m_ieActions) {
1547 				if (action == aloneAction)
1548 					continue;
1549 				int tt    = aloneAction->type;
1550 				int t1A   = aloneAction->fact1_key1;
1551 				if  (
1552 					(t1A==c1A)  &&
1553 					(
1554 						(tt==D_FKEY)       ||
1555 						(tt==FHKEY)        ||
1556 						(tt==S_FKEY_FKEY)  ||
1557 						(tt==S_FKEY_FKEY2) ||
1558 						(tt==S_FKEY_HKEY)  ||
1559 						(tt==S_FKEY_HKEY2)
1560 					)
1561 				)
1562 					alone=false;
1563 			}
1564 			if (alone) {
1565 				PMESG3("Setting <K> fact (slot=%s) as protected (Instantaneous response)", action->keySequence.data());
1566 				action->isInstantaneous = true;
1567 				optimizedActions++;
1568 			} else
1569 				action->isInstantaneous = false;
1570 
1571 		} else if ((action->type == FKEY2)) {
1572 			bool alone = true;
1573 			foreach(IEAction* aloneAction, m_ieActions) {
1574 				if (action == aloneAction)
1575 					continue;
1576 				int tt    = aloneAction->type;
1577 				int t1A   = aloneAction->fact1_key1;
1578 				int t2A   = aloneAction->fact1_key2;
1579 				if (
1580 					((t1A==c1A) && (t2A==c2A)) &&
1581 					(
1582 						(tt==HOLDKEY)         ||
1583 						(tt==D_FKEY2)      ||
1584 						(tt==S_FKEY2_FKEY) ||
1585 						(tt==S_FKEY2_FKEY2)||
1586 						(tt==S_FKEY2_HKEY) ||
1587 						(tt==S_FKEY2_HKEY2)
1588 					)
1589 				)
1590 					alone=false;
1591 			}
1592 			if (alone) {
1593 				PMESG3("Setting <KK> fact (slot=%s) for instantaneous response", action->keySequence.data());
1594 				action->isInstantaneous = true;
1595 				optimizedActions++;
1596 			} else
1597 				action->isInstantaneous = false;
1598 		}
1599 	}
1600 	PMESG2("Keymap initialized! %d actions registered ( %d instantanious) .", m_ieActions.size(), optimizedActions);
1601 
1602 	return 1;
1603 }
1604 
1605 
set_clear_time(int time)1606 void InputEngine::set_clear_time(int time)
1607 {
1608 	clearTime = time;
1609 }
1610 
set_hold_sensitiveness(int htime)1611 void InputEngine::set_hold_sensitiveness(int htime)
1612 {
1613 	assumeHoldTime=htime;
1614 }
1615 
set_double_fact_interval(int time)1616 void InputEngine::set_double_fact_interval(int time)
1617 {
1618     doubleFactWaitTime = time;
1619 }
1620 
register_command_plugin(CommandPlugin * plugin,const QString & pluginName)1621 void InputEngine::register_command_plugin(CommandPlugin *plugin, const QString &pluginName)
1622 {
1623     m_commandplugins.insert(pluginName, plugin);
1624 }
1625 
1626 
1627 // Number colector
check_number_collection(int eventcode)1628 bool InputEngine::check_number_collection(int eventcode)
1629 {
1630 	if (((eventcode >= Qt::Key_0) && (eventcode <= Qt::Key_9)) ||
1631 	     (eventcode == Qt::Key_Comma) || (eventcode == Qt::Key_Period)) {
1632 		sCollectedNumber.append( QChar(eventcode) ); // it had a ",1" complement after fact1_k1... why?
1633 		PMESG("Collected %s so far...", QS_C(sCollectedNumber) ) ;
1634 		QString sn = "NUMBER " + sCollectedNumber;
1635 		collectedNumber = sCollectedNumber.toInt();
1636 		if (holdingCommand) {
1637 			holdingCommand->set_collected_number(sCollectedNumber);
1638 		}
1639 		return true;
1640 	}
1641 	if (eventcode == Qt::Key_Backspace) {
1642 		if (sCollectedNumber.size() > 0) {
1643 			sCollectedNumber = sCollectedNumber.left(sCollectedNumber.size() - 1);
1644 			if (holdingCommand) {
1645 				holdingCommand->set_collected_number(sCollectedNumber);
1646 			}
1647 		}
1648 		return true;
1649 	}
1650 	if (eventcode == Qt::Key_Minus) {
1651 		if (sCollectedNumber.contains("-")) {
1652 			sCollectedNumber = sCollectedNumber.remove("-");
1653 		} else {
1654 			sCollectedNumber.prepend("-");
1655 		}
1656 		if (holdingCommand) {
1657 			holdingCommand->set_collected_number(sCollectedNumber);
1658 		}
1659 	}
1660 	return false;
1661 }
1662 
stop_collecting()1663 void InputEngine::stop_collecting()
1664 {
1665 	PENTER3;
1666 	collectedNumber = sCollectedNumber.toInt();
1667 	sCollectedNumber = "";
1668 }
1669 
collected_number()1670 int InputEngine::collected_number( )
1671 {
1672 	int n = collectedNumber;
1673 	sCollectedNumber = "-1"; // collectedNumber has a life of only one get.
1674 	return n;
1675 }
1676 
is_holding()1677 bool InputEngine::is_holding( )
1678 {
1679 	return isHolding;
1680 }
1681 
get_holding_command() const1682 Command * InputEngine::get_holding_command() const
1683 {
1684 	return holdingCommand;
1685 }
1686 
create_menudata_for_metaobject(const QMetaObject * mo,QList<MenuData> & list) const1687 void InputEngine::create_menudata_for_metaobject(const QMetaObject * mo, QList< MenuData > & list) const
1688 {
1689 	const char* classname = mo->className();
1690 
1691 	for (int i=0; i<m_ieActions.size(); i++) {
1692 		IEAction* ieaction = m_ieActions.at(i);
1693 
1694 		QList<IEAction::Data*> datalist;
1695 		datalist.append(ieaction->objects.value(classname));
1696 		datalist.append(ieaction->objectUsingModifierKeys.value(classname));
1697 
1698 		foreach(IEAction::Data* iedata, datalist) {
1699 
1700 			if ( ! iedata ) {
1701 				continue;
1702 			}
1703 
1704 			MenuData menudata;
1705 
1706 			if ( ! iedata->pluginname.isEmpty() ) {
1707 				CommandPlugin* plug = m_commandplugins.value(iedata->pluginname);
1708 
1709 				if ( ! plug) {
1710 					continue;
1711 				}
1712 				int classInfoIndex = plug->metaObject()->indexOfClassInfo(QS_C(iedata->commandname));
1713 				if (classInfoIndex >= 0) {
1714 					QMetaClassInfo classInfo = plug->metaObject()->classInfo(classInfoIndex);
1715 						// Set the translated string!
1716 					menudata.description = QCoreApplication::translate(classname, classInfo.value());
1717 				} else {
1718 					menudata.description =
1719 							QString("Add a Q_CLASSINFO() in CommandPlug %1.h, Command %2 please")
1720 							.arg(iedata->pluginname).arg(iedata->commandname);
1721 					PWARN("%s", QS_C(menudata.description));
1722 				}
1723 
1724 			} else {
1725 				int classInfoIndex = mo->indexOfClassInfo(QS_C(iedata->slotsignature));
1726 				int methodIndex = -1;
1727 
1728 				for (int i=0; i < mo->methodCount(); i++) {
1729 
1730 					if ( ! (mo->method(i).methodType() == QMetaMethod::Slot) ) {
1731 						continue;
1732 					}
1733 
1734                     QString slotsignature = mo->method(i).methodSignature();
1735 					slotsignature = slotsignature.left(slotsignature.indexOf("("));
1736 					if (iedata->slotsignature == slotsignature) {
1737 						methodIndex = i;
1738 						break;
1739 					}
1740 				}
1741 
1742 
1743 				if (classInfoIndex >= 0 && methodIndex >= 0) {
1744 					QMetaClassInfo classInfo = mo->classInfo(classInfoIndex);
1745 						// Set the translated string!
1746 					menudata.description = QCoreApplication::translate(classname, classInfo.value());
1747 				} else {
1748 					if (methodIndex >= 0) {
1749 						menudata.description = QString("Add a Q_CLASSINFO() for %1::%2 please")
1750 								.arg(classname).arg(iedata->slotsignature);
1751 						PWARN("%s", QS_C(menudata.description));
1752 					} else {
1753 						continue;
1754 					}
1755 				}
1756 			}
1757 
1758 			menudata.keysequence = ieaction->keySequence;
1759 			menudata.iedata = ieaction->keySequence;
1760 			menudata.sortorder = iedata->sortorder;
1761 			menudata.submenu = iedata->submenu;
1762 			menudata.modifierkeys = iedata->modifierkeys;
1763 			if (menudata.modifierkeys.size()) {
1764 				menudata.iedata.prepend("++");
1765 			}
1766 
1767 
1768 			list.append(menudata);
1769 		}
1770 	}
1771 }
1772 
create_menudata_for(QObject * item)1773 QList< MenuData > InputEngine::create_menudata_for(QObject* item)
1774 {
1775 	QList<MenuData > list;
1776 	ContextItem* contextitem;
1777 
1778 	do {
1779 		const QMetaObject* mo = item->metaObject();
1780 
1781 		create_menudata_for_metaobject(mo, list);
1782 
1783 		contextitem = qobject_cast<ContextItem*>(item);
1784 	}
1785 	while (contextitem && (item = contextitem->get_context()) );
1786 
1787 
1788 	return list;
1789 }
1790 
1791 
1792 //---------- EVENT CATCHER -------------
1793 
1794 
1795 
EventCatcher()1796 EventCatcher::EventCatcher()
1797 {
1798 	holdTimer.setSingleShot(true);
1799 	connect( &holdTimer, SIGNAL(timeout()), this, SLOT(assume_hold()));
1800 	connect( &secondChanceTimer, SIGNAL(timeout()), this, SLOT(quit_second_chance()));
1801 	connect( &clearOutputTimer, SIGNAL(timeout()), this, SLOT(clear_output()));
1802 }
1803 
1804 
assume_hold()1805 void EventCatcher::assume_hold() // no release so far ? so consider it a hold...
1806 {
1807 	PENTER3;
1808 	PMESG3("No release so far (waited %d ms). Assuming this is a hold and dispatching it",ie().assumeHoldTime);
1809 	holdTimer.stop(); // quit the holding check..
1810 	ie().isHolding = true;
1811 	ie().dispatch_hold();
1812 }
1813 
clear_output()1814 void EventCatcher::clear_output()
1815 {
1816 	PENTER3;
1817 	if ((ie().isHoldingOutput)) {
1818 		clearOutputTimer.stop();
1819 		ie().isHoldingOutput=false;
1820 	}
1821 }
1822 
quit_second_chance()1823 void EventCatcher::quit_second_chance()
1824 {
1825 	PENTER3;
1826 	secondChanceTimer.stop();
1827 	if (!ie().isHolding) // if it is holding, there is no need to push a new fact
1828 	{
1829 		PMESG3("No second fact (waited %d ms) ... Forcing a null second fact",ie().doubleFactWaitTime);
1830 		ie().push_fact(0,0); // no second press performed, so I am adding a pair of Zeros as second press and keep going...
1831 	}
1832 }
1833 
1834 
render_key_sequence(const QString & key1,const QString & key2)1835 void IEAction::render_key_sequence(const QString& key1, const QString& key2)
1836 {
1837 	switch(type) {
1838 	case	FKEY:
1839 		keySequence = QString("< " + key1 + " >").toLatin1().data();
1840 		break;
1841 	case	FKEY2:
1842 		keySequence = QString("< " + key1 + " " + key2 + " >").toLatin1().data();
1843 		break;
1844 	case	HOLDKEY:
1845 		keySequence = QString("[ " + key1 + " ]").toLatin1().data();
1846 		break;
1847 	case	HKEY2:
1848 		keySequence = QString("[ " + key1 +  " " + key2 + " ]").toLatin1().data();
1849 		break;
1850 	case	D_FKEY:
1851 		keySequence = QString("<< " + key1 + " >>").toLatin1().data();
1852 		break;
1853 	case	D_FKEY2:
1854 		keySequence = QString("<< " + key1 + " " + key2 + " >>").toLatin1().data();
1855 		break;
1856 	case	S_FKEY_FKEY:
1857 		keySequence = QString("> " + key1 + " > " + key2).toLatin1().data();
1858 		break;
1859 	default	:
1860 		keySequence = "Unknown Key Sequence";
1861 	}
1862 }
1863 
~IEAction()1864 IEAction::~ IEAction()
1865 {
1866 	foreach(Data* data, objects) {
1867 		delete data;
1868 	}
1869 	foreach(Data* data, objectUsingModifierKeys) {
1870 		delete data;
1871 	}
1872 }
1873 
1874