xref: /haiku/src/preferences/notifications/GeneralView.cpp (revision 991dadd6324f7b7a68e94743a39ebae789823228)
1 /*
2  * Copyright 2010-2013, 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  */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #include <vector>
14 
15 #include <Roster.h>
16 #include <GroupLayout.h>
17 #include <GroupLayoutBuilder.h>
18 #include <Alert.h>
19 #include <Font.h>
20 #include <Button.h>
21 #include <Catalog.h>
22 #include <StringView.h>
23 #include <TextControl.h>
24 #include <CheckBox.h>
25 #include <String.h>
26 #include <FindDirectory.h>
27 #include <Node.h>
28 #include <notification/Notifications.h>
29 #include <Path.h>
30 #include <File.h>
31 #include <Directory.h>
32 #include <VolumeRoster.h>
33 #include <Volume.h>
34 #include <Query.h>
35 #include <SymLink.h>
36 
37 #include "GeneralView.h"
38 #include "SettingsHost.h"
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "GeneralView"
42 const int32 kToggleNotifications = '_TSR';
43 
44 GeneralView::GeneralView(SettingsHost* host)
45 	:
46 	SettingsPane("general", host)
47 {
48 	// Notifications
49 	fNotificationBox = new BCheckBox("server",
50 		B_TRANSLATE("Enable notifications"),
51 		new BMessage(kToggleNotifications));
52 
53 	// Autostart
54 	fAutoStart = new BCheckBox("autostart",
55 		B_TRANSLATE("Enable notifications at startup"),
56 		new BMessage(kSettingChanged));
57 
58 	// Display time
59 	fTimeout = new BTextControl(B_TRANSLATE("Hide notifications from screen"
60 		" after"), NULL, new BMessage(kSettingChanged));
61 	BStringView* displayTimeLabel = new BStringView("dt_label",
62 		B_TRANSLATE("seconds of inactivity"));
63 
64 	// Default position
65 	// TODO: Here will come a screen representation with the four corners
66 	// clickable
67 
68 	// Load settings
69 	Load();
70 
71 	// Calculate inset
72 	float inset = ceilf(be_plain_font->Size() * 0.7f);
73 
74 	SetLayout(new BGroupLayout(B_VERTICAL));
75 	AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
76 		.AddGroup(B_HORIZONTAL, inset)
77 			.Add(fNotificationBox)
78 			.AddGlue()
79 		.End()
80 
81 		.AddGroup(B_VERTICAL, inset)
82 			.Add(fAutoStart)
83 			.AddGroup(B_HORIZONTAL)
84 				.AddGroup(B_HORIZONTAL, 2)
85 					.Add(fTimeout)
86 					.Add(displayTimeLabel)
87 				.End()
88 			.End()
89 		.End()
90 		.SetInsets(inset, inset, inset, inset)
91 		.AddGlue()
92 	);
93 }
94 
95 
96 void
97 GeneralView::AttachedToWindow()
98 {
99 	fNotificationBox->SetTarget(this);
100 	fAutoStart->SetTarget(this);
101 	fTimeout->SetTarget(this);
102 }
103 
104 
105 void
106 GeneralView::MessageReceived(BMessage* msg)
107 {
108 	switch (msg->what) {
109 		case kToggleNotifications:
110 		{
111 			entry_ref ref;
112 
113 			// Check if server is available
114 			if (!_CanFindServer(&ref)) {
115 				BAlert* alert = new BAlert(B_TRANSLATE("Notifications"),
116 					B_TRANSLATE("The notifications server cannot be"
117 					" found, this means your InfoPopper installation was"
118 					" not successfully completed."), B_TRANSLATE("OK"),
119 					NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
120 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
121 				(void)alert->Go();
122 				return;
123 			}
124 
125 			if (fNotificationBox->Value() == B_CONTROL_OFF) {
126 				// Server team
127 				team_id team = be_roster->TeamFor(kNotificationServerSignature);
128 
129 				// Establish a connection to infopopper_server
130 				status_t ret = B_ERROR;
131 				BMessenger messenger(kNotificationServerSignature, team, &ret);
132 				if (ret != B_OK) {
133 					BAlert* alert = new BAlert(B_TRANSLATE(
134 						"Notifications"), B_TRANSLATE("Notifications "
135 						"cannot be stopped, because the server can't be"
136 						" reached."), B_TRANSLATE("OK"), NULL, NULL,
137 						B_WIDTH_AS_USUAL, B_STOP_ALERT);
138 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
139 					(void)alert->Go();
140 					return;
141 				}
142 
143 				// Send quit message
144 				if (messenger.SendMessage(B_QUIT_REQUESTED) != B_OK) {
145 					BAlert* alert = new BAlert(B_TRANSLATE(
146 						"Notifications"), B_TRANSLATE("Cannot disable"
147 						" notifications because the server can't be "
148 						"reached."), B_TRANSLATE("OK"),
149 						NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
150 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
151 					(void)alert->Go();
152 					return;
153 				}
154 			} else if (!_IsServerRunning()) {
155 				// Start server
156 				status_t err = be_roster->Launch(kNotificationServerSignature);
157 				if (err != B_OK) {
158 					BAlert* alert = new BAlert(B_TRANSLATE(
159 						"Notifications"), B_TRANSLATE("Cannot enable"
160 						" notifications because the server cannot be "
161 						"found.\nThis means your InfoPopper installation"
162 						" was not successfully completed."),
163 						B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
164 						B_STOP_ALERT);
165 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
166 					(void)alert->Go();
167 					return;
168 				}
169 			}
170 			break;
171 		}
172 		case kSettingChanged:
173 			SettingsPane::MessageReceived(msg);
174 			break;
175 		default:
176 			BView::MessageReceived(msg);
177 			break;
178 	}
179 }
180 
181 
182 status_t
183 GeneralView::Load()
184 {
185 	BPath path;
186 
187 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
188 		return B_ERROR;
189 
190 	path.Append(kSettingsDirectory);
191 
192 	if (create_directory(path.Path(), 0755) != B_OK) {
193 		BAlert* alert = new BAlert("",
194 			B_TRANSLATE("There was a problem saving the preferences.\n"
195 				"It's possible you don't have write access to the "
196 				"settings directory."), B_TRANSLATE("OK"), NULL, NULL,
197 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
198 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
199 		(void)alert->Go();
200 	}
201 
202 	path.Append(kGeneralSettings);
203 
204 	BMessage settings;
205 	BFile file(path.Path(), B_READ_ONLY);
206 	settings.Unflatten(&file);
207 
208 	char buffer[255];
209 
210 	fNotificationBox->SetValue(_IsServerRunning() ? B_CONTROL_ON : B_CONTROL_OFF);
211 
212 	bool autoStart;
213 	if (settings.FindBool(kAutoStartName, &autoStart) != B_OK)
214 		autoStart = kDefaultAutoStart;
215 	fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
216 
217 	int32 timeout;
218 	if (settings.FindInt32(kTimeoutName, &timeout) != B_OK)
219 		timeout = kDefaultTimeout;
220 	(void)sprintf(buffer, "%" B_PRId32, timeout);
221 	fTimeout->SetText(buffer);
222 
223 	return B_OK;
224 }
225 
226 
227 status_t
228 GeneralView::Save()
229 {
230 	BPath path;
231 
232 	status_t ret = B_OK;
233 	ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
234 	if (ret != B_OK)
235 		return ret;
236 
237 	path.Append(kSettingsDirectory);
238 	path.Append(kGeneralSettings);
239 
240 	BMessage settings;
241 
242 	bool autoStart = (fAutoStart->Value() == B_CONTROL_ON);
243 	settings.AddBool(kAutoStartName, autoStart);
244 
245 	int32 timeout = atol(fTimeout->Text());
246 	settings.AddInt32(kTimeoutName, timeout);
247 
248 	// Save settings file
249 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
250 	ret = settings.Flatten(&file);
251 	if (ret != B_OK) {
252 		BAlert* alert = new BAlert("",
253 			B_TRANSLATE("An error occurred saving the preferences.\n"
254 				"It's possible you are running out of disk space."),
255 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
256 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
257 		(void)alert->Go();
258 		return ret;
259 	}
260 
261 	// Find server path
262 	entry_ref ref;
263 	if (!_CanFindServer(&ref)) {
264 		BAlert* alert = new BAlert("",
265 			B_TRANSLATE("The notifications server cannot be found.\n"
266 			   "A possible cause is an installation not done correctly"),
267 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
268 		(void)alert->Go();
269 		return B_ERROR;
270 	}
271 
272 	// UserBootscript command
273 	BPath serverPath(&ref);
274 
275 	// Start server at boot time
276 	ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
277 	if (ret != B_OK) {
278 		BAlert* alert = new BAlert("",
279 			B_TRANSLATE("Can't save preferences, you probably don't have "
280 			"write access to the settings directory."), B_TRANSLATE("OK"),
281 			NULL, NULL,
282 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
283 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
284 		(void)alert->Go();
285 		return ret;
286 	}
287 
288 	path.Append("boot/launch");
289 	BDirectory directory(path.Path());
290 	BEntry entry(&directory, serverPath.Leaf());
291 
292 	// Remove symbolic link
293 	entry.Remove();
294 
295 	if (autoStart) {
296 		// Put a symlink into ~/config/boot/launch
297 		if ((ret = directory.CreateSymLink(serverPath.Leaf(),
298 			serverPath.Path(), NULL) != B_OK)) {
299 			BAlert* alert = new BAlert("",
300 				B_TRANSLATE("Can't enable notifications at startup time, "
301 				"you probably don't have write permission to the boot settings"
302 				" directory."), B_TRANSLATE("OK"), NULL, NULL,
303 				B_WIDTH_AS_USUAL, B_STOP_ALERT);
304 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
305 			(void)alert->Go();
306 			return ret;
307 		}
308 	}
309 
310 	return B_OK;
311 }
312 
313 
314 status_t
315 GeneralView::Revert()
316 {
317 	return B_ERROR;
318 }
319 
320 
321 bool
322 GeneralView::_CanFindServer(entry_ref* ref)
323 {
324 	// Try searching with be_roster
325 	if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK)
326 		return true;
327 
328 	// Try with a query and take the first result
329 	BVolumeRoster vroster;
330 	BVolume volume;
331 	char volName[B_FILE_NAME_LENGTH];
332 
333 	vroster.Rewind();
334 
335 	while (vroster.GetNextVolume(&volume) == B_OK) {
336 		if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery())
337 			continue;
338 
339 		volume.GetName(volName);
340 
341 		BQuery *query = new BQuery();
342 		query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")");
343 		query->SetVolume(&volume);
344 		query->Fetch();
345 
346 		if (query->GetNextRef(ref) == B_OK)
347 			return true;
348 	}
349 
350 	return false;
351 }
352 
353 
354 bool
355 GeneralView::_IsServerRunning()
356 {
357 	return be_roster->IsRunning(kNotificationServerSignature);
358 }
359