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