xref: /haiku/src/preferences/notifications/GeneralView.cpp (revision 437e3c1c068fa84621711d4977eefe427ec0f2c6)
1 /*
2  * Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
3  * Copyright 2009, Pier Luigi Fiorini.
4  * Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
8  *		Brian Hill, supernova@tycho.email
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include <vector>
15 
16 #include <Alert.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <Directory.h>
21 #include <File.h>
22 #include <FindDirectory.h>
23 #include <Font.h>
24 #include <LayoutBuilder.h>
25 #include <Node.h>
26 #include <Path.h>
27 #include <Query.h>
28 #include <Roster.h>
29 #include <String.h>
30 #include <StringFormat.h>
31 #include <SymLink.h>
32 #include <Volume.h>
33 #include <VolumeRoster.h>
34 
35 #include <notification/Notifications.h>
36 
37 #include "GeneralView.h"
38 #include "NotificationsConstants.h"
39 #include "SettingsHost.h"
40 
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "GeneralView"
43 
44 const uint32 kToggleNotifications = '_TSR';
45 const uint32 kWidthChanged = '_WIC';
46 const uint32 kTimeoutChanged = '_TIC';
47 const uint32 kPositionChanged = '_NPC';
48 const uint32 kServerChangeTriggered = '_SCT';
49 const BString kSampleMessageID("NotificationsSample");
50 
51 
52 static int32
notification_position_to_index(uint32 notification_position)53 notification_position_to_index(uint32 notification_position) {
54 	if (notification_position == B_FOLLOW_NONE)
55 		return 0;
56 	else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM))
57 		return 1;
58 	else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_BOTTOM))
59 		return 2;
60 	else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_TOP))
61 		return 3;
62 	else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_TOP))
63 		return 4;
64 	return 0;
65 }
66 
67 
GeneralView(SettingsHost * host)68 GeneralView::GeneralView(SettingsHost* host)
69 	:
70 	SettingsPane("general", host)
71 {
72 	// Notification server
73 	fNotificationBox = new BCheckBox("server",
74 		B_TRANSLATE("Enable notifications"),
75 		new BMessage(kToggleNotifications));
76 	BBox* box = new BBox("box");
77 	box->SetLabel(fNotificationBox);
78 
79 	// Window width
80 	float ratio = be_plain_font->Size() / 12.f;
81 	int32 minWidth = int32(kMinimumWidth / kWidthStep * ratio);
82 	int32 maxWidth = int32(kMaximumWidth / kWidthStep * ratio);
83 	fWidthSlider = new BSlider("width", B_TRANSLATE("Window width"),
84 		new BMessage(kWidthChanged), minWidth, maxWidth, B_HORIZONTAL);
85 	fWidthSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
86 	fWidthSlider->SetHashMarkCount(maxWidth - minWidth + 1);
87 	fWidthSlider->SetLimitLabels(
88 		B_TRANSLATE_COMMENT("narrow", "Window width: Slider low text"),
89 		B_TRANSLATE_COMMENT("wide", "Window width: Slider high text"));
90 
91 	// Display time
92 	fDurationSlider = new BSlider("duration", B_TRANSLATE("Duration:"),
93 		new BMessage(kTimeoutChanged), kMinimumTimeout, kMaximumTimeout,
94 		B_HORIZONTAL);
95 	fDurationSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
96 	fDurationSlider->SetHashMarkCount(kMaximumTimeout - kMinimumTimeout + 1);
97 	BString minLabel;
98 	minLabel << kMinimumTimeout;
99 	BString maxLabel;
100 	maxLabel << kMaximumTimeout;
101 	fDurationSlider->SetLimitLabels(
102 		B_TRANSLATE_COMMENT(minLabel.String(), "Slider low text"),
103 		B_TRANSLATE_COMMENT(maxLabel.String(), "Slider high text"));
104 
105 	// Notification Position
106 	fPositionMenu = new BPopUpMenu(B_TRANSLATE("Follow Deskbar"));
107 	const char* positionLabels[] = {
108 		B_TRANSLATE_MARK("Follow Deskbar"),
109 		B_TRANSLATE_MARK("Lower right"),
110 		B_TRANSLATE_MARK("Lower left"),
111 		B_TRANSLATE_MARK("Upper right"),
112 		B_TRANSLATE_MARK("Upper left")
113 	};
114 	const uint32 positions[] = {
115 		B_FOLLOW_DESKBAR,                   // Follow Deskbar
116 		B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT,   // Lower right
117 		B_FOLLOW_BOTTOM | B_FOLLOW_LEFT,    // Lower left
118 		B_FOLLOW_TOP    | B_FOLLOW_RIGHT,   // Upper right
119 		B_FOLLOW_TOP    | B_FOLLOW_LEFT     // Upper left
120 	};
121 	for (int i=0; i < 5; i++) {
122 		BMessage* message = new BMessage(kPositionChanged);
123 		message->AddInt32(kNotificationPositionName, positions[i]);
124 
125 		fPositionMenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(
126 			positionLabels[i]), message));
127 	}
128 	BMenuField* positionField = new BMenuField(B_TRANSLATE("Position:"),
129 		fPositionMenu);
130 
131 	box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
132 		.SetInsets(B_USE_DEFAULT_SPACING)
133 		.Add(fWidthSlider)
134 		.Add(fDurationSlider)
135 		.Add(positionField)
136 		.AddGlue()
137 		.View());
138 
139 	BLayoutBuilder::Group<>(this, B_VERTICAL)
140 		.SetInsets(B_USE_WINDOW_SPACING)
141 		.Add(box)
142 	.End();
143 }
144 
145 
146 void
AttachedToWindow()147 GeneralView::AttachedToWindow()
148 {
149 	BView::AttachedToWindow();
150 	fNotificationBox->SetTarget(this);
151 	fWidthSlider->SetTarget(this);
152 	fDurationSlider->SetTarget(this);
153 	fPositionMenu->SetTargetForItems(this);
154 }
155 
156 
157 void
MessageReceived(BMessage * msg)158 GeneralView::MessageReceived(BMessage* msg)
159 {
160 	switch (msg->what) {
161 		case kToggleNotifications:
162 		{
163 			SettingsPane::SettingsChanged(false);
164 			_EnableControls();
165 			break;
166 		}
167 		case kWidthChanged: {
168 			SettingsPane::SettingsChanged(true);
169 			break;
170 		}
171 		case kTimeoutChanged:
172 		{
173 			int32 value = fDurationSlider->Value();
174 			_SetTimeoutLabel(value);
175 			SettingsPane::SettingsChanged(true);
176 			break;
177 		}
178 		case kPositionChanged:
179 		{
180 			int32 position;
181 			if (msg->FindInt32(kNotificationPositionName, &position) == B_OK) {
182 				fNewPosition = position;
183 				SettingsPane::SettingsChanged(true);
184 			}
185 			break;
186 		}
187 		default:
188 			BView::MessageReceived(msg);
189 			break;
190 	}
191 }
192 
193 
194 status_t
Load(BMessage & settings)195 GeneralView::Load(BMessage& settings)
196 {
197 	bool autoStart = settings.GetBool(kAutoStartName, true);
198 	fNotificationBox->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
199 
200 	if (settings.FindFloat(kWidthName, &fOriginalWidth) != B_OK
201 		|| fOriginalWidth > kMaximumWidth
202 		|| fOriginalWidth < kMinimumWidth)
203 		fOriginalWidth = kDefaultWidth;
204 
205 	if (settings.FindInt32(kTimeoutName, &fOriginalTimeout) != B_OK
206 		|| fOriginalTimeout > kMaximumTimeout
207 		|| fOriginalTimeout < kMinimumTimeout)
208 		fOriginalTimeout = kDefaultTimeout;
209 // TODO need to save again if values outside of expected range
210 	int32 setting;
211 	if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
212 		fOriginalIconSize = kDefaultIconSize;
213 	else
214 		fOriginalIconSize = (icon_size)setting;
215 
216 	int32 position;
217 	if (settings.FindInt32(kNotificationPositionName, &position) != B_OK)
218 		fOriginalPosition = kDefaultNotificationPosition;
219 	else
220 		fOriginalPosition = position;
221 
222 	_EnableControls();
223 
224 	return Revert();
225 }
226 
227 
228 status_t
Save(BMessage & settings)229 GeneralView::Save(BMessage& settings)
230 {
231 	bool autoStart = (fNotificationBox->Value() == B_CONTROL_ON);
232 	settings.AddBool(kAutoStartName, autoStart);
233 
234 	int32 timeout = fDurationSlider->Value();
235 	settings.AddInt32(kTimeoutName, timeout);
236 
237 	float width = fWidthSlider->Value() * kWidthStep;
238 	settings.AddFloat(kWidthName, width);
239 
240 	icon_size iconSize = B_LARGE_ICON;
241 	settings.AddInt32(kIconSizeName, (int32)iconSize);
242 
243 	settings.AddInt32(kNotificationPositionName, (int32)fNewPosition);
244 
245 	return B_OK;
246 }
247 
248 
249 status_t
Revert()250 GeneralView::Revert()
251 {
252 	fDurationSlider->SetValue(fOriginalTimeout);
253 	_SetTimeoutLabel(fOriginalTimeout);
254 
255 	fWidthSlider->SetValue(fOriginalWidth / kWidthStep);
256 
257 	fNewPosition = fOriginalPosition;
258 	BMenuItem* item = fPositionMenu->ItemAt(
259 		notification_position_to_index(fNewPosition));
260 	if (item != NULL)
261 		item->SetMarked(true);
262 
263 	return B_OK;
264 }
265 
266 
267 bool
RevertPossible()268 GeneralView::RevertPossible()
269 {
270 	int32 timeout = fDurationSlider->Value();
271 	if (fOriginalTimeout != timeout)
272 		return true;
273 
274 	int32 width = fWidthSlider->Value() * kWidthStep;
275 	if (fOriginalWidth != width)
276 		return true;
277 
278 	if (fOriginalPosition != fNewPosition)
279 		return true;
280 
281 	return false;
282 }
283 
284 
285 status_t
Defaults()286 GeneralView::Defaults()
287 {
288 	fDurationSlider->SetValue(kDefaultTimeout);
289 	_SetTimeoutLabel(kDefaultTimeout);
290 
291 	fWidthSlider->SetValue(kDefaultWidth / kWidthStep);
292 
293 	fNewPosition = kDefaultNotificationPosition;
294 	BMenuItem* item = fPositionMenu->ItemAt(
295 		notification_position_to_index(fNewPosition));
296 	if (item != NULL)
297 		item->SetMarked(true);
298 
299 	return B_OK;
300 }
301 
302 
303 bool
DefaultsPossible()304 GeneralView::DefaultsPossible()
305 {
306 	int32 timeout = fDurationSlider->Value();
307 	if (kDefaultTimeout != timeout)
308 		return true;
309 
310 	int32 width = fWidthSlider->Value() * kWidthStep;
311 	if (kDefaultWidth != width)
312 		return true;
313 
314 	if (kDefaultNotificationPosition != fNewPosition)
315 		return true;
316 
317 	return false;
318 }
319 
320 
321 bool
UseDefaultRevertButtons()322 GeneralView::UseDefaultRevertButtons()
323 {
324 	return true;
325 }
326 
327 
328 void
_EnableControls()329 GeneralView::_EnableControls()
330 {
331 	bool enabled = fNotificationBox->Value() == B_CONTROL_ON;
332 	fWidthSlider->SetEnabled(enabled);
333 	fDurationSlider->SetEnabled(enabled);
334 	BMenuItem* item = fPositionMenu->ItemAt(
335 		notification_position_to_index(fOriginalPosition));
336 	if (item != NULL)
337 		item->SetMarked(true);
338 }
339 
340 
341 void
_SetTimeoutLabel(int32 value)342 GeneralView::_SetTimeoutLabel(int32 value)
343 {
344 	static BStringFormat format(B_TRANSLATE("{0, plural, "
345 		"=1{Timeout: # second}"
346 		"other{Timeout: # seconds}}"));
347 	BString label;
348 	format.Format(label, value);
349 	fDurationSlider->SetLabel(label.String());
350 }
351 
352 
353 bool
_IsServerRunning()354 GeneralView::_IsServerRunning()
355 {
356 	return be_roster->IsRunning(kNotificationServerSignature);
357 }
358