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