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