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