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