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