diff options
author | mathias <mathias> | 2005-05-06 09:22:53 (GMT) |
---|---|---|
committer | mathias <mathias> | 2005-05-06 09:22:53 (GMT) |
commit | 6c057c6903151aab92309310087d5af455ecefce (patch) | |
tree | 1d4c98ad1637df09b89593b3e6a4a70245db4602 | |
parent | 7d4f711204ab0b51d45eaff332708f529c11c9f5 (diff) | |
download | fluxbox-6c057c6903151aab92309310087d5af455ecefce.zip fluxbox-6c057c6903151aab92309310087d5af455ecefce.tar.bz2 |
Fix for #1160244, #1099704, #1094107:
if the xkb-extension is enabled and the user switches between his/her
keyboardlayouts fluxbox's keybhandling doesn't work well anymore because
xkeyevent.state contains also xkb-related flags and thus we have to handle
that with caution.
KeyUtils now contain 'isolateModifierMask()' to really work only on the
modifiers. why not as part of cleanMods() ? because the XLookupString return
false results, eg TextBox's would only print chars from the first
keyboardlayout.
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | src/CommandDialog.cc | 24 | ||||
-rw-r--r-- | src/FbTk/KeyUtil.cc | 72 | ||||
-rw-r--r-- | src/FbTk/KeyUtil.hh | 9 | ||||
-rw-r--r-- | src/FbTk/TextBox.cc | 9 | ||||
-rw-r--r-- | src/Keys.cc | 76 | ||||
-rw-r--r-- | src/Keys.hh | 8 | ||||
-rw-r--r-- | src/WorkspaceCmd.cc | 3 | ||||
-rw-r--r-- | src/fluxbox.cc | 27 | ||||
-rw-r--r-- | util/fbrun/FbRun.cc | 11 |
10 files changed, 137 insertions, 111 deletions
@@ -1,5 +1,14 @@ | |||
1 | (Format: Year/Month/Day) | 1 | (Format: Year/Month/Day) |
2 | Changes for 0.9.13 | 2 | Changes for 0.9.13 |
3 | *05/05/05: | ||
4 | * Fix #1160244, #1099704, #1094107 Mutiple keyboard layout (Mathias + thanx | ||
5 | to Vadim) | ||
6 | Switching to a secondary/third keyboard layout via the xkb-extensions | ||
7 | leads to broken NextWindow/PrevWindow and broken fbrun/CommandDialog: | ||
8 | - be aware of what xkb is doing to the event.state | ||
9 | - minor cleaning | ||
10 | CommandDialog.cc WorkspaceCmd.cc Keys.cc FbTk/KeyUtil.cc/hh | ||
11 | FbTk/TextBox.cc fluxbox.cc FbRun.cc | ||
3 | *05/05/03: | 12 | *05/05/03: |
4 | * Fix drawing of no-title menus, plus updating of int menu items (Simon) | 13 | * Fix drawing of no-title menus, plus updating of int menu items (Simon) |
5 | IntResMenuItem.hh/cc MenuItem.hh Menu.cc Screen.cc ToggleMenu.hh | 14 | IntResMenuItem.hh/cc MenuItem.hh Menu.cc Screen.cc ToggleMenu.hh |
diff --git a/src/CommandDialog.cc b/src/CommandDialog.cc index 317f7ef..80c8962 100644 --- a/src/CommandDialog.cc +++ b/src/CommandDialog.cc | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "FbTk/ImageControl.hh" | 33 | #include "FbTk/ImageControl.hh" |
34 | #include "FbTk/EventManager.hh" | 34 | #include "FbTk/EventManager.hh" |
35 | #include "FbTk/StringUtil.hh" | 35 | #include "FbTk/StringUtil.hh" |
36 | #include "FbTk/KeyUtil.hh" | ||
36 | #include "FbTk/App.hh" | 37 | #include "FbTk/App.hh" |
37 | 38 | ||
38 | #include <X11/keysym.h> | 39 | #include <X11/keysym.h> |
@@ -43,11 +44,11 @@ | |||
43 | #include <stdexcept> | 44 | #include <stdexcept> |
44 | using namespace std; | 45 | using namespace std; |
45 | 46 | ||
46 | CommandDialog::CommandDialog(BScreen &screen, | 47 | CommandDialog::CommandDialog(BScreen &screen, |
47 | const std::string &title, const std::string precommand) : | 48 | const std::string &title, const std::string precommand) : |
48 | FbTk::FbWindow(screen.rootWindow().screenNumber(), 0, 0, 200, 1, ExposureMask), | 49 | FbTk::FbWindow(screen.rootWindow().screenNumber(), 0, 0, 200, 1, ExposureMask), |
49 | m_textbox(*this, screen.winFrameTheme().font(), ""), | 50 | m_textbox(*this, screen.winFrameTheme().font(), ""), |
50 | m_label(*this, screen.winFrameTheme().font(), title), | 51 | m_label(*this, screen.winFrameTheme().font(), title), |
51 | m_gc(m_textbox), | 52 | m_gc(m_textbox), |
52 | m_screen(screen), | 53 | m_screen(screen), |
53 | m_move_x(0), | 54 | m_move_x(0), |
@@ -121,12 +122,13 @@ void CommandDialog::motionNotifyEvent(XMotionEvent &event) { | |||
121 | } | 122 | } |
122 | 123 | ||
123 | void CommandDialog::keyPressEvent(XKeyEvent &event) { | 124 | void CommandDialog::keyPressEvent(XKeyEvent &event) { |
124 | if (event.state) | 125 | unsigned int state = FbTk::KeyUtil::instance().isolateModifierMask(event.state); |
126 | if (state) | ||
125 | return; | 127 | return; |
126 | 128 | ||
127 | KeySym ks; | 129 | KeySym ks; |
128 | char keychar[1]; | 130 | char keychar; |
129 | XLookupString(&event, keychar, 1, &ks, 0); | 131 | XLookupString(&event, &keychar, 1, &ks, 0); |
130 | 132 | ||
131 | if (ks == XK_Return) { | 133 | if (ks == XK_Return) { |
132 | hide(); // hide and return focus to a FluxboxWindow | 134 | hide(); // hide and return focus to a FluxboxWindow |
@@ -135,7 +137,7 @@ void CommandDialog::keyPressEvent(XKeyEvent &event) { | |||
135 | parseLine(m_precommand + m_textbox.text())); | 137 | parseLine(m_precommand + m_textbox.text())); |
136 | if (cmd.get()) | 138 | if (cmd.get()) |
137 | cmd->execute(); | 139 | cmd->execute(); |
138 | // post execute | 140 | // post execute |
139 | if (*m_postcommand != 0) | 141 | if (*m_postcommand != 0) |
140 | m_postcommand->execute(); | 142 | m_postcommand->execute(); |
141 | 143 | ||
@@ -152,7 +154,7 @@ void CommandDialog::tabComplete() { | |||
152 | try { | 154 | try { |
153 | string::size_type first = m_textbox.text().find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ" | 155 | string::size_type first = m_textbox.text().find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
154 | "abcdefghijklmnopqrstuvwxyz" | 156 | "abcdefghijklmnopqrstuvwxyz" |
155 | "0123456789", | 157 | "0123456789", |
156 | m_textbox.cursorPosition()); | 158 | m_textbox.cursorPosition()); |
157 | if (first == string::npos) | 159 | if (first == string::npos) |
158 | first = 0; | 160 | first = 0; |
@@ -173,7 +175,7 @@ void CommandDialog::tabComplete() { | |||
173 | 175 | ||
174 | if (!matches.empty()) { | 176 | if (!matches.empty()) { |
175 | // sort and apply larges match | 177 | // sort and apply larges match |
176 | std::sort(matches.begin(), matches.end(), less<string>()); | 178 | std::sort(matches.begin(), matches.end(), less<string>()); |
177 | m_textbox.setText(m_textbox.text() + matches[0].substr(prefix.size())); | 179 | m_textbox.setText(m_textbox.text() + matches[0].substr(prefix.size())); |
178 | } else | 180 | } else |
179 | XBell(FbTk::App::instance()->display(), 0); | 181 | XBell(FbTk::App::instance()->display(), 0); |
@@ -204,7 +206,7 @@ void CommandDialog::init() { | |||
204 | 206 | ||
205 | // setup label | 207 | // setup label |
206 | // we listen to motion notify too | 208 | // we listen to motion notify too |
207 | m_label.setEventMask(m_label.eventMask() | ButtonPressMask | ButtonMotionMask); | 209 | m_label.setEventMask(m_label.eventMask() | ButtonPressMask | ButtonMotionMask); |
208 | m_label.setGC(m_screen.winFrameTheme().labelTextFocusGC()); | 210 | m_label.setGC(m_screen.winFrameTheme().labelTextFocusGC()); |
209 | m_label.show(); | 211 | m_label.show(); |
210 | 212 | ||
@@ -234,7 +236,7 @@ void CommandDialog::init() { | |||
234 | void CommandDialog::updateSizes() { | 236 | void CommandDialog::updateSizes() { |
235 | m_label.moveResize(0, 0, | 237 | m_label.moveResize(0, 0, |
236 | width(), m_textbox.font().height() + 2); | 238 | width(), m_textbox.font().height() + 2); |
237 | 239 | ||
238 | m_textbox.moveResize(2, m_label.height(), | 240 | m_textbox.moveResize(2, m_label.height(), |
239 | width() - 4, m_textbox.font().height() + 2); | 241 | width() - 4, m_textbox.font().height() + 2); |
240 | } | 242 | } |
diff --git a/src/FbTk/KeyUtil.cc b/src/FbTk/KeyUtil.cc index 7760254..05e5413 100644 --- a/src/FbTk/KeyUtil.cc +++ b/src/FbTk/KeyUtil.cc | |||
@@ -26,6 +26,30 @@ | |||
26 | 26 | ||
27 | #include <string> | 27 | #include <string> |
28 | 28 | ||
29 | namespace { | ||
30 | |||
31 | struct t_modlist{ | ||
32 | char *str; | ||
33 | unsigned int mask; | ||
34 | bool operator == (const char *modstr) const { | ||
35 | return (strcasecmp(str, modstr) == 0 && mask !=0); | ||
36 | } | ||
37 | }; | ||
38 | |||
39 | const struct t_modlist modlist[] = { | ||
40 | {"SHIFT", ShiftMask}, | ||
41 | {"LOCK", LockMask}, | ||
42 | {"CONTROL", ControlMask}, | ||
43 | {"MOD1", Mod1Mask}, | ||
44 | {"MOD2", Mod2Mask}, | ||
45 | {"MOD3", Mod3Mask}, | ||
46 | {"MOD4", Mod4Mask}, | ||
47 | {"MOD5", Mod5Mask}, | ||
48 | {0, 0} | ||
49 | }; | ||
50 | |||
51 | }; | ||
52 | |||
29 | namespace FbTk { | 53 | namespace FbTk { |
30 | 54 | ||
31 | std::auto_ptr<KeyUtil> KeyUtil::s_keyutil; | 55 | std::auto_ptr<KeyUtil> KeyUtil::s_keyutil; |
@@ -57,19 +81,7 @@ void KeyUtil::loadModmap() { | |||
57 | XFreeModifiermap(m_modmap); | 81 | XFreeModifiermap(m_modmap); |
58 | 82 | ||
59 | m_modmap = XGetModifierMapping(App::instance()->display()); | 83 | m_modmap = XGetModifierMapping(App::instance()->display()); |
60 | // mask to use for modifier | 84 | |
61 | static const int mods[] = { | ||
62 | ShiftMask, | ||
63 | LockMask, | ||
64 | ControlMask, | ||
65 | Mod1Mask, | ||
66 | Mod2Mask, | ||
67 | Mod3Mask, | ||
68 | Mod4Mask, | ||
69 | Mod5Mask, | ||
70 | 0 | ||
71 | }; | ||
72 | |||
73 | // find modifiers and set them | 85 | // find modifiers and set them |
74 | for (int i=0, realkey=0; i<8; ++i) { | 86 | for (int i=0, realkey=0; i<8; ++i) { |
75 | for (int key=0; key<m_modmap->max_keypermod; ++key, ++realkey) { | 87 | for (int key=0; key<m_modmap->max_keypermod; ++key, ++realkey) { |
@@ -77,17 +89,18 @@ void KeyUtil::loadModmap() { | |||
77 | if (m_modmap->modifiermap[realkey] == 0) | 89 | if (m_modmap->modifiermap[realkey] == 0) |
78 | continue; | 90 | continue; |
79 | 91 | ||
80 | KeySym ks = XKeycodeToKeysym(App::instance()->display(), m_modmap->modifiermap[realkey], 0); | 92 | KeySym ks = XKeycodeToKeysym(App::instance()->display(), |
93 | m_modmap->modifiermap[realkey], 0); | ||
81 | 94 | ||
82 | switch (ks) { | 95 | switch (ks) { |
83 | case XK_Caps_Lock: | 96 | case XK_Caps_Lock: |
84 | m_capslock = mods[i]; | 97 | m_capslock = modlist[i].mask; |
85 | break; | 98 | break; |
86 | case XK_Scroll_Lock: | 99 | case XK_Scroll_Lock: |
87 | m_scrolllock = mods[i]; | 100 | m_scrolllock = modlist[i].mask; |
88 | break; | 101 | break; |
89 | case XK_Num_Lock: | 102 | case XK_Num_Lock: |
90 | m_numlock = mods[i]; | 103 | m_numlock = modlist[i].mask; |
91 | break; | 104 | break; |
92 | } | 105 | } |
93 | } | 106 | } |
@@ -164,31 +177,13 @@ unsigned int KeyUtil::getKey(const char *keystr) { | |||
164 | } | 177 | } |
165 | 178 | ||
166 | 179 | ||
167 | struct t_modlist{ | ||
168 | char *str; | ||
169 | unsigned int mask; | ||
170 | bool operator == (const char *modstr) const { | ||
171 | return (strcasecmp(str, modstr) == 0 && mask !=0); | ||
172 | } | ||
173 | }; | ||
174 | |||
175 | /** | 180 | /** |
176 | @return the modifier for the modstr else zero on failure. | 181 | @return the modifier for the modstr else zero on failure. |
177 | */ | 182 | */ |
178 | unsigned int KeyUtil::getModifier(const char *modstr) { | 183 | unsigned int KeyUtil::getModifier(const char *modstr) { |
179 | if (!modstr) | 184 | if (!modstr) |
180 | return 0; | 185 | return 0; |
181 | const static struct t_modlist modlist[] = { | 186 | |
182 | {"SHIFT", ShiftMask}, | ||
183 | {"CONTROL", ControlMask}, | ||
184 | {"MOD1", Mod1Mask}, | ||
185 | {"MOD2", Mod2Mask}, | ||
186 | {"MOD3", Mod3Mask}, | ||
187 | {"MOD4", Mod4Mask}, | ||
188 | {"MOD5", Mod5Mask}, | ||
189 | {0, 0} | ||
190 | }; | ||
191 | |||
192 | // find mod mask string | 187 | // find mod mask string |
193 | for (unsigned int i=0; modlist[i].str !=0; i++) { | 188 | for (unsigned int i=0; modlist[i].str !=0; i++) { |
194 | if (modlist[i] == modstr) | 189 | if (modlist[i] == modstr) |
@@ -210,7 +205,8 @@ void KeyUtil::ungrabKeys() { | |||
210 | unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { | 205 | unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { |
211 | XModifierKeymap *modmap = instance().m_modmap; | 206 | XModifierKeymap *modmap = instance().m_modmap; |
212 | 207 | ||
213 | if (!modmap) return 0; | 208 | if (!modmap) |
209 | return 0; | ||
214 | 210 | ||
215 | // search through modmap for this keycode | 211 | // search through modmap for this keycode |
216 | for (int mod=0; mod < 8; mod++) { | 212 | for (int mod=0; mod < 8; mod++) { |
@@ -218,7 +214,7 @@ unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { | |||
218 | // modifiermap is an array with 8 sets of keycodes | 214 | // modifiermap is an array with 8 sets of keycodes |
219 | // each max_keypermod long, but in a linear array. | 215 | // each max_keypermod long, but in a linear array. |
220 | if (modmap->modifiermap[modmap->max_keypermod*mod + key] == keycode) { | 216 | if (modmap->modifiermap[modmap->max_keypermod*mod + key] == keycode) { |
221 | return (1<<mod); | 217 | return modlist[mod].mask; |
222 | } | 218 | } |
223 | } | 219 | } |
224 | } | 220 | } |
diff --git a/src/FbTk/KeyUtil.hh b/src/FbTk/KeyUtil.hh index 4cafcdd..e32384f 100644 --- a/src/FbTk/KeyUtil.hh +++ b/src/FbTk/KeyUtil.hh | |||
@@ -70,6 +70,14 @@ public: | |||
70 | return mods & ~(capslock() | numlock() ); | 70 | return mods & ~(capslock() | numlock() ); |
71 | } | 71 | } |
72 | 72 | ||
73 | /** | ||
74 | strip away everything which is actually not a modifier | ||
75 | eg, xkb-keyboardgroups are encoded as bit 13 and 14 | ||
76 | */ | ||
77 | unsigned int isolateModifierMask(unsigned int mods) { | ||
78 | return mods & (ShiftMask|LockMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask); | ||
79 | } | ||
80 | |||
73 | /** | 81 | /** |
74 | Convert the specified key into appropriate modifier mask | 82 | Convert the specified key into appropriate modifier mask |
75 | @return corresponding modifier mask | 83 | @return corresponding modifier mask |
@@ -78,6 +86,7 @@ public: | |||
78 | int numlock() const { return Mod2Mask; } //m_numlock; } | 86 | int numlock() const { return Mod2Mask; } //m_numlock; } |
79 | int capslock() const { return LockMask; } //m_capslock; } | 87 | int capslock() const { return LockMask; } //m_capslock; } |
80 | int scrolllock() const { return Mod5Mask; } //m_scrolllock; } | 88 | int scrolllock() const { return Mod5Mask; } //m_scrolllock; } |
89 | |||
81 | private: | 90 | private: |
82 | void loadModmap(); | 91 | void loadModmap(); |
83 | 92 | ||
diff --git a/src/FbTk/TextBox.cc b/src/FbTk/TextBox.cc index 0db8856..2ebd6c0 100644 --- a/src/FbTk/TextBox.cc +++ b/src/FbTk/TextBox.cc | |||
@@ -209,7 +209,7 @@ void TextBox::buttonPressEvent(XButtonEvent &event) { | |||
209 | } | 209 | } |
210 | 210 | ||
211 | void TextBox::keyPressEvent(XKeyEvent &event) { | 211 | void TextBox::keyPressEvent(XKeyEvent &event) { |
212 | // strip numlock and scrolllock mask | 212 | |
213 | event.state = KeyUtil::instance().cleanMods(event.state); | 213 | event.state = KeyUtil::instance().cleanMods(event.state); |
214 | 214 | ||
215 | KeySym ks; | 215 | KeySym ks; |
@@ -218,8 +218,8 @@ void TextBox::keyPressEvent(XKeyEvent &event) { | |||
218 | // a modifier key by itself doesn't do anything | 218 | // a modifier key by itself doesn't do anything |
219 | if (IsModifierKey(ks)) return; | 219 | if (IsModifierKey(ks)) return; |
220 | 220 | ||
221 | if (event.state) { // handle keybindings with state | 221 | if (FbTk::KeyUtil::instance().isolateModifierMask(event.state)) { // handle keybindings with state |
222 | if (event.state == ControlMask) { | 222 | if ((event.state & ControlMask) == ControlMask) { |
223 | 223 | ||
224 | switch (ks) { | 224 | switch (ks) { |
225 | case XK_b: | 225 | case XK_b: |
@@ -248,7 +248,8 @@ void TextBox::keyPressEvent(XKeyEvent &event) { | |||
248 | m_end_pos = 0; | 248 | m_end_pos = 0; |
249 | break; | 249 | break; |
250 | } | 250 | } |
251 | } else if (event.state == ShiftMask || event.state == 0x80) { // shif and altgr | 251 | } else if ((event.state & ShiftMask)== ShiftMask || |
252 | (event.state & 0x80) == 0x80) { // shif and altgr | ||
252 | if (isprint(keychar[0])) { | 253 | if (isprint(keychar[0])) { |
253 | std::string val; | 254 | std::string val; |
254 | val += keychar[0]; | 255 | val += keychar[0]; |
diff --git a/src/Keys.cc b/src/Keys.cc index 96f57cb..0d3eecd 100644 --- a/src/Keys.cc +++ b/src/Keys.cc | |||
@@ -27,7 +27,6 @@ | |||
27 | #include "FbTk/StringUtil.hh" | 27 | #include "FbTk/StringUtil.hh" |
28 | #include "FbTk/App.hh" | 28 | #include "FbTk/App.hh" |
29 | #include "FbTk/Command.hh" | 29 | #include "FbTk/Command.hh" |
30 | #include "FbTk/KeyUtil.hh" | ||
31 | 30 | ||
32 | #include "CommandParser.hh" | 31 | #include "CommandParser.hh" |
33 | #include "FbTk/I18n.hh" | 32 | #include "FbTk/I18n.hh" |
@@ -81,6 +80,8 @@ | |||
81 | #include <X11/Xlib.h> | 80 | #include <X11/Xlib.h> |
82 | #include <X11/Xproto.h> | 81 | #include <X11/Xproto.h> |
83 | #include <X11/keysym.h> | 82 | #include <X11/keysym.h> |
83 | #include <X11/Xutil.h> | ||
84 | #include <X11/XKBlib.h> | ||
84 | 85 | ||
85 | #include <iostream> | 86 | #include <iostream> |
86 | #include <fstream> | 87 | #include <fstream> |
@@ -102,7 +103,7 @@ Keys::Keys(const char *filename): | |||
102 | load(filename); | 103 | load(filename); |
103 | } | 104 | } |
104 | 105 | ||
105 | Keys::~Keys() { | 106 | Keys::~Keys() { |
106 | 107 | ||
107 | FbTk::KeyUtil::ungrabKeys(); | 108 | FbTk::KeyUtil::ungrabKeys(); |
108 | deleteTree(); | 109 | deleteTree(); |
@@ -112,12 +113,12 @@ Keys::~Keys() { | |||
112 | void Keys::deleteTree() { | 113 | void Keys::deleteTree() { |
113 | while (!m_keylist.empty()) { | 114 | while (!m_keylist.empty()) { |
114 | if (m_keylist.back()) | 115 | if (m_keylist.back()) |
115 | delete m_keylist.back(); | 116 | delete m_keylist.back(); |
116 | m_keylist.pop_back(); | 117 | m_keylist.pop_back(); |
117 | } | 118 | } |
118 | } | 119 | } |
119 | 120 | ||
120 | /** | 121 | /** |
121 | Load and grab keys | 122 | Load and grab keys |
122 | TODO: error checking | 123 | TODO: error checking |
123 | @return true on success else false | 124 | @return true on success else false |
@@ -125,7 +126,7 @@ void Keys::deleteTree() { | |||
125 | bool Keys::load(const char *filename) { | 126 | bool Keys::load(const char *filename) { |
126 | if (!filename) | 127 | if (!filename) |
127 | return false; | 128 | return false; |
128 | 129 | ||
129 | //ungrab all keys | 130 | //ungrab all keys |
130 | FbTk::KeyUtil::ungrabKeys(); | 131 | FbTk::KeyUtil::ungrabKeys(); |
131 | 132 | ||
@@ -133,7 +134,7 @@ bool Keys::load(const char *filename) { | |||
133 | deleteTree(); | 134 | deleteTree(); |
134 | 135 | ||
135 | FbTk::App::instance()->sync(false); | 136 | FbTk::App::instance()->sync(false); |
136 | 137 | ||
137 | //open the file | 138 | //open the file |
138 | ifstream infile(filename); | 139 | ifstream infile(filename); |
139 | if (!infile) | 140 | if (!infile) |
@@ -158,8 +159,8 @@ bool Keys::load(const char *filename) { | |||
158 | 159 | ||
159 | bool Keys::save(const char *filename) const { | 160 | bool Keys::save(const char *filename) const { |
160 | //!! | 161 | //!! |
161 | //!! TODO: fix keybinding saving | 162 | //!! TODO: fix keybinding saving |
162 | //!! (we probably need to save key actions | 163 | //!! (we probably need to save key actions |
163 | //!! as strings instead of creating new Commands) | 164 | //!! as strings instead of creating new Commands) |
164 | 165 | ||
165 | // open file for writing | 166 | // open file for writing |
@@ -178,16 +179,16 @@ bool Keys::addBinding(const std::string &linebuffer) { | |||
178 | // must have at least 1 argument | 179 | // must have at least 1 argument |
179 | if (val.size() <= 0) | 180 | if (val.size() <= 0) |
180 | return true; // empty lines are valid. | 181 | return true; // empty lines are valid. |
181 | 182 | ||
182 | if (val[0][0] == '#' || val[0][0] == '!' ) //the line is commented | 183 | if (val[0][0] == '#' || val[0][0] == '!' ) //the line is commented |
183 | return true; // still a valid line. | 184 | return true; // still a valid line. |
184 | 185 | ||
185 | unsigned int key = 0, mod = 0; | 186 | unsigned int key = 0, mod = 0; |
186 | char keyarg = 0; | 187 | char keyarg = 0; |
187 | t_key *current_key=0, *last_key=0; | 188 | t_key *current_key=0, *last_key=0; |
188 | 189 | ||
189 | _FB_USES_NLS; | 190 | _FB_USES_NLS; |
190 | // for each argument | 191 | // for each argument |
191 | for (unsigned int argc=0; argc<val.size(); argc++) { | 192 | for (unsigned int argc=0; argc<val.size(); argc++) { |
192 | 193 | ||
193 | if (val[argc][0] != ':') { // parse key(s) | 194 | if (val[argc][0] != ':') { // parse key(s) |
@@ -199,7 +200,7 @@ bool Keys::addBinding(const std::string &linebuffer) { | |||
199 | int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); | 200 | int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); |
200 | if(tmpmod) | 201 | if(tmpmod) |
201 | mod |= tmpmod; //If it's a modifier | 202 | mod |= tmpmod; //If it's a modifier |
202 | else { | 203 | else { |
203 | key = FbTk::KeyUtil::getKey(val[argc].c_str()); // else get the key | 204 | key = FbTk::KeyUtil::getKey(val[argc].c_str()); // else get the key |
204 | if (key == 0) { | 205 | if (key == 0) { |
205 | cerr<<_FBTEXT(Keys, InvalidKeyMod, "Keys: Invalid key/modifier on line", "A bad key/modifier string was found on line (number following)")<<" "<< | 206 | cerr<<_FBTEXT(Keys, InvalidKeyMod, "Keys: Invalid key/modifier on line", "A bad key/modifier string was found on line (number following)")<<" "<< |
@@ -209,13 +210,13 @@ bool Keys::addBinding(const std::string &linebuffer) { | |||
209 | if (!current_key) { | 210 | if (!current_key) { |
210 | current_key = new t_key(key, mod); | 211 | current_key = new t_key(key, mod); |
211 | last_key = current_key; | 212 | last_key = current_key; |
212 | } else { | 213 | } else { |
213 | t_key *temp_key = new t_key(key, mod); | 214 | t_key *temp_key = new t_key(key, mod); |
214 | last_key->keylist.push_back(temp_key); | 215 | last_key->keylist.push_back(temp_key); |
215 | last_key = temp_key; | 216 | last_key = temp_key; |
216 | } | 217 | } |
217 | } | 218 | } |
218 | } | 219 | } |
219 | 220 | ||
220 | } else { // parse command line | 221 | } else { // parse command line |
221 | if (last_key == 0) { | 222 | if (last_key == 0) { |
@@ -224,7 +225,7 @@ bool Keys::addBinding(const std::string &linebuffer) { | |||
224 | return false; | 225 | return false; |
225 | } | 226 | } |
226 | bool ret_val = true; | 227 | bool ret_val = true; |
227 | const char *str = | 228 | const char *str = |
228 | FbTk::StringUtil::strcasestr(linebuffer.c_str(), | 229 | FbTk::StringUtil::strcasestr(linebuffer.c_str(), |
229 | val[argc].c_str() + 1); // +1 to skip ':' | 230 | val[argc].c_str() + 1); // +1 to skip ':' |
230 | if (str == 0) { | 231 | if (str == 0) { |
@@ -263,41 +264,42 @@ bool Keys::addBinding(const std::string &linebuffer) { | |||
263 | @return the KeyAction of the XKeyEvent | 264 | @return the KeyAction of the XKeyEvent |
264 | */ | 265 | */ |
265 | void Keys::doAction(XKeyEvent &ke) { | 266 | void Keys::doAction(XKeyEvent &ke) { |
266 | static t_key *next_key = 0; | 267 | |
267 | // Remove numlock, capslock and scrolllock | ||
268 | ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); | 268 | ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); |
269 | 269 | ||
270 | static struct t_key* next_key = 0; | ||
271 | |||
270 | if (!next_key) { | 272 | if (!next_key) { |
271 | 273 | ||
272 | for (unsigned int i=0; i<m_keylist.size(); i++) { | 274 | for (unsigned int i=0; i<m_keylist.size(); i++) { |
273 | if (*m_keylist[i] == ke) { | 275 | if (*m_keylist[i] == ke) { |
274 | if (m_keylist[i]->keylist.size()) { | 276 | if (m_keylist[i]->keylist.size()) { |
275 | next_key = m_keylist[i]; | 277 | next_key = m_keylist[i]; |
276 | break; //end for-loop | 278 | break; //end for-loop |
277 | } else { | 279 | } else { |
278 | if (*m_keylist[i]->m_command != 0) | 280 | if (*m_keylist[i]->m_command != 0) |
279 | m_keylist[i]->m_command->execute(); | 281 | m_keylist[i]->m_command->execute(); |
280 | } | 282 | } |
281 | } | 283 | } |
282 | } | 284 | } |
283 | 285 | ||
284 | } else { //check the nextkey | 286 | } else { //check the nextkey |
285 | t_key *temp_key = next_key->find(ke); | 287 | t_key *temp_key = next_key->find(ke); |
286 | if (temp_key) { | 288 | if (temp_key) { |
287 | if (temp_key->keylist.size()) { | 289 | if (temp_key->keylist.size()) { |
288 | next_key = temp_key; | 290 | next_key = temp_key; |
289 | } else { | 291 | } else { |
290 | next_key = 0; | 292 | next_key = 0; |
291 | if (*temp_key->m_command != 0) | 293 | if (*temp_key->m_command != 0) |
292 | temp_key->m_command->execute(); | 294 | temp_key->m_command->execute(); |
293 | } | 295 | } |
294 | } else { | 296 | } else { |
295 | temp_key = next_key; | 297 | temp_key = next_key; |
296 | next_key = 0; | 298 | next_key = 0; |
297 | if (*temp_key->m_command != 0) | 299 | if (*temp_key->m_command != 0) |
298 | temp_key->m_command->execute(); | 300 | temp_key->m_command->execute(); |
299 | 301 | ||
300 | } | 302 | } |
301 | } | 303 | } |
302 | } | 304 | } |
303 | 305 | ||
@@ -318,7 +320,7 @@ bool Keys::mergeTree(t_key *newtree, t_key *basetree) { | |||
318 | if (basetree==0) { | 320 | if (basetree==0) { |
319 | unsigned int baselist_i=0; | 321 | unsigned int baselist_i=0; |
320 | for (; baselist_i<m_keylist.size(); baselist_i++) { | 322 | for (; baselist_i<m_keylist.size(); baselist_i++) { |
321 | if (m_keylist[baselist_i]->mod == newtree->mod && | 323 | if (m_keylist[baselist_i]->mod == newtree->mod && |
322 | m_keylist[baselist_i]->key == newtree->key) { | 324 | m_keylist[baselist_i]->key == newtree->key) { |
323 | if (newtree->keylist.size() && *m_keylist[baselist_i]->m_command == 0) { | 325 | if (newtree->keylist.size() && *m_keylist[baselist_i]->m_command == 0) { |
324 | //assumes the newtree only have one branch | 326 | //assumes the newtree only have one branch |
@@ -330,12 +332,12 @@ bool Keys::mergeTree(t_key *newtree, t_key *basetree) { | |||
330 | 332 | ||
331 | if (baselist_i == m_keylist.size()) { | 333 | if (baselist_i == m_keylist.size()) { |
332 | FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); | 334 | FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); |
333 | m_keylist.push_back(new t_key(newtree)); | 335 | m_keylist.push_back(new t_key(newtree)); |
334 | if (newtree->keylist.size()) | 336 | if (newtree->keylist.size()) |
335 | return mergeTree(newtree->keylist[0], m_keylist.back()); | 337 | return mergeTree(newtree->keylist[0], m_keylist.back()); |
336 | return true; | 338 | return true; |
337 | } | 339 | } |
338 | 340 | ||
339 | } else { | 341 | } else { |
340 | unsigned int baselist_i = 0; | 342 | unsigned int baselist_i = 0; |
341 | for (; baselist_i<basetree->keylist.size(); baselist_i++) { | 343 | for (; baselist_i<basetree->keylist.size(); baselist_i++) { |
@@ -346,24 +348,24 @@ bool Keys::mergeTree(t_key *newtree, t_key *basetree) { | |||
346 | return mergeTree(newtree->keylist[0], basetree->keylist[baselist_i]); | 348 | return mergeTree(newtree->keylist[0], basetree->keylist[baselist_i]); |
347 | } else | 349 | } else |
348 | return false; | 350 | return false; |
349 | } | 351 | } |
350 | } | 352 | } |
351 | //if it wasn't in the list grab the key and add it to the list | 353 | //if it wasn't in the list grab the key and add it to the list |
352 | if (baselist_i==basetree->keylist.size()) { | 354 | if (baselist_i==basetree->keylist.size()) { |
353 | FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); | 355 | FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); |
354 | basetree->keylist.push_back(new t_key(newtree)); | 356 | basetree->keylist.push_back(new t_key(newtree)); |
355 | if (newtree->keylist.size()) | 357 | if (newtree->keylist.size()) |
356 | return mergeTree(newtree->keylist[0], basetree->keylist.back()); | 358 | return mergeTree(newtree->keylist[0], basetree->keylist.back()); |
357 | return true; | 359 | return true; |
358 | } | 360 | } |
359 | } | 361 | } |
360 | 362 | ||
361 | return false; | 363 | return false; |
362 | } | 364 | } |
363 | 365 | ||
364 | Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) { | 366 | Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) { |
365 | key = key_; | 367 | key = key_; |
366 | mod = mod_; | 368 | mod = mod_; |
367 | m_command = command; | 369 | m_command = command; |
368 | } | 370 | } |
369 | 371 | ||
@@ -373,8 +375,8 @@ Keys::t_key::t_key(t_key *k) { | |||
373 | m_command = k->m_command; | 375 | m_command = k->m_command; |
374 | } | 376 | } |
375 | 377 | ||
376 | Keys::t_key::~t_key() { | 378 | Keys::t_key::~t_key() { |
377 | while (!keylist.empty()) { | 379 | while (!keylist.empty()) { |
378 | t_key *k = keylist.back(); | 380 | t_key *k = keylist.back(); |
379 | if (k != 0) { // make sure we don't have a bad key pointer | 381 | if (k != 0) { // make sure we don't have a bad key pointer |
380 | delete k; | 382 | delete k; |
diff --git a/src/Keys.hh b/src/Keys.hh index c9c53b5..f83f6e8 100644 --- a/src/Keys.hh +++ b/src/Keys.hh | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "FbTk/NotCopyable.hh" | 31 | #include "FbTk/NotCopyable.hh" |
32 | #include "FbTk/RefCount.hh" | 32 | #include "FbTk/RefCount.hh" |
33 | #include "FbTk/Command.hh" | 33 | #include "FbTk/Command.hh" |
34 | #include "FbTk/KeyUtil.hh" | ||
34 | 35 | ||
35 | class Keys:private FbTk::NotCopyable { | 36 | class Keys:private FbTk::NotCopyable { |
36 | public: | 37 | public: |
@@ -86,21 +87,22 @@ private: | |||
86 | 87 | ||
87 | inline t_key *find(unsigned int key_, unsigned int mod_) { | 88 | inline t_key *find(unsigned int key_, unsigned int mod_) { |
88 | for (unsigned int i=0; i<keylist.size(); i++) { | 89 | for (unsigned int i=0; i<keylist.size(); i++) { |
89 | if (keylist[i]->key == key_ && keylist[i]->mod == mod_) | 90 | if (keylist[i]->key == key_ && keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(mod_)) |
90 | return keylist[i]; | 91 | return keylist[i]; |
91 | } | 92 | } |
92 | return 0; | 93 | return 0; |
93 | } | 94 | } |
94 | inline t_key *find(XKeyEvent &ke) { | 95 | inline t_key *find(XKeyEvent &ke) { |
95 | for (unsigned int i=0; i<keylist.size(); i++) { | 96 | for (unsigned int i=0; i<keylist.size(); i++) { |
96 | if (keylist[i]->key == ke.keycode && keylist[i]->mod == ke.state) | 97 | if (keylist[i]->key == ke.keycode && |
98 | keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) | ||
97 | return keylist[i]; | 99 | return keylist[i]; |
98 | } | 100 | } |
99 | return 0; | 101 | return 0; |
100 | } | 102 | } |
101 | 103 | ||
102 | inline bool operator == (XKeyEvent &ke) const { | 104 | inline bool operator == (XKeyEvent &ke) const { |
103 | return (mod == ke.state && key == ke.keycode); | 105 | return (mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state) && key == ke.keycode); |
104 | } | 106 | } |
105 | 107 | ||
106 | FbTk::RefCount<FbTk::Command> m_command; | 108 | FbTk::RefCount<FbTk::Command> m_command; |
diff --git a/src/WorkspaceCmd.cc b/src/WorkspaceCmd.cc index 3e6d128..08473df 100644 --- a/src/WorkspaceCmd.cc +++ b/src/WorkspaceCmd.cc | |||
@@ -42,13 +42,13 @@ | |||
42 | #include <iostream> | 42 | #include <iostream> |
43 | 43 | ||
44 | void NextWindowCmd::execute() { | 44 | void NextWindowCmd::execute() { |
45 | |||
46 | BScreen *screen = Fluxbox::instance()->keyScreen(); | 45 | BScreen *screen = Fluxbox::instance()->keyScreen(); |
47 | if (screen != 0) { | 46 | if (screen != 0) { |
48 | Fluxbox *fb = Fluxbox::instance(); | 47 | Fluxbox *fb = Fluxbox::instance(); |
49 | // special case for commands from key events | 48 | // special case for commands from key events |
50 | if (fb->lastEvent().type == KeyPress) { | 49 | if (fb->lastEvent().type == KeyPress) { |
51 | unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); | 50 | unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); |
51 | mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); | ||
52 | if (mods == 0) // can't stacked cycle unless there is a mod to grab | 52 | if (mods == 0) // can't stacked cycle unless there is a mod to grab |
53 | screen->nextFocus(m_option | BScreen::CYCLELINEAR); | 53 | screen->nextFocus(m_option | BScreen::CYCLELINEAR); |
54 | else { | 54 | else { |
@@ -71,6 +71,7 @@ void PrevWindowCmd::execute() { | |||
71 | // special case for commands from key events | 71 | // special case for commands from key events |
72 | if (fb->lastEvent().type == KeyPress) { | 72 | if (fb->lastEvent().type == KeyPress) { |
73 | unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); | 73 | unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); |
74 | mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); | ||
74 | if (mods == 0) // can't stacked cycle unless there is a mod to grab | 75 | if (mods == 0) // can't stacked cycle unless there is a mod to grab |
75 | screen->prevFocus(m_option | BScreen::CYCLELINEAR); | 76 | screen->prevFocus(m_option | BScreen::CYCLELINEAR); |
76 | else { | 77 | else { |
diff --git a/src/fluxbox.cc b/src/fluxbox.cc index cd289ed..271d93f 100644 --- a/src/fluxbox.cc +++ b/src/fluxbox.cc | |||
@@ -777,11 +777,13 @@ void Fluxbox::handleEvent(XEvent * const e) { | |||
777 | #ifdef DEBUG | 777 | #ifdef DEBUG |
778 | cerr<<__FILE__<<"("<<__FUNCTION__<<"): MappingNotify"<<endl; | 778 | cerr<<__FILE__<<"("<<__FUNCTION__<<"): MappingNotify"<<endl; |
779 | #endif // DEBUG | 779 | #endif // DEBUG |
780 | 780 | if (e->xmapping.request == MappingKeyboard | |
781 | FbTk::KeyUtil::instance().init(); // reinitialise the key utils | 781 | || e->xmapping.request == MappingModifier) { |
782 | // reconfigure keys (if the mapping changes, they don't otherwise update | 782 | XRefreshKeyboardMapping(&e->xmapping); |
783 | m_key->reconfigure(StringUtil::expandFilename(*m_rc_keyfile).c_str()); | 783 | FbTk::KeyUtil::instance().init(); // reinitialise the key utils |
784 | 784 | // reconfigure keys (if the mapping changes, they don't otherwise update | |
785 | m_key->reconfigure(StringUtil::expandFilename(*m_rc_keyfile).c_str()); | ||
786 | } | ||
785 | break; | 787 | break; |
786 | case CreateNotify: | 788 | case CreateNotify: |
787 | break; | 789 | break; |
@@ -1076,7 +1078,6 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { | |||
1076 | if (keyScreen() == 0 || mouseScreen() == 0) | 1078 | if (keyScreen() == 0 || mouseScreen() == 0) |
1077 | return; | 1079 | return; |
1078 | 1080 | ||
1079 | |||
1080 | switch (ke.type) { | 1081 | switch (ke.type) { |
1081 | case KeyPress: | 1082 | case KeyPress: |
1082 | m_key->doAction(ke); | 1083 | m_key->doAction(ke); |
@@ -1089,9 +1090,10 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { | |||
1089 | if (m_watching_screen && m_watch_keyrelease) { | 1090 | if (m_watching_screen && m_watch_keyrelease) { |
1090 | // mask the mod of the released key out | 1091 | // mask the mod of the released key out |
1091 | // won't mask anything if it isn't a mod | 1092 | // won't mask anything if it isn't a mod |
1092 | ke.state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode); | 1093 | unsigned int state = FbTk::KeyUtil::instance().isolateModifierMask(ke.state); |
1093 | 1094 | state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode); | |
1094 | if ((m_watch_keyrelease & ke.state) == 0) { | 1095 | |
1096 | if ((m_watch_keyrelease & state) == 0) { | ||
1095 | 1097 | ||
1096 | m_watching_screen->notifyReleasedKeys(ke); | 1098 | m_watching_screen->notifyReleasedKeys(ke); |
1097 | XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime); | 1099 | XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime); |
@@ -1107,8 +1109,6 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { | |||
1107 | default: | 1109 | default: |
1108 | break; | 1110 | break; |
1109 | } | 1111 | } |
1110 | |||
1111 | |||
1112 | } | 1112 | } |
1113 | 1113 | ||
1114 | /// handle system signals | 1114 | /// handle system signals |
@@ -1951,12 +1951,15 @@ void Fluxbox::unfocusWindow(WinClient &client, bool full_revert, bool unfocus_fr | |||
1951 | 1951 | ||
1952 | 1952 | ||
1953 | void Fluxbox::watchKeyRelease(BScreen &screen, unsigned int mods) { | 1953 | void Fluxbox::watchKeyRelease(BScreen &screen, unsigned int mods) { |
1954 | |||
1954 | if (mods == 0) { | 1955 | if (mods == 0) { |
1955 | cerr<<"WARNING: attempt to grab without modifiers!"<<endl; | 1956 | cerr<<"WARNING: attempt to grab without modifiers!"<<endl; |
1956 | return; | 1957 | return; |
1957 | } | 1958 | } |
1958 | m_watching_screen = &screen; | 1959 | m_watching_screen = &screen; |
1959 | m_watch_keyrelease = mods; | 1960 | |
1961 | // just make sure we are saving the mods with any other flags (xkb) | ||
1962 | m_watch_keyrelease = FbTk::KeyUtil::instance().isolateModifierMask(mods); | ||
1960 | XGrabKeyboard(FbTk::App::instance()->display(), | 1963 | XGrabKeyboard(FbTk::App::instance()->display(), |
1961 | screen.rootWindow().window(), True, | 1964 | screen.rootWindow().window(), True, |
1962 | GrabModeAsync, GrabModeAsync, CurrentTime); | 1965 | GrabModeAsync, GrabModeAsync, CurrentTime); |
diff --git a/util/fbrun/FbRun.cc b/util/fbrun/FbRun.cc index b14321b..d72d1bf 100644 --- a/util/fbrun/FbRun.cc +++ b/util/fbrun/FbRun.cc | |||
@@ -223,7 +223,7 @@ void FbRun::redrawLabel() { | |||
223 | } | 223 | } |
224 | 224 | ||
225 | void FbRun::keyPressEvent(XKeyEvent &ke) { | 225 | void FbRun::keyPressEvent(XKeyEvent &ke) { |
226 | // strip numlock, capslock and scrolllock mask | 226 | |
227 | ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); | 227 | ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); |
228 | 228 | ||
229 | int cp= cursorPosition(); | 229 | int cp= cursorPosition(); |
@@ -232,10 +232,11 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { | |||
232 | char keychar[1]; | 232 | char keychar[1]; |
233 | XLookupString(&ke, keychar, 1, &ks, 0); | 233 | XLookupString(&ke, keychar, 1, &ks, 0); |
234 | // a modifier key by itself doesn't do anything | 234 | // a modifier key by itself doesn't do anything |
235 | if (IsModifierKey(ks)) return; | 235 | if (IsModifierKey(ks)) |
236 | return; | ||
236 | 237 | ||
237 | if (ke.state) { // a modifier key is down | 238 | if (FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) { // a modifier key is down |
238 | if (ke.state == ControlMask) { | 239 | if ((ke.state & ControlMask) == ControlMask) { |
239 | switch (ks) { | 240 | switch (ks) { |
240 | case XK_p: | 241 | case XK_p: |
241 | prevHistoryItem(); | 242 | prevHistoryItem(); |
@@ -248,7 +249,7 @@ void FbRun::keyPressEvent(XKeyEvent &ke) { | |||
248 | setCursorPosition(cp); | 249 | setCursorPosition(cp); |
249 | break; | 250 | break; |
250 | } | 251 | } |
251 | } else if (ke.state == (Mod1Mask | ShiftMask)) { | 252 | } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) { |
252 | switch (ks) { | 253 | switch (ks) { |
253 | case XK_less: | 254 | case XK_less: |
254 | firstHistoryItem(); | 255 | firstHistoryItem(); |