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