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