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