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