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