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