xref: /haiku/src/preferences/keymap/ModifierKeysWindow.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2011-2014 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		John Scipione, jscipione@gmail.com
7  */
8 
9 
10 #include "ModifierKeysWindow.h"
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <Bitmap.h>
17 #include <Button.h>
18 #include <Catalog.h>
19 #include <CheckBox.h>
20 #include <ControlLook.h>
21 #include <FindDirectory.h>
22 #include <IconUtils.h>
23 #include <InterfaceDefs.h>
24 #include <LayoutBuilder.h>
25 #include <Locale.h>
26 #include <MenuField.h>
27 #include <MenuItem.h>
28 #include <Message.h>
29 #include <Path.h>
30 #include <PopUpMenu.h>
31 #include <Resources.h>
32 #include <SeparatorView.h>
33 #include <Size.h>
34 #include <StringView.h>
35 
36 #include "KeymapApplication.h"
37 
38 
39 #ifdef DEBUG_ALERT
40 #	define FTRACE(x) fprintf(x)
41 #else
42 #	define FTRACE(x) /* nothing */
43 #endif
44 
45 
46 enum {
47 	SHIFT_KEY = 0x00000001,
48 	CONTROL_KEY = 0x00000002,
49 	OPTION_KEY = 0x00000004,
50 	COMMAND_KEY = 0x00000008
51 };
52 
53 enum {
54 	MENU_ITEM_SHIFT = 0,
55 	MENU_ITEM_CONTROL,
56 	MENU_ITEM_OPTION,
57 	MENU_ITEM_COMMAND,
58 	MENU_ITEM_SEPERATOR,
59 	MENU_ITEM_DISABLED
60 };
61 
62 
63 static const uint32 kMsgHideShowIcons		= 'icon';
64 static const uint32 kMsgUpdateModifier		= 'upmd';
65 static const uint32 kMsgApplyModifiers 		= 'apmd';
66 static const uint32 kMsgRevertModifiers		= 'rvmd';
67 
68 
69 #undef B_TRANSLATION_CONTEXT
70 #define B_TRANSLATION_CONTEXT "Modifier keys window"
71 
72 
73 //	#pragma mark - ConflictView
74 
75 
76 ConflictView::ConflictView(const char* name)
77 	:
78 	BView(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(B_MINI_ICON)),
79 		name, B_FOLLOW_NONE, B_WILL_DRAW),
80 	fIcon(NULL),
81 	fStopIcon(NULL),
82 	fWarnIcon(NULL)
83 {
84 	_FillIcons();
85 }
86 
87 
88 ConflictView::~ConflictView()
89 {
90 	delete fStopIcon;
91 	delete fWarnIcon;
92 }
93 
94 
95 void
96 ConflictView::Draw(BRect updateRect)
97 {
98 	// Draw background
99 	if (Parent())
100 		SetLowColor(Parent()->ViewColor());
101 	else
102 		SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
103 
104 	FillRect(updateRect, B_SOLID_LOW);
105 
106 	if (fIcon == NULL)
107 		return;
108 
109 	// Draw icon
110 	SetDrawingMode(B_OP_ALPHA);
111 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
112 	DrawBitmapAsync(fIcon, BPoint(0, 0));
113 }
114 
115 
116 // get the icon
117 BBitmap*
118 ConflictView::Icon()
119 {
120 	return fIcon;
121 }
122 
123 
124 // show or hide the stop icon
125 void
126 ConflictView::SetStopIcon(bool show)
127 {
128 	fIcon = show ? fStopIcon : NULL;
129 	const char* tip = show ? B_TRANSLATE("Error: duplicate keys")
130 		: NULL;
131 	SetToolTip(tip);
132 }
133 
134 
135 // show or hide the warn icon
136 void
137 ConflictView::SetWarnIcon(bool show)
138 {
139 	fIcon = show ? fWarnIcon : NULL;
140 	const char* tip = show
141 		? B_TRANSLATE("Warning: left and right key roles do not match")
142 		: NULL;
143 	SetToolTip(tip);
144 }
145 
146 
147 //	#pragma mark - ConflictView private methods
148 
149 
150 // fill out the icons with the stop and warn symbols from app_server
151 void
152 ConflictView::_FillIcons()
153 {
154 	if (fStopIcon == NULL) {
155 		// Allocate the fStopIcon bitmap
156 		fStopIcon = new (std::nothrow) BBitmap(Bounds(), 0,
157 			B_RGBA32);
158 		if (fStopIcon->InitCheck() != B_OK) {
159 			FTRACE((stderr, "_FillIcons() - No memory for stop bitmap\n"));
160 			delete fStopIcon;
161 			fStopIcon = NULL;
162 			return;
163 		}
164 
165 		// load dialog-error icon bitmap
166 		if (BIconUtils::GetSystemIcon("dialog-error", fStopIcon) != B_OK) {
167 			delete fStopIcon;
168 			fStopIcon = NULL;
169 			return;
170 		}
171 	}
172 
173 	if (fWarnIcon == NULL) {
174 		// Allocate the fWarnIcon bitmap
175 		fWarnIcon = new (std::nothrow) BBitmap(Bounds(), 0,
176 			B_RGBA32);
177 		if (fWarnIcon->InitCheck() != B_OK) {
178 			FTRACE((stderr, "_FillIcons() - No memory for warn bitmap\n"));
179 			delete fWarnIcon;
180 			fWarnIcon = NULL;
181 			return;
182 		}
183 
184 		// load dialog-warning icon bitmap
185 		if (BIconUtils::GetSystemIcon("dialog-warning", fWarnIcon) != B_OK) {
186 			delete fWarnIcon;
187 			fWarnIcon = NULL;
188 			return;
189 		}
190 	}
191 }
192 
193 
194 //	#pragma mark - ModifierKeysWindow
195 
196 
197 ModifierKeysWindow::ModifierKeysWindow()
198 	:
199 	BWindow(BRect(0, 0, 360, 220), B_TRANSLATE("Modifier keys"),
200 		B_FLOATING_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
201 			| B_AUTO_UPDATE_SIZE_LIMITS)
202 {
203 	get_key_map(&fCurrentMap, &fCurrentBuffer);
204 	get_key_map(&fSavedMap, &fSavedBuffer);
205 
206 	BStringView* keyRole = new BStringView("key role",
207 		B_TRANSLATE_COMMENT("Role", "As in the role of a modifier key"));
208 	keyRole->SetAlignment(B_ALIGN_RIGHT);
209 	keyRole->SetFont(be_bold_font);
210 
211 	BStringView* keyLabel = new BStringView("key label",
212 		B_TRANSLATE_COMMENT("Key", "As in a computer keyboard key"));
213 	keyLabel->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
214 	keyLabel->SetFont(be_bold_font);
215 
216 	BMenuField* shiftMenuField = _CreateShiftMenuField();
217 	shiftMenuField->SetAlignment(B_ALIGN_RIGHT);
218 
219 	BMenuField* controlMenuField = _CreateControlMenuField();
220 	controlMenuField->SetAlignment(B_ALIGN_RIGHT);
221 
222 	BMenuField* optionMenuField = _CreateOptionMenuField();
223 	optionMenuField->SetAlignment(B_ALIGN_RIGHT);
224 
225 	BMenuField* commandMenuField = _CreateCommandMenuField();
226 	commandMenuField->SetAlignment(B_ALIGN_RIGHT);
227 
228 	fShiftConflictView = new ConflictView("shift warning view");
229 	fShiftConflictView->SetExplicitMaxSize(fShiftConflictView->Bounds().Size());
230 
231 	fControlConflictView = new ConflictView("control warning view");
232 	fControlConflictView->SetExplicitMaxSize(fControlConflictView->Bounds().Size());
233 
234 	fOptionConflictView = new ConflictView("option warning view");
235 	fOptionConflictView->SetExplicitMaxSize(fOptionConflictView->Bounds().Size());
236 
237 	fCommandConflictView = new ConflictView("command warning view");
238 	fCommandConflictView->SetExplicitMaxSize(fCommandConflictView->Bounds().Size());
239 
240 	fCancelButton = new BButton("cancelButton", B_TRANSLATE("Cancel"),
241 		new BMessage(B_QUIT_REQUESTED));
242 
243 	fRevertButton = new BButton("revertButton", B_TRANSLATE("Revert"),
244 		new BMessage(kMsgRevertModifiers));
245 	fRevertButton->SetEnabled(false);
246 
247 	fOkButton = new BButton("okButton", B_TRANSLATE("Set modifier keys"),
248 		new BMessage(kMsgApplyModifiers));
249 	fOkButton->MakeDefault(true);
250 
251 	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
252 		.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
253 			.Add(keyRole, 0, 0)
254 			.Add(keyLabel, 1, 0)
255 
256 			.Add(shiftMenuField->CreateLabelLayoutItem(), 0, 1)
257 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1, 1)
258 				.Add(shiftMenuField->CreateMenuBarLayoutItem())
259 				.Add(fShiftConflictView)
260 				.End()
261 
262 			.Add(controlMenuField->CreateLabelLayoutItem(), 0, 2)
263 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1, 2)
264 				.Add(controlMenuField->CreateMenuBarLayoutItem())
265 				.Add(fControlConflictView)
266 				.End()
267 
268 			.Add(optionMenuField->CreateLabelLayoutItem(), 0, 3)
269 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1, 3)
270 				.Add(optionMenuField->CreateMenuBarLayoutItem())
271 				.Add(fOptionConflictView)
272 				.End()
273 
274 			.Add(commandMenuField->CreateLabelLayoutItem(), 0, 4)
275 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1, 4)
276 				.Add(commandMenuField->CreateMenuBarLayoutItem())
277 				.Add(fCommandConflictView)
278 				.End()
279 			.End()
280 		.AddGlue()
281 		.AddGroup(B_HORIZONTAL)
282 			.Add(fCancelButton)
283 			.AddGlue()
284 			.Add(fRevertButton)
285 			.Add(fOkButton)
286 			.End()
287 		.SetInsets(B_USE_WINDOW_SPACING)
288 		.End();
289 
290 	_MarkMenuItems();
291 	_ValidateDuplicateKeys();
292 
293 	PostMessage(kMsgHideShowIcons);
294 }
295 
296 
297 ModifierKeysWindow::~ModifierKeysWindow()
298 {
299 	be_app->PostMessage(kMsgCloseModifierKeysWindow);
300 }
301 
302 
303 void
304 ModifierKeysWindow::MessageReceived(BMessage* message)
305 {
306 	switch (message->what) {
307 		case kMsgHideShowIcons:
308 			_HideShowIcons();
309 			break;
310 
311 		case kMsgUpdateModifier:
312 		{
313 			int32 menuitem = MENU_ITEM_SHIFT;
314 			int32 key = -1;
315 
316 			for (; menuitem <= MENU_ITEM_COMMAND; menuitem++) {
317 				if (message->FindInt32(_KeyToString(menuitem), &key) == B_OK)
318 					break;
319 			}
320 
321 			if (key == -1)
322 				return;
323 
324 			// menuitem contains the item we want to set
325 			// key contains the item we want to set it to.
326 
327 			switch (menuitem) {
328 				case MENU_ITEM_SHIFT:
329 					fCurrentMap->left_shift_key = _KeyToKeyCode(key);
330 					fCurrentMap->right_shift_key = _KeyToKeyCode(key, true);
331 					break;
332 
333 				case MENU_ITEM_CONTROL:
334 					fCurrentMap->left_control_key = _KeyToKeyCode(key);
335 					fCurrentMap->right_control_key = _KeyToKeyCode(key, true);
336 					break;
337 
338 				case MENU_ITEM_OPTION:
339 					fCurrentMap->left_option_key = _KeyToKeyCode(key);
340 					fCurrentMap->right_option_key = _KeyToKeyCode(key, true);
341 					break;
342 
343 				case MENU_ITEM_COMMAND:
344 					fCurrentMap->left_command_key = _KeyToKeyCode(key);
345 					fCurrentMap->right_command_key = _KeyToKeyCode(key, true);
346 					break;
347 			}
348 
349 			_MarkMenuItems();
350 			_ValidateDuplicateKeys();
351 			_HideShowIcons();
352 
353 			// enable/disable revert button
354 			fRevertButton->SetEnabled(
355 				memcmp(fCurrentMap, fSavedMap, sizeof(key_map)));
356 			break;
357 		}
358 
359 		// OK button
360 		case kMsgApplyModifiers:
361 		{
362 			// if duplicate modifiers are found, don't update
363 			if (_DuplicateKeys() != 0)
364 				break;
365 
366 			BMessage* updateModifiers = new BMessage(kMsgUpdateModifierKeys);
367 
368 			if (fCurrentMap->left_shift_key != fSavedMap->left_shift_key) {
369 				updateModifiers->AddUInt32("left_shift_key",
370 					fCurrentMap->left_shift_key);
371 			}
372 
373 			if (fCurrentMap->right_shift_key != fSavedMap->right_shift_key) {
374 				updateModifiers->AddUInt32("right_shift_key",
375 					fCurrentMap->right_shift_key);
376 			}
377 
378 			if (fCurrentMap->left_control_key != fSavedMap->left_control_key) {
379 				updateModifiers->AddUInt32("left_control_key",
380 					fCurrentMap->left_control_key);
381 			}
382 
383 			if (fCurrentMap->right_control_key
384 				!= fSavedMap->right_control_key) {
385 				updateModifiers->AddUInt32("right_control_key",
386 					fCurrentMap->right_control_key);
387 			}
388 
389 			if (fCurrentMap->left_option_key != fSavedMap->left_option_key) {
390 				updateModifiers->AddUInt32("left_option_key",
391 					fCurrentMap->left_option_key);
392 			}
393 
394 			if (fCurrentMap->right_option_key != fSavedMap->right_option_key) {
395 				updateModifiers->AddUInt32("right_option_key",
396 					fCurrentMap->right_option_key);
397 			}
398 
399 			if (fCurrentMap->left_command_key != fSavedMap->left_command_key) {
400 				updateModifiers->AddUInt32("left_command_key",
401 					fCurrentMap->left_command_key);
402 			}
403 
404 			if (fCurrentMap->right_command_key
405 				!= fSavedMap->right_command_key) {
406 				updateModifiers->AddUInt32("right_command_key",
407 					fCurrentMap->right_command_key);
408 			}
409 
410 			// KeymapWindow updates the modifiers
411 			be_app->PostMessage(updateModifiers);
412 
413 			// we are done here, close the window
414 			this->PostMessage(B_QUIT_REQUESTED);
415 			break;
416 		}
417 
418 		// Revert button
419 		case kMsgRevertModifiers:
420 			memcpy(fCurrentMap, fSavedMap, sizeof(key_map));
421 
422 			_MarkMenuItems();
423 			_ValidateDuplicateKeys();
424 			_HideShowIcons();
425 
426 			fRevertButton->SetEnabled(false);
427 			break;
428 
429 		default:
430 			BWindow::MessageReceived(message);
431 	}
432 }
433 
434 
435 //	#pragma mark - ModifierKeysWindow private methods
436 
437 
438 BMenuField*
439 ModifierKeysWindow::_CreateShiftMenuField()
440 {
441 	fShiftMenu = new BPopUpMenu(
442 		B_TRANSLATE_NOCOLLECT(_KeyToString(MENU_ITEM_SHIFT)), true, true);
443 
444 	for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_DISABLED; key++) {
445 		if (key == MENU_ITEM_SEPERATOR) {
446 			// add separator item
447 			BSeparatorItem* separator = new BSeparatorItem;
448 			fShiftMenu->AddItem(separator, MENU_ITEM_SEPERATOR);
449 			continue;
450 		}
451 
452 		BMessage* message = new BMessage(kMsgUpdateModifier);
453 		message->AddInt32(_KeyToString(MENU_ITEM_SHIFT), key);
454 		BMenuItem* item = new BMenuItem(
455 			B_TRANSLATE_NOCOLLECT(_KeyToString(key)), message);
456 
457 		fShiftMenu->AddItem(item, key);
458 	}
459 
460 	fShiftMenu->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
461 		B_ALIGN_VERTICAL_UNSET));
462 
463 	return new BMenuField(B_TRANSLATE_COMMENT("Shift:", "Shift key role name"),
464 		fShiftMenu);
465 }
466 
467 
468 BMenuField*
469 ModifierKeysWindow::_CreateControlMenuField()
470 {
471 	fControlMenu = new BPopUpMenu(
472 		B_TRANSLATE_NOCOLLECT(_KeyToString(MENU_ITEM_CONTROL)), true, true);
473 
474 	for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_DISABLED; key++) {
475 		if (key == MENU_ITEM_SEPERATOR) {
476 			// add separator item
477 			BSeparatorItem* separator = new BSeparatorItem;
478 			fControlMenu->AddItem(separator, MENU_ITEM_SEPERATOR);
479 			continue;
480 		}
481 
482 		BMessage* message = new BMessage(kMsgUpdateModifier);
483 		message->AddInt32(_KeyToString(MENU_ITEM_CONTROL), key);
484 		BMenuItem* item = new BMenuItem(
485 			B_TRANSLATE_NOCOLLECT(_KeyToString(key)), message);
486 
487 		fControlMenu->AddItem(item, key);
488 	}
489 
490 	fControlMenu->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
491 		B_ALIGN_VERTICAL_UNSET));
492 
493 	return new BMenuField(B_TRANSLATE_COMMENT("Control:",
494 		"Control key role name"), fControlMenu);
495 }
496 
497 
498 BMenuField*
499 ModifierKeysWindow::_CreateOptionMenuField()
500 {
501 	fOptionMenu = new BPopUpMenu(
502 		B_TRANSLATE_NOCOLLECT(_KeyToString(MENU_ITEM_OPTION)), true, true);
503 
504 	for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_DISABLED; key++) {
505 		if (key == MENU_ITEM_SEPERATOR) {
506 			// add separator item
507 			BSeparatorItem* separator = new BSeparatorItem;
508 			fOptionMenu->AddItem(separator, MENU_ITEM_SEPERATOR);
509 			continue;
510 		}
511 
512 		BMessage* message = new BMessage(kMsgUpdateModifier);
513 		message->AddInt32(_KeyToString(MENU_ITEM_OPTION), key);
514 		BMenuItem* item = new BMenuItem(
515 			B_TRANSLATE_NOCOLLECT(_KeyToString(key)), message);
516 
517 		fOptionMenu->AddItem(item, key);
518 	}
519 
520 	fOptionMenu->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
521 		B_ALIGN_VERTICAL_UNSET));
522 
523 	return new BMenuField(B_TRANSLATE_COMMENT("Option:",
524 		"Option key role name"), fOptionMenu);
525 }
526 
527 
528 BMenuField*
529 ModifierKeysWindow::_CreateCommandMenuField()
530 {
531 	fCommandMenu = new BPopUpMenu(
532 		B_TRANSLATE_NOCOLLECT(_KeyToString(MENU_ITEM_COMMAND)), true, true);
533 
534 	for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_DISABLED; key++) {
535 		if (key == MENU_ITEM_SEPERATOR) {
536 			// add separator item
537 			BSeparatorItem* separator = new BSeparatorItem;
538 			fCommandMenu->AddItem(separator, MENU_ITEM_SEPERATOR);
539 			continue;
540 		}
541 
542 		BMessage* message = new BMessage(kMsgUpdateModifier);
543 		message->AddInt32(_KeyToString(MENU_ITEM_COMMAND), key);
544 		BMenuItem* item = new BMenuItem(
545 			B_TRANSLATE_NOCOLLECT(_KeyToString(key)), message);
546 		fCommandMenu->AddItem(item, key);
547 	}
548 
549 	fCommandMenu->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
550 		B_ALIGN_VERTICAL_UNSET));
551 
552 	return new BMenuField(B_TRANSLATE_COMMENT("Command:",
553 		"Command key role name"), fCommandMenu);
554 }
555 
556 
557 void
558 ModifierKeysWindow::_MarkMenuItems()
559 {
560 	for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_DISABLED; key++) {
561 		if (key == MENU_ITEM_SEPERATOR)
562 			continue;
563 
564 		if (fCurrentMap->left_shift_key == _KeyToKeyCode(key)
565 			&& fCurrentMap->right_shift_key == _KeyToKeyCode(key, true)) {
566 			fShiftMenu->ItemAt(key)->SetMarked(true);
567 		}
568 
569 		if (fCurrentMap->left_control_key == _KeyToKeyCode(key)
570 			&& fCurrentMap->right_control_key == _KeyToKeyCode(key, true)) {
571 			fControlMenu->ItemAt(key)->SetMarked(true);
572 		}
573 
574 		if (fCurrentMap->left_option_key == _KeyToKeyCode(key)
575 			&& fCurrentMap->right_option_key == _KeyToKeyCode(key, true)) {
576 			fOptionMenu->ItemAt(key)->SetMarked(true);
577 		}
578 
579 		if (fCurrentMap->left_command_key == _KeyToKeyCode(key)
580 			&& fCurrentMap->right_command_key == _KeyToKeyCode(key, true)) {
581 			fCommandMenu->ItemAt(key)->SetMarked(true);
582 		}
583 	}
584 
585 	// Set the warning icon if not marked
586 	BBitmap* shiftIcon = fShiftConflictView->Icon();
587 	BBitmap* controlIcon = fControlConflictView->Icon();
588 	BBitmap* optionIcon = fOptionConflictView->Icon();
589 	BBitmap* commandIcon = fCommandConflictView->Icon();
590 
591 	fShiftConflictView->SetWarnIcon(fShiftMenu->FindMarked() == NULL);
592 	fControlConflictView->SetWarnIcon(fControlMenu->FindMarked() == NULL);
593 	fOptionConflictView->SetWarnIcon(fOptionMenu->FindMarked() == NULL);
594 	fCommandConflictView->SetWarnIcon(fCommandMenu->FindMarked() == NULL);
595 
596 	// if there was a change invalidate the view
597 	if (shiftIcon != fShiftConflictView->Icon())
598 		fShiftConflictView->Invalidate();
599 
600 	if (controlIcon != fControlConflictView->Icon())
601 		fControlConflictView->Invalidate();
602 
603 	if (optionIcon != fOptionConflictView->Icon())
604 		fOptionConflictView->Invalidate();
605 
606 	if (commandIcon != fCommandConflictView->Icon())
607 		fCommandConflictView->Invalidate();
608 }
609 
610 
611 // get the string for a modifier key
612 const char*
613 ModifierKeysWindow::_KeyToString(int32 key)
614 {
615 	switch (key) {
616 		case MENU_ITEM_SHIFT:
617 			return B_TRANSLATE_COMMENT("Shift key",
618 				"Label of key above Ctrl, usually Shift");
619 
620 		case MENU_ITEM_CONTROL:
621 			return B_TRANSLATE_COMMENT("Ctrl key",
622 				"Label of key farthest from the spacebar, usually Ctrl"
623 				"e.g. Strg for German keyboard");
624 
625 		case MENU_ITEM_OPTION:
626 			return B_TRANSLATE_COMMENT("Win/Cmd key",
627 				"Label of the \"Windows\" key (PC)/Command key (Mac)");
628 
629 		case MENU_ITEM_COMMAND:
630 			return B_TRANSLATE_COMMENT("Alt/Opt key",
631 				"Label of Alt key (PC)/Option key (Mac)");
632 
633 		case MENU_ITEM_DISABLED:
634 			return B_TRANSLATE_COMMENT("Disabled", "Do nothing");
635 	}
636 
637 	return "";
638 }
639 
640 
641 // get the keycode for a modifier key
642 uint32
643 ModifierKeysWindow::_KeyToKeyCode(int32 key, bool right)
644 {
645 	switch (key) {
646 		case MENU_ITEM_SHIFT:
647 			if (right)
648 				return 0x56;
649 			return 0x4b;
650 
651 		case MENU_ITEM_CONTROL:
652 			if (right)
653 				return 0x60;
654 			return 0x5c;
655 
656 		case MENU_ITEM_OPTION:
657 			if (right)
658 				return 0x67;
659 			return 0x66;
660 
661 		case MENU_ITEM_COMMAND:
662 			if (right)
663 				return 0x5f;
664 			return 0x5d;
665 
666 		case MENU_ITEM_DISABLED:
667 			return 0;
668 	}
669 
670 	return 0;
671 }
672 
673 
674 // validate duplicate keys
675 void
676 ModifierKeysWindow::_ValidateDuplicateKeys()
677 {
678 	uint32 dupMask = _DuplicateKeys();
679 
680 	BBitmap* shiftIcon = fShiftConflictView->Icon();
681 	BBitmap* controlIcon = fControlConflictView->Icon();
682 	BBitmap* optionIcon = fOptionConflictView->Icon();
683 	BBitmap* commandIcon = fCommandConflictView->Icon();
684 
685 	if ((dupMask & SHIFT_KEY) != 0)
686 		fShiftConflictView->SetStopIcon(true);
687 
688 	if ((dupMask & CONTROL_KEY) != 0)
689 		fControlConflictView->SetStopIcon(true);
690 
691 	if ((dupMask & OPTION_KEY) != 0)
692 		fOptionConflictView->SetStopIcon(true);
693 
694 	if ((dupMask & COMMAND_KEY) != 0)
695 		fCommandConflictView->SetStopIcon(true);
696 
697 	fOkButton->SetEnabled(dupMask == 0);
698 
699 	// if there was a change invalidate the view
700 	if (shiftIcon != fShiftConflictView->Icon())
701 		fShiftConflictView->Invalidate();
702 
703 	if (controlIcon != fControlConflictView->Icon())
704 		fControlConflictView->Invalidate();
705 
706 	if (optionIcon != fOptionConflictView->Icon())
707 		fOptionConflictView->Invalidate();
708 
709 	if (commandIcon != fCommandConflictView->Icon())
710 		fCommandConflictView->Invalidate();
711 }
712 
713 
714 // return a mask marking which keys are duplicates of each other for
715 // validation. Shift = 0, Control = 1, Option = 2, Command = 3
716 uint32
717 ModifierKeysWindow::_DuplicateKeys()
718 {
719 	uint32 duplicateMask = 0;
720 
721 	for (int32 testKey = MENU_ITEM_SHIFT; testKey <= MENU_ITEM_COMMAND;
722 		testKey++) {
723 		uint32 testLeft = 0;
724 		uint32 testRight = 0;
725 
726 		switch (testKey) {
727 			case MENU_ITEM_SHIFT:
728 				testLeft = fCurrentMap->left_shift_key;
729 				testRight = fCurrentMap->right_shift_key;
730 				break;
731 
732 			case MENU_ITEM_CONTROL:
733 				testLeft = fCurrentMap->left_control_key;
734 				testRight = fCurrentMap->right_control_key;
735 				break;
736 
737 			case MENU_ITEM_OPTION:
738 				testLeft = fCurrentMap->left_option_key;
739 				testRight = fCurrentMap->right_option_key;
740 				break;
741 
742 			case MENU_ITEM_COMMAND:
743 				testLeft = fCurrentMap->left_command_key;
744 				testRight = fCurrentMap->right_command_key;
745 				break;
746 		}
747 
748 		if (testLeft == 0 && testRight == 0)
749 			continue;
750 
751 		for (int32 key = MENU_ITEM_SHIFT; key <= MENU_ITEM_COMMAND; key++) {
752 			if (key == testKey) {
753 				// skip over yourself
754 				continue;
755 			}
756 
757 			uint32 left = 0;
758 			uint32 right = 0;
759 
760 			switch (key) {
761 				case MENU_ITEM_SHIFT:
762 					left = fCurrentMap->left_shift_key;
763 					right = fCurrentMap->right_shift_key;
764 					break;
765 
766 				case MENU_ITEM_CONTROL:
767 					left = fCurrentMap->left_control_key;
768 					right = fCurrentMap->right_control_key;
769 					break;
770 
771 				case MENU_ITEM_OPTION:
772 					left = fCurrentMap->left_option_key;
773 					right = fCurrentMap->right_option_key;
774 					break;
775 
776 				case MENU_ITEM_COMMAND:
777 					left = fCurrentMap->left_command_key;
778 					right = fCurrentMap->right_command_key;
779 					break;
780 			}
781 
782 			if (left == 0 && right == 0)
783 				continue;
784 
785 			if (left == testLeft || right == testRight) {
786 				duplicateMask |= 1 << testKey;
787 				duplicateMask |= 1 << key;
788 			}
789 		}
790 	}
791 
792 	return duplicateMask;
793 }
794 
795 
796 void
797 ModifierKeysWindow::_HideShowIcons()
798 {
799 	if (fShiftConflictView->Icon() == NULL) {
800 		while (!fShiftConflictView->IsHidden())
801 			fShiftConflictView->Hide();
802 	} else {
803 		while (fShiftConflictView->IsHidden())
804 			fShiftConflictView->Show();
805 	}
806 
807 	if (fControlConflictView->Icon() == NULL) {
808 		while (!fControlConflictView->IsHidden())
809 			fControlConflictView->Hide();
810 	} else {
811 		while (fControlConflictView->IsHidden())
812 			fControlConflictView->Show();
813 	}
814 
815 	if (fOptionConflictView->Icon() == NULL) {
816 		while (!fOptionConflictView->IsHidden())
817 			fOptionConflictView->Hide();
818 	} else {
819 		while (fOptionConflictView->IsHidden())
820 			fOptionConflictView->Show();
821 	}
822 
823 	if (fCommandConflictView->Icon() == NULL) {
824 		while (!fCommandConflictView->IsHidden())
825 			fCommandConflictView->Hide();
826 	} else {
827 		while (fCommandConflictView->IsHidden())
828 			fCommandConflictView->Show();
829 	}
830 }
831