1 /* 2 * Copyright 2006, 2011, Stephan Aßmus <superstippi@gmx.de>. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "IconEditorApp.h" 8 9 #include <new> 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <Alert.h> 14 #include <Catalog.h> 15 #include <FilePanel.h> 16 #include <FindDirectory.h> 17 #include <IconEditorProtocol.h> 18 #include <Locale.h> 19 #include <Message.h> 20 #include <Mime.h> 21 #include <Path.h> 22 23 #include "support_settings.h" 24 25 #include "AutoLocker.h" 26 #include "Defines.h" 27 #include "MainWindow.h" 28 #include "SavePanel.h" 29 30 31 #undef B_TRANSLATION_CONTEXT 32 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Main" 33 34 35 using std::nothrow; 36 37 static const char* kAppSig = "application/x-vnd.haiku-icon_o_matic"; 38 39 static const float kWindowOffset = 20; 40 41 42 IconEditorApp::IconEditorApp() 43 : 44 BApplication(kAppSig), 45 fWindowCount(0), 46 fLastWindowFrame(50, 50, 900, 750), 47 48 fOpenPanel(NULL), 49 fSavePanel(NULL), 50 51 fLastOpenPath(""), 52 fLastSavePath(""), 53 fLastExportPath("") 54 { 55 // create file panels 56 BMessenger messenger(this, this); 57 BMessage message(B_REFS_RECEIVED); 58 fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, B_FILE_NODE, 59 true, &message); 60 61 message.what = MSG_SAVE_AS; 62 fSavePanel = new SavePanel("save panel", &messenger, NULL, B_FILE_NODE 63 | B_DIRECTORY_NODE | B_SYMLINK_NODE, false, &message); 64 65 _RestoreSettings(); 66 } 67 68 69 IconEditorApp::~IconEditorApp() 70 { 71 delete fOpenPanel; 72 delete fSavePanel; 73 } 74 75 76 // #pragma mark - 77 78 79 bool 80 IconEditorApp::QuitRequested() 81 { 82 // Run the QuitRequested() hook in each window's own thread. Otherwise 83 // the BAlert which a window shows when an icon is not saved will not 84 // repaint the window. (BAlerts check which thread is running Go() and 85 // will repaint windows when it's a BWindow.) 86 bool quit = true; 87 for (int32 i = 0; BWindow* window = WindowAt(i); i++) { 88 if (!window->Lock()) 89 continue; 90 // Try to cast the window while the pointer must be valid. 91 MainWindow* mainWindow = dynamic_cast<MainWindow*>(window); 92 window->Unlock(); 93 if (mainWindow == NULL) 94 continue; 95 BMessenger messenger(window, window); 96 BMessage reply; 97 if (messenger.SendMessage(B_QUIT_REQUESTED, &reply) != B_OK) 98 continue; 99 bool result; 100 if (reply.FindBool("result", &result) == B_OK && !result) 101 quit = false; 102 } 103 104 if (!quit) 105 return false; 106 107 _StoreSettings(); 108 109 return true; 110 } 111 112 113 void 114 IconEditorApp::MessageReceived(BMessage* message) 115 { 116 switch (message->what) { 117 case MSG_NEW: 118 _NewWindow()->Show(); 119 break; 120 case MSG_OPEN: 121 { 122 BMessage openMessage(B_REFS_RECEIVED); 123 MainWindow* window; 124 if (message->FindPointer("window", (void**)&window) == B_OK) 125 openMessage.AddPointer("window", window); 126 fOpenPanel->SetMessage(&openMessage); 127 fOpenPanel->Show(); 128 break; 129 } 130 case MSG_APPEND: 131 { 132 MainWindow* window; 133 if (message->FindPointer("window", (void**)&window) != B_OK) 134 break; 135 BMessage openMessage(B_REFS_RECEIVED); 136 openMessage.AddBool("append", true); 137 openMessage.AddPointer("window", window); 138 fOpenPanel->SetMessage(&openMessage); 139 fOpenPanel->Show(); 140 break; 141 } 142 case B_EDIT_ICON_DATA: 143 { 144 BMessenger messenger; 145 if (message->FindMessenger("reply to", &messenger) < B_OK) { 146 // required 147 break; 148 } 149 const uint8* data; 150 ssize_t size; 151 if (message->FindData("icon data", B_VECTOR_ICON_TYPE, 152 (const void**)&data, &size) < B_OK) { 153 // optional (new icon will be created) 154 data = NULL; 155 size = 0; 156 } 157 MainWindow* window = _NewWindow(); 158 window->Open(messenger, data, size); 159 window->Show(); 160 break; 161 } 162 case MSG_SAVE_AS: 163 case MSG_EXPORT_AS: 164 { 165 BMessenger messenger; 166 if (message->FindMessenger("target", &messenger) != B_OK) 167 break; 168 169 fSavePanel->SetExportMode(message->what == MSG_EXPORT_AS); 170 // fSavePanel->Refresh(); 171 const char* saveText; 172 if (message->FindString("save text", &saveText) == B_OK) 173 fSavePanel->SetSaveText(saveText); 174 fSavePanel->SetTarget(messenger); 175 fSavePanel->Show(); 176 break; 177 } 178 179 case MSG_WINDOW_CLOSED: 180 { 181 fWindowCount--; 182 if (fWindowCount == 0) 183 PostMessage(B_QUIT_REQUESTED); 184 BMessage settings; 185 if (message->FindMessage("settings", &settings) == B_OK) 186 fLastWindowSettings = settings; 187 BRect frame; 188 if (message->FindRect("window frame", &frame) == B_OK) { 189 fLastWindowFrame = frame; 190 fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset); 191 } 192 break; 193 } 194 195 default: 196 BApplication::MessageReceived(message); 197 break; 198 } 199 } 200 201 202 void 203 IconEditorApp::ReadyToRun() 204 { 205 // create main window 206 if (fWindowCount == 0) 207 _NewWindow()->Show(); 208 209 _InstallDocumentMimeType(); 210 } 211 212 213 void 214 IconEditorApp::RefsReceived(BMessage* message) 215 { 216 bool append; 217 if (message->FindBool("append", &append) != B_OK) 218 append = false; 219 MainWindow* window; 220 if (message->FindPointer("window", (void**)&window) != B_OK) 221 window = NULL; 222 // When appending, we need to know a window. 223 if (append && window == NULL) 224 return; 225 entry_ref ref; 226 if (append) { 227 if (!window->Lock()) 228 return; 229 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) 230 window->Open(ref, true); 231 window->Unlock(); 232 } else { 233 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 234 if (window != NULL && i == 0) { 235 window->Lock(); 236 window->Open(ref, false); 237 window->Unlock(); 238 } else { 239 window = _NewWindow(); 240 window->Open(ref, false); 241 window->Show(); 242 } 243 } 244 } 245 246 if (fOpenPanel != NULL && fSavePanel != NULL) 247 _SyncPanels(fOpenPanel, fSavePanel); 248 } 249 250 251 void 252 IconEditorApp::ArgvReceived(int32 argc, char** argv) 253 { 254 if (argc < 2) 255 return; 256 257 entry_ref ref; 258 259 for (int32 i = 1; i < argc; i++) { 260 if (get_ref_for_path(argv[i], &ref) == B_OK) { 261 MainWindow* window = _NewWindow(); 262 window->Open(ref); 263 window->Show(); 264 } 265 } 266 } 267 268 269 // #pragma mark - 270 271 272 MainWindow* 273 IconEditorApp::_NewWindow() 274 { 275 fLastWindowFrame.OffsetBy(kWindowOffset, kWindowOffset); 276 MainWindow* window = new MainWindow(fLastWindowFrame, this, 277 &fLastWindowSettings); 278 fWindowCount++; 279 return window; 280 } 281 282 283 void 284 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to) 285 { 286 if (from->Window()->Lock()) { 287 // location 288 if (to->Window()->Lock()) { 289 BRect frame = from->Window()->Frame(); 290 to->Window()->MoveTo(frame.left, frame.top); 291 to->Window()->ResizeTo(frame.Width(), frame.Height()); 292 to->Window()->Unlock(); 293 } 294 // current folder 295 entry_ref panelDir; 296 from->GetPanelDirectory(&panelDir); 297 to->SetPanelDirectory(&panelDir); 298 from->Window()->Unlock(); 299 } 300 } 301 302 303 const char* 304 IconEditorApp::_LastFilePath(path_kind which) 305 { 306 const char* path = NULL; 307 308 switch (which) { 309 case LAST_PATH_OPEN: 310 if (fLastOpenPath.Length() > 0) 311 path = fLastOpenPath.String(); 312 else if (fLastSavePath.Length() > 0) 313 path = fLastSavePath.String(); 314 else if (fLastExportPath.Length() > 0) 315 path = fLastExportPath.String(); 316 break; 317 case LAST_PATH_SAVE: 318 if (fLastSavePath.Length() > 0) 319 path = fLastSavePath.String(); 320 else if (fLastExportPath.Length() > 0) 321 path = fLastExportPath.String(); 322 else if (fLastOpenPath.Length() > 0) 323 path = fLastOpenPath.String(); 324 break; 325 case LAST_PATH_EXPORT: 326 if (fLastExportPath.Length() > 0) 327 path = fLastExportPath.String(); 328 else if (fLastSavePath.Length() > 0) 329 path = fLastSavePath.String(); 330 else if (fLastOpenPath.Length() > 0) 331 path = fLastOpenPath.String(); 332 break; 333 } 334 if (path == NULL) { 335 336 BPath homePath; 337 if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK) 338 path = homePath.Path(); 339 else 340 path = "/boot/home"; 341 } 342 343 return path; 344 } 345 346 347 // #pragma mark - 348 349 350 void 351 IconEditorApp::_StoreSettings() 352 { 353 BMessage settings('stns'); 354 355 settings.AddRect("window frame", fLastWindowFrame); 356 settings.AddMessage("window settings", &fLastWindowSettings); 357 settings.AddInt32("export mode", fSavePanel->ExportMode()); 358 359 save_settings(&settings, "Icon-O-Matic"); 360 } 361 362 363 void 364 IconEditorApp::_RestoreSettings() 365 { 366 BMessage settings('stns'); 367 load_settings(&settings, "Icon-O-Matic"); 368 369 BRect frame; 370 if (settings.FindRect("window frame", &frame) == B_OK) { 371 fLastWindowFrame = frame; 372 // Compensate offset for next window... 373 fLastWindowFrame.OffsetBy(-kWindowOffset, -kWindowOffset); 374 } 375 BMessage lastSettings; 376 if (settings.FindMessage("window settings", &lastSettings) 377 == B_OK) { 378 fLastWindowSettings = lastSettings; 379 } 380 381 int32 mode; 382 if (settings.FindInt32("export mode", &mode) >= B_OK) 383 fSavePanel->SetExportMode(mode); 384 } 385 386 387 void 388 IconEditorApp::_InstallDocumentMimeType() 389 { 390 // install mime type of documents 391 BMimeType mime(kNativeIconMimeType); 392 status_t ret = mime.InitCheck(); 393 if (ret < B_OK) { 394 fprintf(stderr, "Could not init native document mime type (%s): %s.\n", 395 kNativeIconMimeType, strerror(ret)); 396 return; 397 } 398 399 if (mime.IsInstalled() && !(modifiers() & B_SHIFT_KEY)) { 400 // mime is already installed, and the user is not 401 // pressing the shift key to force a re-install 402 return; 403 } 404 405 ret = mime.Install(); 406 if (ret < B_OK) { 407 fprintf(stderr, "Could not install native document mime type (%s): " 408 "%s.\n", kNativeIconMimeType, strerror(ret)); 409 return; 410 } 411 // set preferred app 412 ret = mime.SetPreferredApp(kAppSig); 413 if (ret < B_OK) 414 fprintf(stderr, "Could not set native document preferred app: %s\n", 415 strerror(ret)); 416 417 // set descriptions 418 ret = mime.SetShortDescription("Haiku Icon"); 419 if (ret < B_OK) 420 fprintf(stderr, "Could not set short description of mime type: %s\n", 421 strerror(ret)); 422 ret = mime.SetLongDescription("Native Haiku vector icon"); 423 if (ret < B_OK) 424 fprintf(stderr, "Could not set long description of mime type: %s\n", 425 strerror(ret)); 426 427 // set extensions 428 BMessage message('extn'); 429 message.AddString("extensions", "icon"); 430 ret = mime.SetFileExtensions(&message); 431 if (ret < B_OK) 432 fprintf(stderr, "Could not set extensions of mime type: %s\n", 433 strerror(ret)); 434 435 // set sniffer rule 436 const char* snifferRule = "0.9 ('IMSG')"; 437 ret = mime.SetSnifferRule(snifferRule); 438 if (ret < B_OK) { 439 BString parseError; 440 BMimeType::CheckSnifferRule(snifferRule, &parseError); 441 fprintf(stderr, "Could not set sniffer rule of mime type: %s\n", 442 parseError.String()); 443 } 444 } 445 446