1 /* 2 * Copyright 2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "IconEditorApp.h" 10 11 #include <Alert.h> 12 #include <Directory.h> 13 #include <Entry.h> 14 #include <File.h> 15 #include <FilePanel.h> 16 17 #include <new> 18 #include <stdio.h> 19 #include <string.h> 20 21 #include "AutoLocker.h" 22 #include "BitmapExporter.h" 23 #include "CommandStack.h" 24 #include "Document.h" 25 #include "FlatIconExporter.h" 26 #include "FlatIconFormat.h" 27 #include "FlatIconImporter.h" 28 #include "Icon.h" 29 #include "MainWindow.h" 30 #include "MessageExporter.h" 31 #include "MessageImporter.h" 32 #include "PathContainer.h" 33 #include "RDefExporter.h" 34 #include "ShapeContainer.h" 35 #include "SVGExporter.h" 36 #include "SVGImporter.h" 37 38 using std::nothrow; 39 40 // constructor 41 IconEditorApp::IconEditorApp() 42 : BApplication("application/x-vnd.haiku-icon_o_matic"), 43 fMainWindow(NULL), 44 fDocument(new Document("test")), 45 46 fOpenPanel(NULL), 47 fSavePanel(NULL) 48 { 49 } 50 51 // destructor 52 IconEditorApp::~IconEditorApp() 53 { 54 // NOTE: it is important that the GUI has been deleted 55 // at this point, so that all the listener/observer 56 // stuff is properly detached 57 delete fDocument; 58 59 delete fOpenPanel; 60 delete fSavePanel; 61 } 62 63 // #pragma mark - 64 65 // QuitRequested 66 bool 67 IconEditorApp::QuitRequested() 68 { 69 // TODO: ask main window if quitting is ok 70 fMainWindow->Lock(); 71 fMainWindow->Quit(); 72 fMainWindow = NULL; 73 74 return true; 75 } 76 77 // MessageReceived 78 void 79 IconEditorApp::MessageReceived(BMessage* message) 80 { 81 switch (message->what) { 82 case MSG_NEW: 83 _MakeIconEmpty(); 84 break; 85 case MSG_OPEN: { 86 // fOpenPanel->Refresh(); 87 BMessage openMessage(B_REFS_RECEIVED); 88 fOpenPanel->SetMessage(&openMessage); 89 fOpenPanel->Show(); 90 break; 91 } 92 case MSG_APPEND: { 93 // fOpenPanel->Refresh(); 94 BMessage openMessage(B_REFS_RECEIVED); 95 openMessage.AddBool("append", true); 96 fOpenPanel->SetMessage(&openMessage); 97 fOpenPanel->Show(); 98 break; 99 } 100 case MSG_SAVE: 101 case MSG_EXPORT: { 102 const entry_ref* ref; 103 if (message->what == MSG_SAVE) 104 ref = fDocument->Ref(); 105 else 106 ref = fDocument->ExportRef(); 107 if (ref) { 108 _Save(*ref, message->what == MSG_EXPORT ? 109 EXPORT_MODE_FLAT_ICON : 110 EXPORT_MODE_MESSAGE); 111 break; 112 } // else fall through 113 } 114 case MSG_SAVE_AS: 115 case MSG_EXPORT_AS: 116 case MSG_EXPORT_BITMAP: 117 case MSG_EXPORT_BITMAP_SET: 118 case MSG_EXPORT_SVG: 119 case MSG_EXPORT_ICON_ATTRIBUTE: 120 case MSG_EXPORT_ICON_MIME_ATTRIBUTE: 121 case MSG_EXPORT_ICON_RDEF: { 122 uint32 exportMode; 123 if (message->FindInt32("mode", (int32*)&exportMode) < B_OK) 124 exportMode = EXPORT_MODE_MESSAGE; 125 entry_ref ref; 126 const char* name; 127 if (message->FindRef("directory", &ref) == B_OK 128 && message->FindString("name", &name) == B_OK) { 129 // this message comes from the file panel 130 BDirectory dir(&ref); 131 BEntry entry; 132 if (dir.InitCheck() >= B_OK 133 && entry.SetTo(&dir, name, true) >= B_OK 134 && entry.GetRef(&ref) >= B_OK) { 135 136 _Save(ref, exportMode); 137 } 138 _SyncPanels(fSavePanel, fOpenPanel); 139 } else { 140 const char* saveText = NULL; 141 if (fDocument->Ref()) 142 saveText = fDocument->Ref()->name; 143 144 switch (message->what) { 145 case MSG_EXPORT_AS: 146 case MSG_EXPORT: 147 exportMode = EXPORT_MODE_FLAT_ICON; 148 if (fDocument->ExportRef()) 149 saveText = fDocument->ExportRef()->name; 150 break; 151 case MSG_EXPORT_BITMAP: 152 exportMode = EXPORT_MODE_BITMAP; 153 break; 154 case MSG_EXPORT_BITMAP_SET: 155 exportMode = EXPORT_MODE_BITMAP_SET; 156 break; 157 case MSG_EXPORT_SVG: 158 exportMode = EXPORT_MODE_SVG; 159 break; 160 case MSG_EXPORT_ICON_ATTRIBUTE: 161 exportMode = EXPORT_MODE_ICON_ATTR; 162 break; 163 case MSG_EXPORT_ICON_MIME_ATTRIBUTE: 164 exportMode = EXPORT_MODE_ICON_MIME_ATTR; 165 break; 166 case MSG_EXPORT_ICON_RDEF: 167 exportMode = EXPORT_MODE_ICON_RDEF; 168 break; 169 case MSG_SAVE_AS: 170 case MSG_SAVE: 171 default: 172 exportMode = EXPORT_MODE_MESSAGE; 173 break; 174 } 175 BMessage fpMessage(MSG_SAVE_AS); 176 fpMessage.AddInt32("mode", exportMode); 177 178 fSavePanel->SetMessage(&fpMessage); 179 // fSavePanel->Refresh(); 180 if (saveText) 181 fSavePanel->SetSaveText(saveText); 182 fSavePanel->Show(); 183 } 184 break; 185 } 186 187 default: 188 BApplication::MessageReceived(message); 189 break; 190 } 191 } 192 193 // ReadyToRun 194 void 195 IconEditorApp::ReadyToRun() 196 { 197 // create file panels 198 BMessenger messenger(this, this); 199 fOpenPanel = new BFilePanel(B_OPEN_PANEL, 200 &messenger, 201 NULL, 202 B_FILE_NODE, 203 true, 204 new BMessage(B_REFS_RECEIVED)); 205 206 fSavePanel = new BFilePanel(B_SAVE_PANEL, 207 &messenger, 208 NULL, 209 B_FILE_NODE 210 | B_DIRECTORY_NODE 211 | B_SYMLINK_NODE, 212 false, 213 new BMessage(MSG_SAVE)); 214 215 // create main window 216 fMainWindow = new MainWindow(this, fDocument); 217 fMainWindow->Show(); 218 } 219 220 // RefsReceived 221 void 222 IconEditorApp::RefsReceived(BMessage* message) 223 { 224 // TODO: multiple documents (iterate over refs) 225 bool append; 226 if (message->FindBool("append", &append) < B_OK) 227 append = false; 228 entry_ref ref; 229 if (message->FindRef("refs", &ref) == B_OK) 230 _Open(ref, append); 231 232 if (fOpenPanel && fSavePanel) 233 _SyncPanels(fOpenPanel, fSavePanel); 234 } 235 236 // ArgvReceived 237 void 238 IconEditorApp::ArgvReceived(int32 argc, char** argv) 239 { 240 if (argc < 2) 241 return; 242 243 // TODO: multiple documents (iterate over argv) 244 entry_ref ref; 245 if (get_ref_for_path(argv[1], &ref) == B_OK) 246 _Open(ref); 247 } 248 249 // #pragma mark - 250 251 void 252 IconEditorApp::_MakeIconEmpty() 253 { 254 bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); 255 256 AutoWriteLocker locker(fDocument); 257 258 if (fMainWindow) 259 fMainWindow->MakeEmpty(); 260 261 fDocument->MakeEmpty(); 262 263 locker.Unlock(); 264 265 if (mainWindowLocked) 266 fMainWindow->Unlock(); 267 } 268 269 // _Open 270 void 271 IconEditorApp::_Open(const entry_ref& ref, bool append) 272 { 273 BFile file(&ref, B_READ_ONLY); 274 if (file.InitCheck() < B_OK) 275 return; 276 277 Icon* icon; 278 if (append) 279 icon = new (nothrow) Icon(*fDocument->Icon()); 280 else 281 icon = new (nothrow) Icon(); 282 283 if (!icon) 284 return; 285 286 enum { 287 REF_NONE = 0, 288 REF_MESSAGE, 289 REF_FLAT 290 }; 291 uint32 refMode = REF_NONE; 292 293 // try different file types 294 FlatIconImporter flatImporter; 295 status_t ret = flatImporter.Import(icon, &file); 296 if (ret >= B_OK) { 297 refMode = REF_FLAT; 298 } else { 299 file.Seek(0, SEEK_SET); 300 MessageImporter msgImporter; 301 ret = msgImporter.Import(icon, &file); 302 if (ret >= B_OK) { 303 refMode = REF_MESSAGE; 304 } else { 305 file.Seek(0, SEEK_SET); 306 SVGImporter svgImporter; 307 ret = svgImporter.Import(icon, &ref); 308 } 309 } 310 311 if (ret < B_OK) { 312 // inform user of failure at this point 313 BString helper("Opening the document failed!"); 314 helper << "\n\n" << "Error: " << strerror(ret); 315 BAlert* alert = new BAlert("bad news", helper.String(), 316 "Bummer", NULL, NULL); 317 // launch alert asynchronously 318 alert->Go(NULL); 319 320 delete icon; 321 return; 322 } 323 324 // keep the mainwindow locked while switching icons 325 bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); 326 327 AutoWriteLocker locker(fDocument); 328 329 if (mainWindowLocked) 330 fMainWindow->SetIcon(NULL); 331 332 fDocument->MakeEmpty(); 333 334 fDocument->SetIcon(icon); 335 336 if (!append) { 337 switch (refMode) { 338 case REF_MESSAGE: 339 fDocument->SetRef(ref); 340 break; 341 case REF_FLAT: 342 fDocument->SetExportRef(ref); 343 break; 344 } 345 } 346 347 locker.Unlock(); 348 349 if (mainWindowLocked) { 350 fMainWindow->Unlock(); 351 // cause the mainwindow to adopt icon in 352 // it's own thread 353 fMainWindow->PostMessage(MSG_SET_ICON); 354 } 355 } 356 357 // _Save 358 void 359 IconEditorApp::_Save(const entry_ref& ref, uint32 exportMode) 360 { 361 Exporter* exporter; 362 switch (exportMode) { 363 case EXPORT_MODE_FLAT_ICON: 364 exporter = new FlatIconExporter(); 365 fDocument->SetExportRef(ref); 366 break; 367 368 case EXPORT_MODE_ICON_ATTR: 369 case EXPORT_MODE_ICON_MIME_ATTR: { 370 BNode node(&ref); 371 FlatIconExporter iconExporter; 372 const char* attrName 373 = exportMode == EXPORT_MODE_ICON_ATTR ? 374 kVectorAttrNodeName : kVectorAttrMimeName; 375 iconExporter.Export(fDocument->Icon(), &node, attrName); 376 return; 377 } 378 379 case EXPORT_MODE_ICON_RDEF: 380 exporter = new RDefExporter(); 381 break; 382 383 case EXPORT_MODE_BITMAP: 384 exporter = new BitmapExporter(64); 385 break; 386 387 case EXPORT_MODE_BITMAP_SET: { 388 entry_ref smallRef(ref); 389 // 64x64 390 char name[B_OS_NAME_LENGTH]; 391 sprintf(name, "%s_64.png", ref.name); 392 smallRef.set_name(name); 393 exporter = new BitmapExporter(64); 394 exporter->SetSelfDestroy(true); 395 exporter->Export(fDocument, smallRef); 396 // 16x16 397 sprintf(name, "%s_16.png", ref.name); 398 smallRef.set_name(name); 399 Exporter* smallExporter = new BitmapExporter(16); 400 smallExporter->SetSelfDestroy(true); 401 smallExporter->Export(fDocument, smallRef); 402 // 32x32 403 sprintf(name, "%s_32.png", ref.name); 404 smallRef.set_name(name); 405 smallExporter = new BitmapExporter(32); 406 smallExporter->SetSelfDestroy(true); 407 smallExporter->Export(fDocument, smallRef); 408 return; 409 } 410 411 case EXPORT_MODE_SVG: 412 exporter = new SVGExporter(); 413 break; 414 415 case EXPORT_MODE_MESSAGE: 416 default: 417 exporter = new MessageExporter(); 418 fDocument->SetRef(ref); 419 break; 420 } 421 422 exporter->SetSelfDestroy(true); 423 // we don't wait for the export thread to finish here 424 exporter->Export(fDocument, ref); 425 } 426 427 // _SyncPanels 428 void 429 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to) 430 { 431 if (from->Window()->Lock()) { 432 // location 433 if (to->Window()->Lock()) { 434 BRect frame = from->Window()->Frame(); 435 to->Window()->MoveTo(frame.left, frame.top); 436 to->Window()->ResizeTo(frame.Width(), frame.Height()); 437 to->Window()->Unlock(); 438 } 439 // current folder 440 entry_ref panelDir; 441 from->GetPanelDirectory(&panelDir); 442 to->SetPanelDirectory(&panelDir); 443 from->Window()->Unlock(); 444 } 445 } 446