xref: /haiku/src/apps/haikudepot/ui/App.cpp (revision 5e19679ea35a79a26477c6215c7abba9bb7c4d00)
1 /*
2  * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "App.h"
8 
9 #include <stdio.h>
10 
11 #include <Alert.h>
12 #include <Catalog.h>
13 #include <Entry.h>
14 #include <Message.h>
15 #include <package/PackageInfo.h>
16 #include <Path.h>
17 #include <Roster.h>
18 #include <Screen.h>
19 #include <String.h>
20 
21 #include "support.h"
22 
23 #include "FeaturedPackagesView.h"
24 #include "Logger.h"
25 #include "MainWindow.h"
26 #include "ServerSettings.h"
27 #include "ScreenshotWindow.h"
28 
29 
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "App"
32 
33 
34 App::App()
35 	:
36 	BApplication("application/x-vnd.Haiku-HaikuDepot"),
37 	fMainWindow(NULL),
38 	fWindowCount(0),
39 	fSettingsRead(false)
40 {
41 	_CheckPackageDaemonRuns();
42 }
43 
44 
45 App::~App()
46 {
47 	// We cannot let global destructors cleanup static BitmapRef objects,
48 	// since calling BBitmap destructors needs a valid BApplication still
49 	// around. That's why we do it here.
50 	PackageInfo::CleanupDefaultIcon();
51 	FeaturedPackagesView::CleanupIcons();
52 	ScreenshotWindow::CleanupIcons();
53 }
54 
55 
56 bool
57 App::QuitRequested()
58 {
59 	if (fMainWindow != NULL
60 		&& fMainWindow->LockLooperWithTimeout(1500000) == B_OK) {
61 		BMessage windowSettings;
62 		fMainWindow->StoreSettings(windowSettings);
63 
64 		fMainWindow->UnlockLooper();
65 
66 		_StoreSettings(windowSettings);
67 	}
68 
69 	return true;
70 }
71 
72 
73 void
74 App::ReadyToRun()
75 {
76 	if (fWindowCount > 0)
77 		return;
78 
79 	BMessage settings;
80 	_LoadSettings(settings);
81 
82 	fMainWindow = new MainWindow(settings);
83 	_ShowWindow(fMainWindow);
84 }
85 
86 
87 void
88 App::MessageReceived(BMessage* message)
89 {
90 	switch (message->what) {
91 		case MSG_MAIN_WINDOW_CLOSED:
92 		{
93 			BMessage windowSettings;
94 			if (message->FindMessage("window settings",
95 					&windowSettings) == B_OK) {
96 				_StoreSettings(windowSettings);
97 			}
98 
99 			fWindowCount--;
100 			if (fWindowCount == 0)
101 				Quit();
102 			break;
103 		}
104 
105 		default:
106 			BApplication::MessageReceived(message);
107 			break;
108 	}
109 }
110 
111 
112 void
113 App::RefsReceived(BMessage* message)
114 {
115 	entry_ref ref;
116 	int32 index = 0;
117 	while (message->FindRef("refs", index++, &ref) == B_OK) {
118 		BEntry entry(&ref, true);
119 		_Open(entry);
120 	}
121 }
122 
123 
124 enum arg_switch {
125 	UNKNOWN_SWITCH,
126 	NOT_SWITCH,
127 	HELP_SWITCH,
128 	WEB_APP_BASE_URL_SWITCH,
129 	VERBOSITY_SWITCH,
130 };
131 
132 
133 static void
134 app_print_help()
135 {
136 	fprintf(stdout, "HaikuDepot ");
137 	fprintf(stdout, "[-u|--webappbaseurl <web-app-base-url>] ");
138 	fprintf(stdout, "[-v|--verbosity [off|info|debug|trace] ");
139 	fprintf(stdout, "[-h|--help]\n\n");
140 	fprintf(stdout, "'-h' : causes this help text to be printed out.\n");
141 	fprintf(stdout, "'-v' : allows for the verbosity level to be set.\n");
142 	fprintf(stdout, "'-u' : allows for the haiku depot server to be\n");
143 	fprintf(stdout, "   configured.");
144 }
145 
146 
147 static arg_switch
148 app_resolve_switch(char *arg)
149 {
150 	int arglen = strlen(arg);
151 
152 	if (arglen > 0 && arg[0] == '-') {
153 
154 		if (arglen > 3 && arg[1] == '-') { // long form
155 			if (0 == strcmp(&arg[2], "webappbaseurl"))
156 				return WEB_APP_BASE_URL_SWITCH;
157 
158 			if (0 == strcmp(&arg[2], "help"))
159 				return HELP_SWITCH;
160 
161 			if (0 == strcmp(&arg[2], "verbosity"))
162 				return VERBOSITY_SWITCH;
163 		} else {
164 			if (arglen == 2) { // short form
165 				switch (arg[1]) {
166 					case 'u':
167 						return WEB_APP_BASE_URL_SWITCH;
168 
169 					case 'h':
170 						return HELP_SWITCH;
171 
172 					case 'v':
173 						return VERBOSITY_SWITCH;
174 				}
175 			}
176 		}
177 
178 		return UNKNOWN_SWITCH;
179 	}
180 
181 	return NOT_SWITCH;
182 }
183 
184 
185 void
186 App::ArgvReceived(int32 argc, char* argv[])
187 {
188 	for (int i = 1; i < argc;) {
189 
190 			// check to make sure that if there is a value for the switch,
191 			// that the value is in fact supplied.
192 
193 		switch (app_resolve_switch(argv[i])) {
194 			case VERBOSITY_SWITCH:
195 			case WEB_APP_BASE_URL_SWITCH:
196 				if (i == argc-1) {
197 					fprintf(stdout, "unexpected end of arguments; missing "
198 						"value for switch [%s]\n", argv[i]);
199 					Quit();
200 					return;
201 				}
202 				break;
203 
204 			default:
205 				break;
206 		}
207 
208 			// now process each switch.
209 
210 		switch (app_resolve_switch(argv[i])) {
211 
212 			case VERBOSITY_SWITCH:
213 				if (!Logger::SetLevelByName(argv[i+1])) {
214 					fprintf(stdout, "unknown log level [%s]\n", argv[i + 1]);
215 					Quit();
216 				}
217 				i++; // also move past the log level value
218 				break;
219 
220 			case HELP_SWITCH:
221 				app_print_help();
222 				Quit();
223 				break;
224 
225 			case WEB_APP_BASE_URL_SWITCH:
226 				if (ServerSettings::SetBaseUrl(BUrl(argv[i + 1])) != B_OK) {
227 					fprintf(stdout, "malformed web app base url; %s\n",
228 						argv[i + 1]);
229 					Quit();
230 				}
231 				else {
232 					fprintf(stdout, "did configure the web base url; %s\n",
233 						argv[i + 1]);
234 				}
235 
236 				i++; // also move past the url value
237 
238 				break;
239 
240 			case NOT_SWITCH:
241 			{
242 				BEntry entry(argv[i], true);
243 				_Open(entry);
244 				break;
245 			}
246 
247 			case UNKNOWN_SWITCH:
248 				fprintf(stdout, "unknown switch; %s\n", argv[i]);
249 				Quit();
250 				break;
251 		}
252 
253 		i++; // move on at least one arg
254 	}
255 }
256 
257 
258 // #pragma mark - private
259 
260 
261 void
262 App::_Open(const BEntry& entry)
263 {
264 	BPath path;
265 	if (!entry.Exists() || entry.GetPath(&path) != B_OK) {
266 		fprintf(stderr, "Package file not found: %s\n", path.Path());
267 		return;
268 	}
269 
270 	// Try to parse package file via Package Kit
271 	BPackageKit::BPackageInfo info;
272 	status_t status = info.ReadFromPackageFile(path.Path());
273 	if (status != B_OK) {
274 		fprintf(stderr, "Failed to parse package file: %s\n",
275 			strerror(status));
276 		return;
277 	}
278 
279 	// Transfer information into PackageInfo
280 	PackageInfoRef package(new(std::nothrow) PackageInfo(info), true);
281 	if (package.Get() == NULL) {
282 		fprintf(stderr, "Could not allocate PackageInfo\n");
283 		return;
284 	}
285 
286 	package->SetLocalFilePath(path.Path());
287 
288 	BMessage settings;
289 	_LoadSettings(settings);
290 
291 	MainWindow* window = new MainWindow(settings, package);
292 	_ShowWindow(window);
293 }
294 
295 
296 void
297 App::_ShowWindow(MainWindow* window)
298 {
299 	window->Show();
300 	fWindowCount++;
301 }
302 
303 
304 bool
305 App::_LoadSettings(BMessage& settings)
306 {
307 	if (!fSettingsRead) {
308 		fSettings = true;
309 		if (load_settings(&fSettings, "main_settings", "HaikuDepot") != B_OK)
310 			fSettings.MakeEmpty();
311 	}
312 	settings = fSettings;
313 	return !fSettings.IsEmpty();
314 }
315 
316 
317 void
318 App::_StoreSettings(const BMessage& settings)
319 {
320 	// Take what is in settings and replace data under the same name in
321 	// fSettings, leaving anything in fSettings that is not contained in
322 	// settings.
323 	int32 i = 0;
324 
325 	char* name;
326 	type_code type;
327 	int32 count;
328 
329 	while (settings.GetInfo(B_ANY_TYPE, i++, &name, &type, &count) == B_OK) {
330 		fSettings.RemoveName(name);
331 		for (int32 j = 0; j < count; j++) {
332 			const void* data;
333 			ssize_t size;
334 			if (settings.FindData(name, type, j, &data, &size) != B_OK)
335 				break;
336 			fSettings.AddData(name, type, data, size);
337 		}
338 	}
339 
340 	save_settings(&fSettings, "main_settings", "HaikuDepot");
341 }
342 
343 
344 // #pragma mark -
345 
346 
347 static const char* kPackageDaemonSignature
348 	= "application/x-vnd.haiku-package_daemon";
349 
350 void
351 App::_CheckPackageDaemonRuns()
352 {
353 	while (!be_roster->IsRunning(kPackageDaemonSignature)) {
354 		BAlert* alert = new BAlert("start_package_daemon",
355 			B_TRANSLATE("HaikuDepot needs the package daemon to function, "
356 				"and it appears to be not running.\n"
357 				"Would you like to start it now?"),
358 			B_TRANSLATE("No, quit HaikuDepot"),
359 			B_TRANSLATE("Start package daemon"), NULL, B_WIDTH_AS_USUAL,
360 			B_WARNING_ALERT);
361 		alert->SetShortcut(0, B_ESCAPE);
362 
363 		if (alert->Go() == 0)
364 			exit(1);
365 
366 		if (!_LaunchPackageDaemon())
367 			break;
368 	}
369 }
370 
371 
372 bool
373 App::_LaunchPackageDaemon()
374 {
375 	status_t ret = be_roster->Launch(kPackageDaemonSignature);
376 	if (ret != B_OK) {
377 		BString errorMessage
378 			= B_TRANSLATE("Starting the package daemon failed:\n\n%Error%");
379 		errorMessage.ReplaceAll("%Error%", strerror(ret));
380 
381 		BAlert* alert = new BAlert("package_daemon_problem",
382 			errorMessage,
383 			B_TRANSLATE("Quit HaikuDepot"),
384 			B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL,
385 			B_WARNING_ALERT);
386 		alert->SetShortcut(0, B_ESCAPE);
387 
388 		if (alert->Go() == 0)
389 			return false;
390 	}
391 	// TODO: Would be nice to send a message to the package daemon instead
392 	// and get a reply once it is ready.
393 	snooze(2000000);
394 	return true;
395 }
396 
397