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