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