BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / preferences / keymap / Keymap.cpp
blob7dda76d6dada68ed24f7eb3cb764fcf8efb0e4de
1 /*
2 * Copyright 2004-2011 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Sandor Vroemisse
7 * Jérôme Duval
8 * Axel Dörfler, axeld@pinc-software.de.
9 */
12 #include "Keymap.h"
14 #include <new>
15 #include <stdio.h>
16 #include <string.h>
18 #include <ByteOrder.h>
19 #include <File.h>
20 #include <FindDirectory.h>
21 #include <Path.h>
23 #include <input_globals.h>
26 static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
27 | B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
30 static void
31 print_key(char* chars, int32 offset, bool last = false)
33 int size = chars[offset++];
35 switch (size) {
36 case 0:
37 // Not mapped
38 fputs("N/A", stdout);
39 break;
41 case 1:
42 // single-byte UTF-8/ASCII character
43 fputc(chars[offset], stdout);
44 break;
46 default:
48 // 2-, 3-, or 4-byte UTF-8 character
49 char* str = new char[size + 1];
50 strncpy(str, &chars[offset], size);
51 str[size] = 0;
52 fputs(str, stdout);
53 delete[] str;
54 break;
58 if (!last)
59 fputs("\t", stdout);
63 // #pragma mark -
66 Keymap::Keymap()
68 fModificationMessage(NULL)
73 Keymap::~Keymap()
75 delete fModificationMessage;
79 void
80 Keymap::SetTarget(BMessenger target, BMessage* modificationMessage)
82 delete fModificationMessage;
84 fTarget = target;
85 fModificationMessage = modificationMessage;
89 void
90 Keymap::SetName(const char* name)
92 strlcpy(fName, name, sizeof(fName));
96 void
97 Keymap::DumpKeymap()
99 if (fKeys.version != 3)
100 return;
102 // Print a chart of the normal, shift, control, option, option+shift,
103 // Caps, Caps+shift, Caps+option, and Caps+option+shift keys.
104 puts("Key #\tn\ts\tc\to\tos\tC\tCs\tCo\tCos\n");
106 for (uint8 i = 0; i < 128; i++) {
107 printf(" 0x%02x\t", i);
108 print_key(fChars, fKeys.normal_map[i]);
109 print_key(fChars, fKeys.shift_map[i]);
110 print_key(fChars, fKeys.control_map[i]);
111 print_key(fChars, fKeys.option_map[i]);
112 print_key(fChars, fKeys.option_shift_map[i]);
113 print_key(fChars, fKeys.caps_map[i]);
114 print_key(fChars, fKeys.caps_shift_map[i]);
115 print_key(fChars, fKeys.option_caps_map[i]);
116 print_key(fChars, fKeys.option_caps_shift_map[i], true);
117 fputs("\n", stdout);
122 //! Load a map from a file
123 status_t
124 Keymap::Load(const entry_ref& ref)
126 BEntry entry;
127 status_t status = entry.SetTo(&ref, true);
128 if (status != B_OK)
129 return status;
131 BFile file(&entry, B_READ_ONLY);
132 status = SetTo(file);
133 if (status != B_OK)
134 return status;
136 // fetch name from attribute and fall back to filename
138 ssize_t bytesRead = file.ReadAttr("keymap:name", B_STRING_TYPE, 0, fName,
139 sizeof(fName));
140 if (bytesRead > 0)
141 fName[bytesRead] = '\0';
142 else
143 strlcpy(fName, ref.name, sizeof(fName));
145 return B_OK;
149 //! We save a map to a file
150 status_t
151 Keymap::Save(const entry_ref& ref)
153 BFile file;
154 status_t status = file.SetTo(&ref,
155 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
156 if (status != B_OK) {
157 printf("error %s\n", strerror(status));
158 return status;
161 for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
162 ((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
164 ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
165 if (bytesWritten < (ssize_t)sizeof(fKeys))
166 status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
168 for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
169 ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
171 if (status == B_OK) {
172 fCharsSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
174 bytesWritten = file.Write(&fCharsSize, sizeof(uint32));
175 if (bytesWritten < (ssize_t)sizeof(uint32))
176 status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
178 fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
181 if (status == B_OK) {
182 bytesWritten = file.Write(fChars, fCharsSize);
183 if (bytesWritten < (ssize_t)fCharsSize)
184 status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
187 if (status == B_OK) {
188 file.WriteAttr("keymap:name", B_STRING_TYPE, 0, fName, strlen(fName));
189 // Failing would be non-fatal
192 return status;
196 status_t
197 Keymap::SetModifier(uint32 keyCode, uint32 modifier)
199 const uint32 kSingleModifierKeys = B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY
200 | B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY | B_LEFT_CONTROL_KEY
201 | B_RIGHT_CONTROL_KEY | B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY;
203 if ((modifier & kSingleModifierKeys) != 0)
204 modifier &= kSingleModifierKeys;
205 else if ((modifier & kModifierKeys) != 0)
206 modifier &= kModifierKeys;
208 if (modifier == B_CAPS_LOCK)
209 fKeys.caps_key = keyCode;
210 else if (modifier == B_NUM_LOCK)
211 fKeys.num_key = keyCode;
212 else if (modifier == B_SCROLL_LOCK)
213 fKeys.scroll_key = keyCode;
214 else if (modifier == B_LEFT_SHIFT_KEY)
215 fKeys.left_shift_key = keyCode;
216 else if (modifier == B_RIGHT_SHIFT_KEY)
217 fKeys.right_shift_key = keyCode;
218 else if (modifier == B_LEFT_COMMAND_KEY)
219 fKeys.left_command_key = keyCode;
220 else if (modifier == B_RIGHT_COMMAND_KEY)
221 fKeys.right_command_key = keyCode;
222 else if (modifier == B_LEFT_CONTROL_KEY)
223 fKeys.left_control_key = keyCode;
224 else if (modifier == B_RIGHT_CONTROL_KEY)
225 fKeys.right_control_key = keyCode;
226 else if (modifier == B_LEFT_OPTION_KEY)
227 fKeys.left_option_key = keyCode;
228 else if (modifier == B_RIGHT_OPTION_KEY)
229 fKeys.right_option_key = keyCode;
230 else if (modifier == B_MENU_KEY)
231 fKeys.menu_key = keyCode;
232 else
233 return B_BAD_VALUE;
235 if (fModificationMessage != NULL)
236 fTarget.SendMessage(fModificationMessage);
238 return B_OK;
242 //! Enables/disables the "deadness" of the given keycode/modifier combo.
243 void
244 Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled)
246 uint32 tableMask = 0;
247 int32 offset = Offset(keyCode, modifiers, &tableMask);
248 uint8 deadKeyIndex = DeadKeyIndex(offset);
249 if (deadKeyIndex > 0) {
250 uint32* deadTables[] = {
251 &fKeys.acute_tables,
252 &fKeys.grave_tables,
253 &fKeys.circumflex_tables,
254 &fKeys.dieresis_tables,
255 &fKeys.tilde_tables
258 if (enabled)
259 (*deadTables[deadKeyIndex - 1]) |= tableMask;
260 else
261 (*deadTables[deadKeyIndex - 1]) &= ~tableMask;
263 if (fModificationMessage != NULL)
264 fTarget.SendMessage(fModificationMessage);
269 /*! Returns the trigger character string that is currently set for the dead
270 key with the given index (which is 1..5).
272 void
273 Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger)
275 outTrigger = "";
276 if (deadKeyIndex < 1 || deadKeyIndex > 5)
277 return;
279 int32 deadOffsets[] = {
280 fKeys.acute_dead_key[1],
281 fKeys.grave_dead_key[1],
282 fKeys.circumflex_dead_key[1],
283 fKeys.dieresis_dead_key[1],
284 fKeys.tilde_dead_key[1]
287 int32 offset = deadOffsets[deadKeyIndex - 1];
288 if (offset < 0 || offset >= (int32)fCharsSize)
289 return;
291 uint32 deadNumBytes = fChars[offset];
292 if (!deadNumBytes)
293 return;
295 outTrigger.SetTo(&fChars[offset + 1], deadNumBytes);
299 /*! Sets the trigger character string that shall be used for the dead key
300 with the given index (which is 1..5).
302 void
303 Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger)
305 if (deadKeyIndex < 1 || deadKeyIndex > 5)
306 return;
308 int32 deadOffsets[] = {
309 fKeys.acute_dead_key[1],
310 fKeys.grave_dead_key[1],
311 fKeys.circumflex_dead_key[1],
312 fKeys.dieresis_dead_key[1],
313 fKeys.tilde_dead_key[1]
316 int32 offset = deadOffsets[deadKeyIndex - 1];
317 if (offset < 0 || offset >= (int32)fCharsSize)
318 return;
320 if (_SetChars(offset, trigger.String(), trigger.Length())) {
321 // reset modifier table such that new dead key is enabled wherever
322 // it is available
323 uint32* deadTables[] = {
324 &fKeys.acute_tables,
325 &fKeys.grave_tables,
326 &fKeys.circumflex_tables,
327 &fKeys.dieresis_tables,
328 &fKeys.tilde_tables
330 *deadTables[deadKeyIndex - 1]
331 = B_NORMAL_TABLE | B_SHIFT_TABLE | B_CONTROL_TABLE | B_OPTION_TABLE
332 | B_OPTION_SHIFT_TABLE | B_CAPS_TABLE | B_CAPS_SHIFT_TABLE
333 | B_OPTION_CAPS_TABLE | B_OPTION_CAPS_SHIFT_TABLE;
335 if (fModificationMessage != NULL)
336 fTarget.SendMessage(fModificationMessage);
341 status_t
342 Keymap::RestoreSystemDefault()
344 BPath path;
345 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
346 if (status != B_OK)
347 return status;
349 path.Append("Key_map");
351 BEntry entry(path.Path());
352 entry.Remove();
354 return Use();
358 //! We make our input server use the map in /boot/home/config/settings/Keymap
359 status_t
360 Keymap::Use()
362 status_t result = _restore_key_map_();
363 if (result == B_OK)
364 set_keyboard_locks(modifiers());
365 return result;
369 void
370 Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey,
371 const char* bytes, int32 numBytes)
373 int32 offset = Offset(keyCode, modifiers);
374 if (offset < 0)
375 return;
377 if (numBytes < 0)
378 numBytes = strlen(bytes);
379 if (numBytes > 6)
380 return;
382 if (_SetChars(offset, bytes, numBytes)) {
383 if (fModificationMessage != NULL)
384 fTarget.SendMessage(fModificationMessage);
389 Keymap&
390 Keymap::operator=(const Keymap& other)
392 if (this == &other)
393 return *this;
395 delete[] fChars;
396 delete fModificationMessage;
398 fChars = new(std::nothrow) char[other.fCharsSize];
399 if (fChars != NULL) {
400 memcpy(fChars, other.fChars, other.fCharsSize);
401 fCharsSize = other.fCharsSize;
402 } else
403 fCharsSize = 0;
405 memcpy(&fKeys, &other.fKeys, sizeof(key_map));
406 strlcpy(fName, other.fName, sizeof(fName));
408 fTarget = other.fTarget;
410 if (other.fModificationMessage != NULL)
411 fModificationMessage = new BMessage(*other.fModificationMessage);
413 return *this;
417 bool
418 Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes)
420 int32 oldNumBytes = fChars[offset];
422 if (oldNumBytes == numBytes
423 && !memcmp(&fChars[offset + 1], bytes, numBytes)) {
424 // nothing to do
425 return false;
428 int32 diff = numBytes - oldNumBytes;
429 if (diff != 0) {
430 fCharsSize += diff;
432 if (diff > 0) {
433 // make space for the new data
434 char* chars = new(std::nothrow) char[fCharsSize];
435 if (chars != NULL) {
436 memcpy(chars, fChars, offset + oldNumBytes + 1);
437 memcpy(&chars[offset + 1 + numBytes],
438 &fChars[offset + 1 + oldNumBytes],
439 fCharsSize - 2 - offset - diff);
440 delete[] fChars;
441 fChars = chars;
442 } else
443 return false;
444 } else if (diff < 0) {
445 // shrink table
446 memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes],
447 fCharsSize - offset - 2 - diff);
450 // update offsets
451 int32* data = fKeys.control_map;
452 int32 size = sizeof(fKeys.control_map) / 4 * 9
453 + sizeof(fKeys.acute_dead_key) / 4 * 5;
454 for (int32 i = 0; i < size; i++) {
455 if (data[i] > offset)
456 data[i] += diff;
460 memcpy(&fChars[offset + 1], bytes, numBytes);
461 fChars[offset] = numBytes;
463 return true;