1/*
2 This source file is part of Konsole, a terminal emulator.
3
4 Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
5
6 This program 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 Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20*/
21
22#ifndef KEYBOARDTRANSLATOR_H
23#define KEYBOARDTRANSLATOR_H
24
25// Qt
26#include <QHash>
27#include <QList>
28#include <QKeySequence>
29#include <QMetaType>
30#include <QVarLengthArray>
31
32// Konsole
33//#include "konsole_export.h"
34#define KONSOLEPRIVATE_EXPORT
35
36class QIODevice;
37class QTextStream;
38
39namespace Konsole
40{
41
42/**
43 * A convertor which maps between key sequences pressed by the user and the
44 * character strings which should be sent to the terminal and commands
45 * which should be invoked when those character sequences are pressed.
46 *
47 * Konsole supports multiple keyboard translators, allowing the user to
48 * specify the character sequences which are sent to the terminal
49 * when particular key sequences are pressed.
50 *
51 * A key sequence is defined as a key code, associated keyboard modifiers
52 * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state
53 * which the terminal must be in for the key sequence to apply.
54 */
55class KeyboardTranslator
56{
57public:
58 /**
59 * The meaning of a particular key sequence may depend upon the state which
60 * the terminal emulation is in. Therefore findEntry() may return a different
61 * Entry depending upon the state flags supplied.
62 *
63 * This enum describes the states which may be associated with with a particular
64 * entry in the keyboard translation entry.
65 */
66 enum State
67 {
68 /** Indicates that no special state is active */
69 NoState = 0,
70 /**
71 * TODO More documentation
72 */
73 NewLineState = 1,
74 /**
75 * Indicates that the terminal is in 'Ansi' mode.
76 * TODO: More documentation
77 */
78 AnsiState = 2,
79 /**
80 * TODO More documentation
81 */
82 CursorKeysState = 4,
83 /**
84 * Indicates that the alternate screen ( typically used by interactive programs
85 * such as screen or vim ) is active
86 */
87 AlternateScreenState = 8,
88 /** Indicates that any of the modifier keys is active. */
89 AnyModifierState = 16,
90 /** Indicates that the numpad is in application mode. */
91 ApplicationKeypadState = 32
92 };
93 Q_DECLARE_FLAGS(States,State)
94
95 /**
96 * This enum describes commands which are associated with particular key sequences.
97 */
98 enum Command
99 {
100 /** Indicates that no command is associated with this command sequence */
101 NoCommand = 0,
102 /** TODO Document me */
103 SendCommand = 1,
104 /** Scroll the terminal display up one page */
105 ScrollPageUpCommand = 2,
106 /** Scroll the terminal display down one page */
107 ScrollPageDownCommand = 4,
108 /** Scroll the terminal display up one line */
109 ScrollLineUpCommand = 8,
110 /** Scroll the terminal display down one line */
111 ScrollLineDownCommand = 16,
112 /** Toggles scroll lock mode */
113 ScrollLockCommand = 32,
114 /** Scroll the terminal display up to the start of history */
115 ScrollUpToTopCommand = 64,
116 /** Scroll the terminal display down to the end of history */
117 ScrollDownToBottomCommand = 128,
118 /** Echos the operating system specific erase character. */
119 EraseCommand = 256
120 };
121 Q_DECLARE_FLAGS(Commands,Command)
122
123 /**
124 * Represents an association between a key sequence pressed by the user
125 * and the character sequence and commands associated with it for a particular
126 * KeyboardTranslator.
127 */
128 class Entry
129 {
130 public:
131 /**
132 * Constructs a new entry for a keyboard translator.
133 */
134 Entry();
135
136 /**
137 * Returns true if this entry is null.
138 * This is true for newly constructed entries which have no properties set.
139 */
140 bool isNull() const;
141
142 /** Returns the commands associated with this entry */
143 Command command() const;
144 /** Sets the command associated with this entry. */
145 void setCommand(Command command);
146
147 /**
148 * Returns the character sequence associated with this entry, optionally replacing
149 * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed.
150 *
151 * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code.
152 * Document them.
153 *
154 * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in
155 * the entry should be replaced with a number to indicate the modifier keys being pressed.
156 *
157 * @param modifiers The keyboard modifiers being pressed.
158 */
159 QByteArray text(bool expandWildCards = false,
160 Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
161
162 /** Sets the character sequence associated with this entry */
163 void setText(const QByteArray& text);
164
165 /**
166 * Returns the character sequence associated with this entry,
167 * with any non-printable characters replaced with escape sequences.
168 *
169 * eg. \\E for Escape, \\t for tab, \\n for new line.
170 *
171 * @param expandWildCards See text()
172 * @param modifiers See text()
173 */
174 QByteArray escapedText(bool expandWildCards = false,
175 Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
176
177 /** Returns the character code ( from the Qt::Key enum ) associated with this entry */
178 int keyCode() const;
179 /** Sets the character code associated with this entry */
180 void setKeyCode(int keyCode);
181
182 /**
183 * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry.
184 * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry
185 * only matches when that modifier is NOT pressed.
186 *
187 * If a modifier is not set in modifierMask() then the entry matches whether the modifier
188 * is pressed or not.
189 */
190 Qt::KeyboardModifiers modifiers() const;
191
192 /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */
193 Qt::KeyboardModifiers modifierMask() const;
194
195 /** See modifiers() */
196 void setModifiers( Qt::KeyboardModifiers modifiers );
197 /** See modifierMask() and modifiers() */
198 void setModifierMask( Qt::KeyboardModifiers modifiers );
199
200 /**
201 * Returns a bitwise-OR of the enabled state flags associated with this entry.
202 * If flag is set in stateMask() but not in state(), this means that the entry only
203 * matches when the terminal is NOT in that state.
204 *
205 * If a state is not set in stateMask() then the entry matches whether the terminal
206 * is in that state or not.
207 */
208 States state() const;
209
210 /** Returns the state flags which are valid in this entry. See state() */
211 States stateMask() const;
212
213 /** See state() */
214 void setState( States state );
215 /** See stateMask() */
216 void setStateMask( States mask );
217
218 /**
219 * Returns the key code and modifiers associated with this entry
220 * as a QKeySequence
221 */
222 //QKeySequence keySequence() const;
223
224 /**
225 * Returns this entry's conditions ( ie. its key code, modifier and state criteria )
226 * as a string.
227 */
228 QString conditionToString() const;
229
230 /**
231 * Returns this entry's result ( ie. its command or character sequence )
232 * as a string.
233 *
234 * @param expandWildCards See text()
235 * @param modifiers See text()
236 */
237 QString resultToString(bool expandWildCards = false,
238 Qt::KeyboardModifiers modifiers = Qt::NoModifier) const;
239
240 /**
241 * Returns true if this entry matches the given key sequence, specified
242 * as a combination of @p keyCode , @p modifiers and @p state.
243 */
244 bool matches( int keyCode ,
245 Qt::KeyboardModifiers modifiers ,
246 States flags ) const;
247
248 bool operator==(const Entry& rhs) const;
249
250 private:
251 void insertModifier( QString& item , int modifier ) const;
252 void insertState( QString& item , int state ) const;
253 QByteArray unescape(const QByteArray& text) const;
254
255 int _keyCode;
256 Qt::KeyboardModifiers _modifiers;
257 Qt::KeyboardModifiers _modifierMask;
258 States _state;
259 States _stateMask;
260
261 Command _command;
262 QByteArray _text;
263 };
264
265 /** Constructs a new keyboard translator with the given @p name */
266 KeyboardTranslator(const QString& name);
267
268 //KeyboardTranslator(const KeyboardTranslator& other);
269
270 /** Returns the name of this keyboard translator */
271 QString name() const;
272
273 /** Sets the name of this keyboard translator */
274 void setName(const QString& name);
275
276 /** Returns the descriptive name of this keyboard translator */
277 QString description() const;
278
279 /** Sets the descriptive name of this keyboard translator */
280 void setDescription(const QString& description);
281
282 /**
283 * Looks for an entry in this keyboard translator which matches the given
284 * key code, keyboard modifiers and state flags.
285 *
286 * Returns the matching entry if found or a null Entry otherwise ( ie.
287 * entry.isNull() will return true )
288 *
289 * @param keyCode A key code from the Qt::Key enum
290 * @param modifiers A combination of modifiers
291 * @param state Optional flags which specify the current state of the terminal
292 */
293 Entry findEntry(int keyCode ,
294 Qt::KeyboardModifiers modifiers ,
295 States state = NoState) const;
296
297 /**
298 * Adds an entry to this keyboard translator's table. Entries can be looked up according
299 * to their key sequence using findEntry()
300 */
301 void addEntry(const Entry& entry);
302
303 /**
304 * Replaces an entry in the translator. If the @p existing entry is null,
305 * then this is equivalent to calling addEntry(@p replacement)
306 */
307 void replaceEntry(const Entry& existing , const Entry& replacement);
308
309 /**
310 * Removes an entry from the table.
311 */
312 void removeEntry(const Entry& entry);
313
314 /** Returns a list of all entries in the translator. */
315 QList<Entry> entries() const;
316
317private:
318
319 QMultiHash<int,Entry> _entries; // entries in this keyboard translation,
320 // entries are indexed according to
321 // their keycode
322 QString _name;
323 QString _description;
324};
325Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States)
326Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands)
327
328/**
329 * Parses the contents of a Keyboard Translator (.keytab) file and
330 * returns the entries found in it.
331 *
332 * Usage example:
333 *
334 * @code
335 * QFile source( "/path/to/keytab" );
336 * source.open( QIODevice::ReadOnly );
337 *
338 * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" );
339 *
340 * KeyboardTranslatorReader reader(source);
341 * while ( reader.hasNextEntry() )
342 * translator->addEntry(reader.nextEntry());
343 *
344 * source.close();
345 *
346 * if ( !reader.parseError() )
347 * {
348 * // parsing succeeded, do something with the translator
349 * }
350 * else
351 * {
352 * // parsing failed
353 * }
354 * @endcode
355 */
356class KeyboardTranslatorReader
357{
358public:
359 /** Constructs a new reader which parses the given @p source */
360 KeyboardTranslatorReader( QIODevice* source );
361
362 /**
363 * Returns the description text.
364 * TODO: More documentation
365 */
366 QString description() const;
367
368 /** Returns true if there is another entry in the source stream */
369 bool hasNextEntry();
370 /** Returns the next entry found in the source stream */
371 KeyboardTranslator::Entry nextEntry();
372
373 /**
374 * Returns true if an error occurred whilst parsing the input or
375 * false if no error occurred.
376 */
377 bool parseError();
378
379 /**
380 * Parses a condition and result string for a translator entry
381 * and produces a keyboard translator entry.
382 *
383 * The condition and result strings are in the same format as in
384 */
385 static KeyboardTranslator::Entry createEntry( const QString& condition ,
386 const QString& result );
387private:
388 struct Token
389 {
390 enum Type
391 {
392 TitleKeyword,
393 TitleText,
394 KeyKeyword,
395 KeySequence,
396 Command,
397 OutputText
398 };
399 Type type;
400 QString text;
401 };
402 QList<Token> tokenize(const QString&);
403 void readNext();
404 bool decodeSequence(const QString& ,
405 int& keyCode,
406 Qt::KeyboardModifiers& modifiers,
407 Qt::KeyboardModifiers& modifierMask,
408 KeyboardTranslator::States& state,
409 KeyboardTranslator::States& stateFlags);
410
411 static bool parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier);
412 static bool parseAsStateFlag(const QString& item , KeyboardTranslator::State& state);
413 static bool parseAsKeyCode(const QString& item , int& keyCode);
414 static bool parseAsCommand(const QString& text , KeyboardTranslator::Command& command);
415
416 QIODevice* _source;
417 QString _description;
418 KeyboardTranslator::Entry _nextEntry;
419 bool _hasNext;
420};
421
422/** Writes a keyboard translation to disk. */
423class KeyboardTranslatorWriter
424{
425public:
426 /**
427 * Constructs a new writer which saves data into @p destination.
428 * The caller is responsible for closing the device when writing is complete.
429 */
430 KeyboardTranslatorWriter(QIODevice* destination);
431 ~KeyboardTranslatorWriter();
432
433 /**
434 * Writes the header for the keyboard translator.
435 * @param description Description of the keyboard translator.
436 */
437 void writeHeader( const QString& description );
438 /** Writes a translator entry. */
439 void writeEntry( const KeyboardTranslator::Entry& entry );
440
441private:
442 QIODevice* _destination;
443 QTextStream* _writer;
444};
445
446/**
447 * Manages the keyboard translations available for use by terminal sessions,
448 * see KeyboardTranslator.
449 */
450class KONSOLEPRIVATE_EXPORT KeyboardTranslatorManager
451{
452public:
453 /**
454 * Constructs a new KeyboardTranslatorManager and loads the list of
455 * available keyboard translations.
456 *
457 * The keyboard translations themselves are not loaded until they are
458 * first requested via a call to findTranslator()
459 */
460 KeyboardTranslatorManager();
461 ~KeyboardTranslatorManager();
462
463 KeyboardTranslatorManager(const KeyboardTranslatorManager&) = delete;
464 KeyboardTranslatorManager& operator=(const KeyboardTranslatorManager&) = delete;
465
466 /**
467 * Adds a new translator. If a translator with the same name
468 * already exists, it will be replaced by the new translator.
469 *
470 * TODO: More documentation.
471 */
472 void addTranslator(KeyboardTranslator* translator);
473
474 /**
475 * Deletes a translator. Returns true on successful deletion or false otherwise.
476 *
477 * TODO: More documentation
478 */
479 bool deleteTranslator(const QString& name);
480
481 /** Returns the default translator for Konsole. */
482 const KeyboardTranslator* defaultTranslator();
483
484 /**
485 * Returns the keyboard translator with the given name or 0 if no translator
486 * with that name exists.
487 *
488 * The first time that a translator with a particular name is requested,
489 * the on-disk .keyboard file is loaded and parsed.
490 */
491 const KeyboardTranslator* findTranslator(const QString& name);
492 /**
493 * Returns a list of the names of available keyboard translators.
494 *
495 * The first time this is called, a search for available
496 * translators is started.
497 */
498 QList<QString> allTranslators();
499
500 /** Returns the global KeyboardTranslatorManager instance. */
501 static KeyboardTranslatorManager* instance();
502
503private:
504 static const QByteArray defaultTranslatorText;
505
506 void findTranslators(); // locate the available translators
507 KeyboardTranslator* loadTranslator(const QString& name); // loads the translator
508 // with the given name
509 KeyboardTranslator* loadTranslator(QIODevice* device,const QString& name);
510
511 bool saveTranslator(const KeyboardTranslator* translator);
512 QString findTranslatorPath(const QString& name);
513
514 QHash<QString,KeyboardTranslator*> _translators; // maps translator-name -> KeyboardTranslator
515 // instance
516 bool _haveLoadedAll;
517};
518
519inline int KeyboardTranslator::Entry::keyCode() const { return _keyCode; }
520inline void KeyboardTranslator::Entry::setKeyCode(int keyCode) { _keyCode = keyCode; }
521
522inline void KeyboardTranslator::Entry::setModifiers( Qt::KeyboardModifiers modifier )
523{
524 _modifiers = modifier;
525}
526inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const { return _modifiers; }
527
528inline void KeyboardTranslator::Entry::setModifierMask( Qt::KeyboardModifiers mask )
529{
530 _modifierMask = mask;
531}
532inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const { return _modifierMask; }
533
534inline bool KeyboardTranslator::Entry::isNull() const
535{
536 return ( *this == Entry() );
537}
538
539inline void KeyboardTranslator::Entry::setCommand( Command command )
540{
541 _command = command;
542}
543inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const { return _command; }
544
545inline void KeyboardTranslator::Entry::setText( const QByteArray& text )
546{
547 _text = unescape(text);
548}
549inline int oneOrZero(int value)
550{
551 return value ? 1 : 0;
552}
553inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
554{
555 QByteArray expandedText = _text;
556
557 if (expandWildCards)
558 {
559 int modifierValue = 1;
560 modifierValue += oneOrZero(modifiers & Qt::ShiftModifier);
561 modifierValue += oneOrZero(modifiers & Qt::AltModifier) << 1;
562 modifierValue += oneOrZero(modifiers & Qt::ControlModifier) << 2;
563
564 for (int i=0;i<_text.length();i++)
565 {
566 if (expandedText[i] == '*')
567 expandedText[i] = '0' + modifierValue;
568 }
569 }
570
571 return expandedText;
572}
573
574inline void KeyboardTranslator::Entry::setState( States state )
575{
576 _state = state;
577}
578inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const { return _state; }
579
580inline void KeyboardTranslator::Entry::setStateMask( States stateMask )
581{
582 _stateMask = stateMask;
583}
584inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const { return _stateMask; }
585
586}
587
588Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry)
589Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator*)
590
591#endif // KEYBOARDTRANSLATOR_H
592
593