xref: /haiku/src/preferences/notifications/GeneralView.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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_TRANSLATE_CONTEXT
41 #define B_TRANSLATE_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 
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 					(void)alert->Go();
140 					return;
141 				}
142 
143 				if (_IsServerRunning()) {
144 					// Server team
145 					team_id team = be_roster->TeamFor(kNotificationServerSignature);
146 
147 					// Establish a connection to infopopper_server
148 					status_t ret = B_ERROR;
149 					BMessenger messenger(kNotificationServerSignature, team, &ret);
150 					if (ret != B_OK) {
151 						BAlert* alert = new BAlert(B_TRANSLATE(
152 							"Notifications"), B_TRANSLATE("Notifications "
153 							"cannot be stopped, because the server can't be"
154 							" reached."), B_TRANSLATE("OK"), NULL, NULL,
155 							B_WIDTH_AS_USUAL, B_STOP_ALERT);
156 						(void)alert->Go();
157 						return;
158 					}
159 
160 					// Send quit message
161 					if (messenger.SendMessage(new BMessage(B_QUIT_REQUESTED)) != B_OK) {
162 						BAlert* alert = new BAlert(B_TRANSLATE(
163 							"Notifications"), B_TRANSLATE("Cannot disable"
164 							" notifications because the server can't be "
165 							"reached."), B_TRANSLATE("OK"),
166 							NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
167 						(void)alert->Go();
168 						return;
169 					}
170 
171 					fServerButton->SetLabel(kStartServer);
172 					fStatusLabel->SetText(kStopped);
173 				} else {
174 					// Start server
175 					status_t err = be_roster->Launch(kNotificationServerSignature);
176 					if (err != B_OK) {
177 						BAlert* alert = new BAlert(B_TRANSLATE(
178 							"Notifications"), B_TRANSLATE("Cannot enable"
179 							" notifications because the server cannot be "
180 							"found.\nThis means your InfoPopper installation"
181 							" was not successfully completed."),
182 							B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
183 							B_STOP_ALERT);
184 						(void)alert->Go();
185 						return;
186 					}
187 
188 					fServerButton->SetLabel(kStopServer);
189 					fStatusLabel->SetText(kStarted);
190 				}
191 			} break;
192 		case kSettingChanged:
193 			SettingsPane::MessageReceived(msg);
194 			break;
195 		default:
196 			BView::MessageReceived(msg);
197 			break;
198 	}
199 }
200 
201 
202 status_t
203 GeneralView::Load()
204 {
205 	BPath path;
206 
207 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
208 		return B_ERROR;
209 
210 	path.Append(kSettingsDirectory);
211 
212 	if (create_directory(path.Path(), 0755) != B_OK) {
213 		BAlert* alert = new BAlert("",
214 			B_TRANSLATE("There was a problem saving the preferences.\n"
215 				"It's possible you don't have write access to the "
216 				"settings directory."), B_TRANSLATE("OK"), NULL, NULL,
217 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
218 		(void)alert->Go();
219 	}
220 
221 	path.Append(kGeneralSettings);
222 
223 	BMessage settings;
224 	BFile file(path.Path(), B_READ_ONLY);
225 	settings.Unflatten(&file);
226 
227 	char buffer[255];
228 
229 	bool autoStart;
230 	if (settings.FindBool(kAutoStartName, &autoStart) != B_OK)
231 		autoStart = kDefaultAutoStart;
232 	fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
233 
234 	int32 timeout;
235 	if (settings.FindInt32(kTimeoutName, &timeout) != B_OK)
236 		timeout = kDefaultTimeout;
237 	(void)sprintf(buffer, "%ld", timeout);
238 	fTimeout->SetText(buffer);
239 
240 	return B_OK;
241 }
242 
243 
244 status_t
245 GeneralView::Save()
246 {
247 	BPath path;
248 
249 	status_t ret = B_OK;
250 	ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
251 	if (ret != B_OK)
252 		return ret;
253 
254 	path.Append(kSettingsDirectory);
255 	path.Append(kGeneralSettings);
256 
257 	BMessage settings;
258 
259 	bool autoStart = (fAutoStart->Value() == B_CONTROL_ON);
260 	settings.AddBool(kAutoStartName, autoStart);
261 
262 	int32 timeout = atol(fTimeout->Text());
263 	settings.AddInt32(kTimeoutName, timeout);
264 
265 	// Save settings file
266 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
267 	ret = settings.Flatten(&file);
268 	if (ret != B_OK) {
269 		BAlert* alert = new BAlert("",
270 			B_TRANSLATE("An error occurred saving the preferences.\n"
271 				"It's possible you are running out of disk space."),
272 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
273 		(void)alert->Go();
274 		return ret;
275 	}
276 
277 	// Find server path
278 	entry_ref ref;
279 	if (!_CanFindServer(&ref)) {
280 		BAlert* alert = new BAlert("",
281 			B_TRANSLATE("The notifications server cannot be found.\n"
282 			   "A possible cause is an installation not done correctly"),
283 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
284 		(void)alert->Go();
285 		return B_ERROR;
286 	}
287 
288 	// UserBootscript command
289 	BPath serverPath(&ref);
290 
291 	// Start server at boot time
292 	ret = find_directory(B_USER_BOOT_DIRECTORY, &path, true);
293 	if (ret != B_OK) {
294 		BAlert* alert = new BAlert("",
295 			B_TRANSLATE("Can't save preferences, you probably don't have "
296 			"write access to the boot settings directory."), B_TRANSLATE("OK"),
297 			NULL, NULL,
298 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
299 		(void)alert->Go();
300 		return ret;
301 	}
302 
303 	path.Append("launch");
304 	BDirectory directory(path.Path());
305 	BEntry entry(&directory, serverPath.Leaf());
306 
307 	// Remove symbolic link
308 	entry.Remove();
309 
310 	if (autoStart) {
311 		// Put a symlink into ~/config/boot/launch
312 		if ((ret = directory.CreateSymLink(serverPath.Leaf(),
313 			serverPath.Path(), NULL) != B_OK)) {
314 			BAlert* alert = new BAlert("",
315 				B_TRANSLATE("Can't enable notifications at startup time, "
316 				"you probably don't have write permission to the boot settings"
317 				" directory."), B_TRANSLATE("OK"), NULL, NULL,
318 				B_WIDTH_AS_USUAL, B_STOP_ALERT);
319 			(void)alert->Go();
320 			return ret;
321 		}
322 	}
323 
324 	return B_OK;
325 }
326 
327 
328 status_t
329 GeneralView::Revert()
330 {
331 	return B_ERROR;
332 }
333 
334 
335 bool
336 GeneralView::_CanFindServer(entry_ref* ref)
337 {
338 	// Try searching with be_roster
339 	if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK)
340 		return true;
341 
342 	// Try with a query and take the first result
343 	BVolumeRoster vroster;
344 	BVolume volume;
345 	char volName[B_FILE_NAME_LENGTH];
346 
347 	vroster.Rewind();
348 
349 	while (vroster.GetNextVolume(&volume) == B_OK) {
350 		if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery())
351 			continue;
352 
353 		volume.GetName(volName);
354 
355 		BQuery *query = new BQuery();
356 		query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")");
357 		query->SetVolume(&volume);
358 		query->Fetch();
359 
360 		if (query->GetNextRef(ref) == B_OK)
361 			return true;
362 	}
363 
364 	return false;
365 }
366 
367 
368 bool
369 GeneralView::_IsServerRunning()
370 {
371 	return be_roster->IsRunning(kNotificationServerSignature);
372 }
373