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