xref: /haiku/src/apps/deskbar/PreferencesWindow.cpp (revision d4eea7d43cf24aa5174f40958575c5ce439a84ec)
1 /*
2  * Copyright 2009-2011 Haiku, Inc.
3  * All Rights Reserved. Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		John Scipione, jscipione@gmail.com
7  *		Jonas Sundström, jonas@kirilla.com
8  */
9 
10 
11 #include "PreferencesWindow.h"
12 
13 #include <ctype.h>
14 
15 #include <Box.h>
16 #include <Button.h>
17 #include <Catalog.h>
18 #include <CheckBox.h>
19 #include <ControlLook.h>
20 #include <File.h>
21 #include <FormattingConventions.h>
22 #include <GroupLayout.h>
23 #include <ListView.h>
24 #include <Locale.h>
25 #include <LayoutBuilder.h>
26 #include <OpenWithTracker.h>
27 #include <Path.h>
28 #include <RadioButton.h>
29 #include <Roster.h>
30 #include <SeparatorView.h>
31 #include <Screen.h>
32 #include <Slider.h>
33 #include <SpaceLayoutItem.h>
34 #include <TextControl.h>
35 #include <View.h>
36 
37 #include "BarApp.h"
38 #include "StatusView.h"
39 
40 
41 static const float kIndentSpacing
42 	= be_control_look->DefaultItemSpacing() * 2.3;
43 static const uint32 kSettingsViewChanged = 'Svch';
44 static const char* kSettingsFileName = "Deskbar_prefs_window_settings";
45 
46 
47 #undef B_TRANSLATION_CONTEXT
48 #define B_TRANSLATION_CONTEXT "PreferencesWindow"
49 
50 
51 PreferencesWindow::PreferencesWindow(BRect frame)
52 	:
53 	BWindow(frame, B_TRANSLATE("Deskbar preferences"), B_TITLED_WINDOW,
54 		B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
55 {
56 	// Initial settings (used by revert button)
57 	memcpy(&fSettings, static_cast<TBarApp*>(be_app)->Settings(),
58 		sizeof(desk_settings));
59 
60 	// Menu controls
61 	fMenuRecentDocuments = new BCheckBox(B_TRANSLATE("Recent documents:"),
62 		new BMessage(kUpdateRecentCounts));
63 	fMenuRecentApplications = new BCheckBox(B_TRANSLATE("Recent applications:"),
64 		new BMessage(kUpdateRecentCounts));
65 	fMenuRecentFolders = new BCheckBox(B_TRANSLATE("Recent folders:"),
66 		new BMessage(kUpdateRecentCounts));
67 
68 	fMenuRecentDocumentCount = new BTextControl(NULL, NULL,
69 		new BMessage(kUpdateRecentCounts));
70 	fMenuRecentApplicationCount = new BTextControl(NULL, NULL,
71 		new BMessage(kUpdateRecentCounts));
72 	fMenuRecentFolderCount = new BTextControl(NULL, NULL,
73 		new BMessage(kUpdateRecentCounts));
74 
75 	// Applications controls
76 	fAppsSort = new BCheckBox(B_TRANSLATE("Sort applications by name"),
77 		new BMessage(kSortRunningApps));
78 	fAppsSortTrackerFirst = new BCheckBox(B_TRANSLATE("Tracker always first"),
79 		new BMessage(kTrackerFirst));
80 	fAppsShowExpanders = new BCheckBox(B_TRANSLATE("Show application expander"),
81 		new BMessage(kSuperExpando));
82 	fAppsExpandNew = new BCheckBox(B_TRANSLATE("Expand new applications"),
83 		new BMessage(kExpandNewTeams));
84 	fAppsHideLabels = new BCheckBox(B_TRANSLATE("Hide application names"),
85 		new BMessage(kHideLabels));
86 	fAppsIconSizeSlider = new BSlider("icon_size", B_TRANSLATE("Icon size"),
87 		new BMessage(kResizeTeamIcons), kMinimumIconSize / kIconSizeInterval,
88 		kMaximumIconSize / kIconSizeInterval, B_HORIZONTAL);
89 	fAppsIconSizeSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
90 	fAppsIconSizeSlider->SetHashMarkCount((kMaximumIconSize - kMinimumIconSize)
91 		/ kIconSizeInterval + 1);
92 	fAppsIconSizeSlider->SetLimitLabels(B_TRANSLATE("Small"),
93 		B_TRANSLATE("Large"));
94 	fAppsIconSizeSlider->SetModificationMessage(new BMessage(kResizeTeamIcons));
95 
96 	// Window controls
97 	fWindowAlwaysOnTop = new BCheckBox(B_TRANSLATE("Always on top"),
98 		new BMessage(kAlwaysTop));
99 	fWindowAutoRaise = new BCheckBox(B_TRANSLATE("Auto-raise"),
100 		new BMessage(kAutoRaise));
101 	fWindowAutoHide = new BCheckBox(B_TRANSLATE("Auto-hide"),
102 		new BMessage(kAutoHide));
103 
104 	// Menu settings
105 	BTextView* docTextView = fMenuRecentDocumentCount->TextView();
106 	BTextView* appTextView = fMenuRecentApplicationCount->TextView();
107 	BTextView* folderTextView = fMenuRecentFolderCount->TextView();
108 
109 	for (int32 i = 0; i < 256; i++) {
110 		if (!isdigit(i)) {
111 			docTextView->DisallowChar(i);
112 			appTextView->DisallowChar(i);
113 			folderTextView->DisallowChar(i);
114 		}
115 	}
116 
117 	docTextView->SetMaxBytes(4);
118 	appTextView->SetMaxBytes(4);
119 	folderTextView->SetMaxBytes(4);
120 
121 	int32 docCount = fSettings.recentDocsCount;
122 	int32 appCount = fSettings.recentAppsCount;
123 	int32 folderCount = fSettings.recentFoldersCount;
124 
125 	fMenuRecentDocuments->SetValue(fSettings.recentDocsEnabled);
126 	fMenuRecentDocumentCount->SetEnabled(fSettings.recentDocsEnabled);
127 
128 	fMenuRecentApplications->SetValue(fSettings.recentAppsEnabled);
129 	fMenuRecentApplicationCount->SetEnabled(fSettings.recentAppsEnabled);
130 
131 	fMenuRecentFolders->SetValue(fSettings.recentFoldersEnabled);
132 	fMenuRecentFolderCount->SetEnabled(fSettings.recentFoldersEnabled);
133 
134 	BString docString;
135 	BString appString;
136 	BString folderString;
137 
138 	docString << docCount;
139 	appString << appCount;
140 	folderString << folderCount;
141 
142 	fMenuRecentDocumentCount->SetText(docString.String());
143 	fMenuRecentApplicationCount->SetText(appString.String());
144 	fMenuRecentFolderCount->SetText(folderString.String());
145 
146 	// Applications settings
147 	fAppsSort->SetValue(fSettings.sortRunningApps);
148 	fAppsSortTrackerFirst->SetValue(fSettings.trackerAlwaysFirst);
149 	fAppsShowExpanders->SetValue(fSettings.superExpando);
150 	fAppsExpandNew->SetValue(fSettings.expandNewTeams);
151 	fAppsHideLabels->SetValue(fSettings.hideLabels);
152 	fAppsIconSizeSlider->SetValue(fSettings.iconSize
153 		/ kIconSizeInterval);
154 
155 	// Window settings
156 	fWindowAlwaysOnTop->SetValue(fSettings.alwaysOnTop);
157 	fWindowAutoRaise->SetValue(fSettings.autoRaise);
158 	fWindowAutoHide->SetValue(fSettings.autoHide);
159 
160 	_EnableDisableDependentItems();
161 
162 	// Targets
163 	fAppsSort->SetTarget(be_app);
164 	fAppsSortTrackerFirst->SetTarget(be_app);
165 	fAppsShowExpanders->SetTarget(be_app);
166 	fAppsExpandNew->SetTarget(be_app);
167 	fAppsHideLabels->SetTarget(be_app);
168 	fAppsIconSizeSlider->SetTarget(be_app);
169 
170 	fWindowAlwaysOnTop->SetTarget(be_app);
171 	fWindowAutoRaise->SetTarget(be_app);
172 	fWindowAutoHide->SetTarget(be_app);
173 
174 	// Applications
175 	BBox* appsSettingsBox = new BBox("applications");
176 	appsSettingsBox->SetLabel(B_TRANSLATE("Applications"));
177 	appsSettingsBox->AddChild(BLayoutBuilder::Group<>()
178 		.AddGroup(B_VERTICAL, 0)
179 			.Add(fAppsSort)
180 			.Add(fAppsSortTrackerFirst)
181 			.Add(fAppsShowExpanders)
182 			.AddGroup(B_HORIZONTAL, 0)
183 				.Add(BSpaceLayoutItem::CreateHorizontalStrut(kIndentSpacing))
184 				.Add(fAppsExpandNew)
185 				.End()
186 			.Add(fAppsHideLabels)
187 			.AddGlue()
188 			.Add(BSpaceLayoutItem::CreateVerticalStrut(B_USE_SMALL_SPACING))
189 			.Add(fAppsIconSizeSlider)
190 			.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
191 				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
192 			.End()
193 		.View());
194 
195 	// Menu
196 	BBox* menuBox = new BBox("menu");
197 	menuBox->SetLabel(B_TRANSLATE("Menu"));
198 	menuBox->AddChild(BLayoutBuilder::Group<>()
199 		.AddGroup(B_VERTICAL, 0)
200 			.AddGroup(B_HORIZONTAL, 0)
201 				.AddGroup(B_VERTICAL, 0)
202 					.Add(fMenuRecentDocuments)
203 					.Add(fMenuRecentFolders)
204 					.Add(fMenuRecentApplications)
205 					.End()
206 				.AddGroup(B_VERTICAL, 0)
207 					.Add(fMenuRecentDocumentCount)
208 					.Add(fMenuRecentFolderCount)
209 					.Add(fMenuRecentApplicationCount)
210 					.End()
211 				.End()
212 				.Add(BSpaceLayoutItem::CreateVerticalStrut(
213 					B_USE_SMALL_SPACING))
214 				.Add(new BButton(B_TRANSLATE("Edit in Tracker"
215 					B_UTF8_ELLIPSIS), new BMessage(kEditInTracker)))
216 			.AddGlue()
217 			.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
218 				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
219 			.End()
220 		.View());
221 
222 	// Window
223 	BBox* windowSettingsBox = new BBox("window");
224 	windowSettingsBox->SetLabel(B_TRANSLATE("Window"));
225 	windowSettingsBox->AddChild(BLayoutBuilder::Group<>()
226 		.AddGroup(B_VERTICAL, 0)
227 			.Add(fWindowAlwaysOnTop)
228 			.Add(fWindowAutoRaise)
229 			.Add(fWindowAutoHide)
230 			.AddGlue()
231 			.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
232 				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
233 			.End()
234 		.View());
235 
236 	// Action Buttons
237 	fDefaultsButton = new BButton(B_TRANSLATE("Defaults"),
238 		new BMessage(kDefaults));
239 	fRevertButton = new BButton(B_TRANSLATE("Revert"),
240 		new BMessage(kRevert));
241 
242 	// Layout
243 	BLayoutBuilder::Group<>(this)
244 		.AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING)
245 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
246 				.Add(appsSettingsBox)
247 				.AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
248 					.Add(menuBox)
249 					.Add(windowSettingsBox)
250 				.End()
251 			.End()
252 			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
253 				.Add(fDefaultsButton)
254 				.Add(fRevertButton)
255 				.AddGlue()
256 				.End()
257 			.SetInsets(B_USE_DEFAULT_SPACING)
258 			.End();
259 
260 	BMessage windowSettings;
261 	BPoint where;
262 	if (_LoadSettings(&windowSettings) == B_OK
263 		&& windowSettings.FindPoint("window_position", &where) == B_OK
264 		&& BScreen(this).Frame().Contains(where)) {
265 		MoveTo(where);
266 	} else
267 		CenterOnScreen();
268 }
269 
270 
271 PreferencesWindow::~PreferencesWindow()
272 {
273 	_UpdateRecentCounts();
274 }
275 
276 
277 void
278 PreferencesWindow::MessageReceived(BMessage* message)
279 {
280 	switch (message->what) {
281 		case kEditInTracker:
282 			OpenWithTracker(B_USER_DESKBAR_DIRECTORY);
283 			break;
284 
285 		case kUpdatePreferences:
286 			_EnableDisableDependentItems();
287 			_UpdateButtons();
288 			break;
289 
290 		case kUpdateRecentCounts:
291 			_UpdateRecentCounts();
292 			_UpdateButtons();
293 			break;
294 
295 		case kStateChanged:
296 			_EnableDisableDependentItems();
297 			break;
298 
299 		case kRevert:
300 			_UpdatePreferences(&fSettings);
301 			break;
302 
303 		case kDefaults:
304 			_UpdatePreferences(
305 				static_cast<TBarApp*>(be_app)->DefaultSettings());
306 			break;
307 
308 		default:
309 			BWindow::MessageReceived(message);
310 			break;
311 	}
312 }
313 
314 
315 bool
316 PreferencesWindow::QuitRequested()
317 {
318 	BMessage windowSettings;
319 	windowSettings.AddPoint("window_position", Frame().LeftTop());
320 	_SaveSettings(&windowSettings);
321 
322 	be_app->PostMessage(kConfigQuit);
323 
324 	return false;
325 }
326 
327 
328 void
329 PreferencesWindow::Show()
330 {
331 	if (IsHidden())
332 		SetWorkspaces(B_CURRENT_WORKSPACE);
333 
334 	_UpdateButtons();
335 
336 	BWindow::Show();
337 }
338 
339 
340 //	#pragma mark - private methods
341 
342 
343 void
344 PreferencesWindow::_EnableDisableDependentItems()
345 {
346 	TBarApp* barApp = static_cast<TBarApp*>(be_app);
347 	if (barApp->BarView()->Vertical()
348 		&& barApp->BarView()->ExpandoState()) {
349 		fAppsShowExpanders->SetEnabled(true);
350 		fAppsExpandNew->SetEnabled(fAppsShowExpanders->Value());
351 	} else {
352 		fAppsShowExpanders->SetEnabled(false);
353 		fAppsExpandNew->SetEnabled(false);
354 	}
355 
356 	fMenuRecentDocumentCount->SetEnabled(
357 		fMenuRecentDocuments->Value() != B_CONTROL_OFF);
358 	fMenuRecentFolderCount->SetEnabled(
359 		fMenuRecentFolders->Value() != B_CONTROL_OFF);
360 	fMenuRecentApplicationCount->SetEnabled(
361 		fMenuRecentApplications->Value() != B_CONTROL_OFF);
362 
363 	fWindowAutoRaise->SetEnabled(
364 		fWindowAlwaysOnTop->Value() == B_CONTROL_OFF);
365 }
366 
367 
368 bool
369 PreferencesWindow::_IsDefaultable()
370 {
371 	desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();
372 	desk_settings* defaults = static_cast<TBarApp*>(be_app)->DefaultSettings();
373 
374 	return defaults->sortRunningApps != settings->sortRunningApps
375 		|| defaults->trackerAlwaysFirst != settings->trackerAlwaysFirst
376 		|| defaults->superExpando != settings->superExpando
377 		|| defaults->expandNewTeams != settings->expandNewTeams
378 		|| defaults->hideLabels != settings->hideLabels
379 		|| defaults->iconSize != settings->iconSize
380 		|| defaults->recentAppsEnabled != settings->recentAppsEnabled
381 		|| defaults->recentDocsEnabled != settings->recentDocsEnabled
382 		|| defaults->recentFoldersEnabled
383 			!= settings->recentFoldersEnabled
384 		|| defaults->recentAppsCount != settings->recentAppsCount
385 		|| defaults->recentDocsCount != settings->recentDocsCount
386 		|| defaults->recentFoldersCount != settings->recentFoldersCount
387 		|| defaults->alwaysOnTop != settings->alwaysOnTop
388 		|| defaults->autoRaise != settings->autoRaise
389 		|| defaults->autoHide != settings->autoHide;
390 }
391 
392 
393 bool
394 PreferencesWindow::_IsRevertable()
395 {
396 	desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();
397 
398 	return fSettings.sortRunningApps != settings->sortRunningApps
399 		|| fSettings.trackerAlwaysFirst != settings->trackerAlwaysFirst
400 		|| fSettings.superExpando != settings->superExpando
401 		|| fSettings.expandNewTeams != settings->expandNewTeams
402 		|| fSettings.hideLabels != settings->hideLabels
403 		|| fSettings.iconSize != settings->iconSize
404 		|| fSettings.recentAppsEnabled != settings->recentAppsEnabled
405 		|| fSettings.recentDocsEnabled != settings->recentDocsEnabled
406 		|| fSettings.recentFoldersEnabled
407 			!= settings->recentFoldersEnabled
408 		|| fSettings.recentAppsCount != settings->recentAppsCount
409 		|| fSettings.recentDocsCount != settings->recentDocsCount
410 		|| fSettings.recentFoldersCount != settings->recentFoldersCount
411 		|| fSettings.alwaysOnTop != settings->alwaysOnTop
412 		|| fSettings.autoRaise != settings->autoRaise
413 		|| fSettings.autoHide != settings->autoHide;
414 }
415 
416 
417 status_t
418 PreferencesWindow::_InitSettingsFile(BFile* file, bool write)
419 {
420 	BPath prefsPath;
421 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath);
422 	if (status != B_OK)
423 		return status;
424 
425 	status = prefsPath.Append(kSettingsFileName);
426 	if (status != B_OK)
427 		return status;
428 
429 	if (write) {
430 		status = file->SetTo(prefsPath.Path(),
431 			B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
432 	} else
433 		status = file->SetTo(prefsPath.Path(), B_READ_ONLY);
434 
435 	return status;
436 }
437 
438 
439 status_t
440 PreferencesWindow::_LoadSettings(BMessage* settings)
441 {
442 	BFile prefsFile;
443 	status_t status = _InitSettingsFile(&prefsFile, false);
444 	if (status != B_OK)
445 		return status;
446 
447 	return settings->Unflatten(&prefsFile);
448 }
449 
450 
451 status_t
452 PreferencesWindow::_SaveSettings(BMessage* settings)
453 {
454 	BFile prefsFile;
455 	status_t status = _InitSettingsFile(&prefsFile, true);
456 	if (status != B_OK)
457 		return status;
458 
459 	return settings->Flatten(&prefsFile);
460 }
461 
462 
463 void
464 PreferencesWindow::_UpdateButtons()
465 {
466 	fDefaultsButton->SetEnabled(_IsDefaultable());
467 	fRevertButton->SetEnabled(_IsRevertable());
468 }
469 
470 
471 void
472 PreferencesWindow::_UpdatePreferences(desk_settings* settings)
473 {
474 	desk_settings* current = static_cast<TBarApp*>(be_app)->Settings();
475 	bool updateRecentCounts = false;
476 
477 	if (current->sortRunningApps != settings->sortRunningApps) {
478 		fAppsSort->SetValue(settings->sortRunningApps);
479 		fAppsSort->Invoke();
480 	}
481 	if (current->trackerAlwaysFirst != settings->trackerAlwaysFirst) {
482 		fAppsSortTrackerFirst->SetValue(settings->trackerAlwaysFirst);
483 		fAppsSortTrackerFirst->Invoke();
484 	}
485 	if (current->superExpando != settings->superExpando) {
486 		fAppsShowExpanders->SetValue(settings->superExpando);
487 		fAppsShowExpanders->Invoke();
488 	}
489 	if (current->expandNewTeams != settings->expandNewTeams) {
490 		fAppsExpandNew->SetValue(settings->expandNewTeams);
491 		fAppsExpandNew->Invoke();
492 	}
493 	if (current->hideLabels != settings->hideLabels) {
494 		fAppsHideLabels->SetValue(settings->hideLabels);
495 		fAppsHideLabels->Invoke();
496 	}
497 	if (current->iconSize != settings->iconSize) {
498 		fAppsIconSizeSlider->SetValue(settings->iconSize / kIconSizeInterval);
499 		fAppsIconSizeSlider->Invoke();
500 	}
501 	if (current->recentDocsEnabled != settings->recentDocsEnabled) {
502 		fMenuRecentDocuments->SetValue(settings->recentDocsEnabled
503 			? B_CONTROL_ON : B_CONTROL_OFF);
504 		updateRecentCounts = true;
505 	}
506 	if (current->recentFoldersEnabled != settings->recentFoldersEnabled) {
507 		fMenuRecentFolders->SetValue(settings->recentFoldersEnabled
508 			? B_CONTROL_ON : B_CONTROL_OFF);
509 		updateRecentCounts = true;
510 	}
511 	if (current->recentAppsEnabled != settings->recentAppsEnabled) {
512 		fMenuRecentApplications->SetValue(settings->recentAppsEnabled
513 			? B_CONTROL_ON : B_CONTROL_OFF);
514 		updateRecentCounts = true;
515 	}
516 	if (current->recentDocsCount != settings->recentDocsCount) {
517 		BString docString;
518 		docString << settings->recentDocsCount;
519 		fMenuRecentDocumentCount->SetText(docString.String());
520 		updateRecentCounts = true;
521 	}
522 	if (current->recentFoldersCount != settings->recentFoldersCount) {
523 		BString folderString;
524 		folderString << settings->recentFoldersCount;
525 		fMenuRecentFolderCount->SetText(folderString.String());
526 		updateRecentCounts = true;
527 	}
528 	if (current->recentAppsCount != settings->recentAppsCount) {
529 		BString appString;
530 		appString << settings->recentAppsCount;
531 		fMenuRecentApplicationCount->SetText(appString.String());
532 		updateRecentCounts = true;
533 	}
534 	if (current->alwaysOnTop != settings->alwaysOnTop) {
535 		fWindowAlwaysOnTop->SetValue(settings->alwaysOnTop);
536 		fWindowAlwaysOnTop->Invoke();
537 	}
538 	if (current->autoRaise != settings->autoRaise) {
539 		fWindowAutoRaise->SetValue(settings->autoRaise);
540 		fWindowAutoRaise->Invoke();
541 	}
542 	if (current->autoHide != settings->autoHide) {
543 		fWindowAutoHide->SetValue(settings->autoHide);
544 		fWindowAutoHide->Invoke();
545 	}
546 
547 	if (updateRecentCounts)
548 		_UpdateRecentCounts();
549 }
550 
551 
552 void
553 PreferencesWindow::_UpdateRecentCounts()
554 {
555 	BMessage message(kUpdateRecentCounts);
556 
557 	int32 docCount = atoi(fMenuRecentDocumentCount->Text());
558 	int32 appCount = atoi(fMenuRecentApplicationCount->Text());
559 	int32 folderCount = atoi(fMenuRecentFolderCount->Text());
560 
561 	message.AddInt32("documents", max_c(0, docCount));
562 	message.AddInt32("applications", max_c(0, appCount));
563 	message.AddInt32("folders", max_c(0, folderCount));
564 
565 	message.AddBool("documentsEnabled", fMenuRecentDocuments->Value());
566 	message.AddBool("applicationsEnabled", fMenuRecentApplications->Value());
567 	message.AddBool("foldersEnabled", fMenuRecentFolders->Value());
568 
569 	be_app->PostMessage(&message);
570 
571 	_EnableDisableDependentItems();
572 }
573