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