xref: /haiku/src/preferences/keymap/ModifierKeysWindow.cpp (revision 1b6bc2675fe3691538c8764ab016593f3b06ca53)
1 /*
2  * Copyright 2011 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 <Catalog.h>
17 #include <GroupLayout.h>
18 #include <GridLayoutBuilder.h>
19 #include <GroupLayoutBuilder.h>
20 #include <Locale.h>
21 #include <LayoutBuilder.h>
22 #include <MenuItem.h>
23 #include <Message.h>
24 #include <Size.h>
25 #include <StringView.h>
26 
27 #include "KeymapApplication.h"
28 
29 
30 enum {
31 	MENU_ITEM_CAPS_LOCK = 0,
32 	MENU_ITEM_CONTROL,
33 	MENU_ITEM_OPTION,
34 	MENU_ITEM_COMMAND,
35 	MENU_ITEM_SEPERATOR,
36 	MENU_ITEM_DISABLED
37 };
38 
39 static const uint32 kMsgUpdateModifier		= 'upmd';
40 static const uint32 kMsgApplyModifiers 		= 'apmd';
41 static const uint32 kMsgRevertModifiers		= 'rvmd';
42 
43 
44 #undef B_TRANSLATE_CONTEXT
45 #define B_TRANSLATE_CONTEXT "Modifier Keys window"
46 
47 
48 ModifierKeysWindow::ModifierKeysWindow()
49 	:
50 	BWindow(BRect(80, 50, 400, 260), B_TRANSLATE("Modifier Keys"),
51 		B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
52 		| B_AUTO_UPDATE_SIZE_LIMITS)
53 {
54 	get_key_map(&fCurrentMap, &fCurrentBuffer);
55 	get_key_map(&fSavedMap, &fSavedBuffer);
56 
57 	BStringView* capsLockStringView
58 		= new BStringView("caps", B_TRANSLATE("Caps Lock:"));
59 	capsLockStringView->SetExplicitMaxSize(
60 		BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
61 
62 	BStringView* controlStringView
63 		= new BStringView("control", B_TRANSLATE("Control:"));
64 	controlStringView->SetExplicitMaxSize(
65 		BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
66 
67 	BStringView* optionStringView
68 		= new BStringView("option", B_TRANSLATE("Option:"));
69 	optionStringView->SetExplicitMaxSize(
70 		BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
71 
72 	BStringView* commandStringView
73 		= new BStringView("command", B_TRANSLATE("Command:"));
74 	commandStringView->SetExplicitMaxSize(
75 		BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
76 
77 	fCancelButton = new BButton("CancelButton", B_TRANSLATE("Cancel"),
78 		new BMessage(B_QUIT_REQUESTED));
79 
80 	fRevertButton = new BButton("revertButton", B_TRANSLATE("Revert"),
81 		new BMessage(kMsgRevertModifiers));
82 	fRevertButton->SetEnabled(false);
83 
84 	fOkButton = new BButton("OkButton", B_TRANSLATE("OK"),
85 		new BMessage(kMsgApplyModifiers));
86 	fOkButton->MakeDefault(true);
87 
88 	// Build the layout
89 	SetLayout(new BGroupLayout(B_VERTICAL));
90 
91 	AddChild(BGroupLayoutBuilder(B_VERTICAL, 10)
92 		.Add(BGridLayoutBuilder(10, 10)
93 			.Add(capsLockStringView, 0, 0)
94 			.Add(_CreateCapsLockMenuField(), 1, 0)
95 
96 			.Add(controlStringView, 0, 1)
97 			.Add(_CreateControlMenuField(), 1, 1)
98 
99 			.Add(optionStringView, 0, 2)
100 			.Add(_CreateOptionMenuField(), 1, 2)
101 
102 			.Add(commandStringView, 0, 3)
103 			.Add(_CreateCommandMenuField(), 1, 3)
104 		)
105 		.AddGlue()
106 		.AddGroup(B_HORIZONTAL, 10)
107 			.Add(fCancelButton)
108 			.AddGlue()
109 			.Add(fRevertButton)
110 			.Add(fOkButton)
111 		.End()
112 		.SetInsets(10, 10, 10, 10)
113 	);
114 
115 	CenterOnScreen();
116 }
117 
118 
119 ModifierKeysWindow::~ModifierKeysWindow()
120 {
121 	be_app->PostMessage(kMsgCloseModifierKeysWindow);
122 }
123 
124 
125 void
126 ModifierKeysWindow::MessageReceived(BMessage* message)
127 {
128 	switch (message->what) {
129 		case kMsgUpdateModifier:
130 		{
131 			int32 menu = MENU_ITEM_CAPS_LOCK;
132 			int32 key = -1;
133 
134 			for (; menu <= MENU_ITEM_COMMAND; menu++) {
135 				if (message->FindInt32(_KeyToString(menu), &key) == B_OK)
136 					break;
137 			}
138 
139 			if (key == -1) {
140 				// No option was found, don't update
141 				return;
142 			}
143 
144 			// Now 'menu' contains the menu we want to set and 'key' contains
145 			// the option we want to set it to.
146 
147 			switch (menu) {
148 				case MENU_ITEM_CAPS_LOCK:
149 					fCurrentMap->caps_key = _KeyToKeyCode(key);
150 					fCapsLockMenu->ItemAt(key)->SetMarked(true);
151 					break;
152 
153 				case MENU_ITEM_CONTROL:
154 					fCurrentMap->left_control_key
155 						= _KeyToKeyCode(key);
156 					if (key != MENU_ITEM_CAPS_LOCK) {
157 						fCurrentMap->right_control_key
158 							= _KeyToKeyCode(key, true);
159 					}
160 
161 					fControlMenu->ItemAt(key)->SetMarked(true);
162 					break;
163 
164 				case MENU_ITEM_OPTION:
165 					fCurrentMap->left_option_key
166 						= _KeyToKeyCode(key);
167 					if (key != MENU_ITEM_CAPS_LOCK) {
168 						fCurrentMap->right_option_key
169 							= _KeyToKeyCode(key, true);
170 					}
171 
172 					fOptionMenu->ItemAt(key)->SetMarked(true);
173 					break;
174 
175 				case MENU_ITEM_COMMAND:
176 					fCurrentMap->left_command_key
177 						= _KeyToKeyCode(key);
178 					if (key != MENU_ITEM_CAPS_LOCK) {
179 						fCurrentMap->right_command_key
180 							= _KeyToKeyCode(key, true);
181 					}
182 
183 					fCommandMenu->ItemAt(key)->SetMarked(true);
184 					break;
185 			}
186 
187 			fRevertButton->SetEnabled(memcmp(fCurrentMap, fSavedMap,
188 				sizeof(key_map)));
189 			break;
190 		}
191 
192 		// Ok button
193 		case kMsgApplyModifiers:
194 		{
195 			BMessage* updateModifiers = new BMessage(kMsgUpdateModifiers);
196 
197 			if (fCurrentMap->caps_key != fSavedMap->caps_key)
198 				updateModifiers->AddUInt32("caps_key", fCurrentMap->caps_key);
199 
200 			if (fCurrentMap->left_control_key != fSavedMap->left_control_key) {
201 				updateModifiers->AddUInt32("left_control_key",
202 					fCurrentMap->left_control_key);
203 			}
204 
205 			if (fCurrentMap->right_control_key
206 				!= fSavedMap->right_control_key) {
207 				updateModifiers->AddUInt32("right_control_key",
208 					fCurrentMap->right_control_key);
209 			}
210 
211 			if (fCurrentMap->left_option_key != fSavedMap->left_option_key) {
212 				updateModifiers->AddUInt32("left_option_key",
213 					fCurrentMap->left_option_key);
214 			}
215 
216 			if (fCurrentMap->right_option_key != fSavedMap->right_option_key) {
217 				updateModifiers->AddUInt32("right_option_key",
218 					fCurrentMap->right_option_key);
219 			}
220 
221 			if (fCurrentMap->left_command_key != fSavedMap->left_command_key) {
222 				updateModifiers->AddUInt32("left_command_key",
223 					fCurrentMap->left_command_key);
224 			}
225 
226 			if (fCurrentMap->right_command_key
227 				!= fSavedMap->right_command_key) {
228 				updateModifiers->AddUInt32("right_command_key",
229 					fCurrentMap->right_command_key);
230 			}
231 
232 			// Tell KeymapWindow to update the modifier keys
233 			be_app->PostMessage(updateModifiers);
234 
235 			// We are done here, close the window
236 			this->PostMessage(B_QUIT_REQUESTED);
237 			break;
238 		}
239 
240 		// Revert button
241 		case kMsgRevertModifiers:
242 			memcpy(fCurrentMap, fSavedMap, sizeof(key_map));
243 
244 			_MarkMenuItems();
245 			fRevertButton->SetEnabled(false);
246 			break;
247 
248 		default:
249 			BWindow::MessageReceived(message);
250 	}
251 }
252 
253 
254 BMenuField*
255 ModifierKeysWindow::_CreateCapsLockMenuField()
256 {
257 	fCapsLockMenu = new BPopUpMenu(
258 		B_TRANSLATE(_KeyToString(MENU_ITEM_CAPS_LOCK)), true, true);
259 
260 	for (int32 key = MENU_ITEM_CAPS_LOCK; key <= MENU_ITEM_DISABLED; key++) {
261 		if (key == MENU_ITEM_SEPERATOR) {
262 			BSeparatorItem* separator = new BSeparatorItem;
263 			fCapsLockMenu->AddItem(separator, key);
264 		} else {
265 			BMessage* message = new BMessage(kMsgUpdateModifier);
266 			message->AddInt32(_KeyToString(MENU_ITEM_CAPS_LOCK), key);
267 
268 			BMenuItem* item = new BMenuItem(B_TRANSLATE(_KeyToString(key)),
269 				message);
270 
271 			if (fCurrentMap->caps_key == _KeyToKeyCode(key))
272 				item->SetMarked(true);
273 
274 			fCapsLockMenu->AddItem(item, key);
275 		}
276 	}
277 
278 	return new BMenuField(NULL, fCapsLockMenu);
279 }
280 
281 
282 BMenuField*
283 ModifierKeysWindow::_CreateControlMenuField()
284 {
285 	fControlMenu = new BPopUpMenu(
286 		B_TRANSLATE(_KeyToString(MENU_ITEM_CONTROL)), true, true);
287 
288 	for (int32 key = MENU_ITEM_CAPS_LOCK; key <= MENU_ITEM_COMMAND; key++) {
289 		BMessage* message = new BMessage(kMsgUpdateModifier);
290 		message->AddInt32(_KeyToString(MENU_ITEM_CONTROL), key);
291 
292 		BMenuItem* item = new BMenuItem(B_TRANSLATE(_KeyToString(key)),
293 			message);
294 
295 		if (fCurrentMap->left_control_key == _KeyToKeyCode(key)
296 			&& fCurrentMap->right_control_key == _KeyToKeyCode(key, true))
297 			item->SetMarked(true);
298 
299 		fControlMenu->AddItem(item, key);
300 	}
301 
302 	return new BMenuField(NULL, fControlMenu);
303 }
304 
305 
306 BMenuField*
307 ModifierKeysWindow::_CreateOptionMenuField()
308 {
309 	fOptionMenu = new BPopUpMenu(
310 		B_TRANSLATE(_KeyToString(MENU_ITEM_OPTION)), true, true);
311 
312 	for (int32 key = MENU_ITEM_CAPS_LOCK; key <= MENU_ITEM_COMMAND; key++) {
313 		BMessage* message = new BMessage(kMsgUpdateModifier);
314 		message->AddInt32(_KeyToString(MENU_ITEM_OPTION), key);
315 
316 		BMenuItem* item = new BMenuItem(B_TRANSLATE(_KeyToString(key)),
317 			message);
318 
319 		if (fCurrentMap->left_option_key == _KeyToKeyCode(key)
320 			&& fCurrentMap->right_option_key == _KeyToKeyCode(key, true))
321 			item->SetMarked(true);
322 
323 		fOptionMenu->AddItem(item, key);
324 	}
325 
326 	return new BMenuField(NULL, fOptionMenu);
327 }
328 
329 
330 BMenuField*
331 ModifierKeysWindow::_CreateCommandMenuField()
332 {
333 	fCommandMenu = new BPopUpMenu(
334 		B_TRANSLATE(_KeyToString(MENU_ITEM_COMMAND)), true, true);
335 
336 	for (int32 key = MENU_ITEM_CAPS_LOCK; key <= MENU_ITEM_COMMAND; key++) {
337 		BMessage* message = new BMessage(kMsgUpdateModifier);
338 		message->AddInt32(_KeyToString(MENU_ITEM_COMMAND), key);
339 
340 		BMenuItem* item = new BMenuItem(B_TRANSLATE(_KeyToString(key)),
341 			message);
342 
343 		if (fCurrentMap->left_command_key == _KeyToKeyCode(key)
344 			&& fCurrentMap->right_command_key == _KeyToKeyCode(key, true))
345 			item->SetMarked(true);
346 
347 		fCommandMenu->AddItem(item, key);
348 	}
349 
350 	return new BMenuField(NULL, fCommandMenu);
351 }
352 
353 
354 void
355 ModifierKeysWindow::_MarkMenuItems()
356 {
357 	for (int32 key = MENU_ITEM_CAPS_LOCK; key <= MENU_ITEM_COMMAND; key++) {
358 		if (fCurrentMap->caps_key == _KeyToKeyCode(key))
359 			fCapsLockMenu->ItemAt(key)->SetMarked(true);
360 
361 		if (fCurrentMap->left_control_key == _KeyToKeyCode(key)
362 			&& fCurrentMap->right_control_key == _KeyToKeyCode(key, true))
363 			fControlMenu->ItemAt(key)->SetMarked(true);
364 
365 		if (fCurrentMap->left_option_key == _KeyToKeyCode(key)
366 			&& fCurrentMap->right_option_key == _KeyToKeyCode(key, true))
367 			fOptionMenu->ItemAt(key)->SetMarked(true);
368 
369 		if (fCurrentMap->left_command_key == _KeyToKeyCode(key)
370 			&& fCurrentMap->right_command_key == _KeyToKeyCode(key, true))
371 			fCommandMenu->ItemAt(key)->SetMarked(true);
372 	}
373 
374 	// Check if caps lock is disabled
375 	if (fCurrentMap->caps_key == _KeyToKeyCode(MENU_ITEM_DISABLED))
376 		fCapsLockMenu->ItemAt(MENU_ITEM_DISABLED)->SetMarked(true);
377 }
378 
379 
380 const char*
381 ModifierKeysWindow::_KeyToString(int32 key)
382 {
383 	switch (key) {
384 		case MENU_ITEM_CAPS_LOCK:
385 			return "Caps Lock";
386 		case MENU_ITEM_CONTROL:
387 			return "Control";
388 		case MENU_ITEM_OPTION:
389 			return "Option";
390 		case MENU_ITEM_COMMAND:
391 			return "Command";
392 		case MENU_ITEM_DISABLED:
393 			return "Disabled";
394 	}
395 
396 	return "";
397 }
398 
399 
400 uint32
401 ModifierKeysWindow::_KeyToKeyCode(int32 key, bool right)
402 {
403 	switch (key) {
404 		case MENU_ITEM_CAPS_LOCK:
405 			return 0x3b;
406 		case MENU_ITEM_CONTROL:
407 			if (right)
408 				return 0x60;
409 			return 0x5c;
410 		case MENU_ITEM_OPTION:
411 			if (right)
412 				return 0x67;
413 			return 0x66;
414 		case MENU_ITEM_COMMAND:
415 			if (right)
416 				return 0x5f;
417 			return 0x5d;
418 		case MENU_ITEM_DISABLED:
419 			return 0;
420 	}
421 
422 	return 0;
423 }
424