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 switch (message->what) { 141 case MSG_EXPORT_AS: 142 case MSG_EXPORT: 143 exportMode = EXPORT_MODE_FLAT_ICON; 144 break; 145 case MSG_EXPORT_BITMAP: 146 exportMode = EXPORT_MODE_BITMAP; 147 break; 148 case MSG_EXPORT_BITMAP_SET: 149 exportMode = EXPORT_MODE_BITMAP_SET; 150 break; 151 case MSG_EXPORT_SVG: 152 exportMode = EXPORT_MODE_SVG; 153 break; 154 case MSG_EXPORT_ICON_ATTRIBUTE: 155 exportMode = EXPORT_MODE_ICON_ATTR; 156 break; 157 case MSG_EXPORT_ICON_MIME_ATTRIBUTE: 158 exportMode = EXPORT_MODE_ICON_MIME_ATTR; 159 break; 160 case MSG_EXPORT_ICON_RDEF: 161 exportMode = EXPORT_MODE_ICON_RDEF; 162 break; 163 case MSG_SAVE_AS: 164 case MSG_SAVE: 165 default: 166 exportMode = EXPORT_MODE_MESSAGE; 167 break; 168 } 169 BMessage fpMessage(MSG_SAVE_AS); 170 fpMessage.AddInt32("mode", exportMode); 171 172 fSavePanel->SetMessage(&fpMessage); 173 // fSavePanel->Refresh(); 174 fSavePanel->Show(); 175 } 176 break; 177 } 178 179 default: 180 BApplication::MessageReceived(message); 181 break; 182 } 183 } 184 185 // ReadyToRun 186 void 187 IconEditorApp::ReadyToRun() 188 { 189 // create file panels 190 BMessenger messenger(this, this); 191 fOpenPanel = new BFilePanel(B_OPEN_PANEL, 192 &messenger, 193 NULL, 194 B_FILE_NODE, 195 true, 196 new BMessage(B_REFS_RECEIVED)); 197 198 fSavePanel = new BFilePanel(B_SAVE_PANEL, 199 &messenger, 200 NULL, 201 B_FILE_NODE 202 | B_DIRECTORY_NODE 203 | B_SYMLINK_NODE, 204 false, 205 new BMessage(MSG_SAVE)); 206 207 // create main window 208 fMainWindow = new MainWindow(this, fDocument); 209 fMainWindow->Show(); 210 } 211 212 // RefsReceived 213 void 214 IconEditorApp::RefsReceived(BMessage* message) 215 { 216 // TODO: multiple documents (iterate over refs) 217 bool append; 218 if (message->FindBool("append", &append) < B_OK) 219 append = false; 220 entry_ref ref; 221 if (message->FindRef("refs", &ref) == B_OK) 222 _Open(ref, append); 223 224 if (fOpenPanel && fSavePanel) 225 _SyncPanels(fOpenPanel, fSavePanel); 226 } 227 228 // ArgvReceived 229 void 230 IconEditorApp::ArgvReceived(int32 argc, char** argv) 231 { 232 if (argc < 2) 233 return; 234 235 // TODO: multiple documents (iterate over argv) 236 entry_ref ref; 237 if (get_ref_for_path(argv[1], &ref) == B_OK) 238 _Open(ref); 239 } 240 241 // #pragma mark - 242 243 void 244 IconEditorApp::_MakeIconEmpty() 245 { 246 bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); 247 248 AutoWriteLocker locker(fDocument); 249 250 if (fMainWindow) 251 fMainWindow->MakeEmpty(); 252 253 fDocument->MakeEmpty(); 254 255 locker.Unlock(); 256 257 if (mainWindowLocked) 258 fMainWindow->Unlock(); 259 } 260 261 // _Open 262 void 263 IconEditorApp::_Open(const entry_ref& ref, bool append) 264 { 265 BFile file(&ref, B_READ_ONLY); 266 if (file.InitCheck() < B_OK) 267 return; 268 269 Icon* icon; 270 if (append) 271 icon = new (nothrow) Icon(*fDocument->Icon()); 272 else 273 icon = new (nothrow) Icon(); 274 275 if (!icon) 276 return; 277 278 enum { 279 REF_NONE = 0, 280 REF_MESSAGE, 281 REF_FLAT 282 }; 283 uint32 refMode = REF_NONE; 284 285 // try different file types 286 FlatIconImporter flatImporter; 287 status_t ret = flatImporter.Import(icon, &file); 288 if (ret >= B_OK) { 289 refMode = REF_FLAT; 290 } else { 291 file.Seek(0, SEEK_SET); 292 MessageImporter msgImporter; 293 ret = msgImporter.Import(icon, &file); 294 if (ret >= B_OK) { 295 refMode = REF_MESSAGE; 296 } else { 297 file.Seek(0, SEEK_SET); 298 SVGImporter svgImporter; 299 ret = svgImporter.Import(icon, &ref); 300 } 301 } 302 303 if (ret < B_OK) { 304 // inform user of failure at this point 305 BString helper("Opening the document failed!"); 306 helper << "\n\n" << "Error: " << strerror(ret); 307 BAlert* alert = new BAlert("bad news", helper.String(), 308 "Bummer", NULL, NULL); 309 // launch alert asynchronously 310 alert->Go(NULL); 311 312 delete icon; 313 return; 314 } 315 316 // keep the mainwindow locked while switching icons 317 bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); 318 319 AutoWriteLocker locker(fDocument); 320 321 if (mainWindowLocked) 322 fMainWindow->SetIcon(NULL); 323 324 fDocument->MakeEmpty(); 325 326 fDocument->SetIcon(icon); 327 328 switch (refMode) { 329 case REF_MESSAGE: 330 fDocument->SetRef(ref); 331 break; 332 case REF_FLAT: 333 fDocument->SetExportRef(ref); 334 break; 335 } 336 337 locker.Unlock(); 338 339 if (mainWindowLocked) { 340 fMainWindow->Unlock(); 341 // cause the mainwindow to adopt icon in 342 // it's own thread 343 fMainWindow->PostMessage(MSG_SET_ICON); 344 } 345 } 346 347 // _Save 348 void 349 IconEditorApp::_Save(const entry_ref& ref, uint32 exportMode) 350 { 351 Exporter* exporter; 352 switch (exportMode) { 353 case EXPORT_MODE_FLAT_ICON: 354 exporter = new FlatIconExporter(); 355 fDocument->SetExportRef(ref); 356 break; 357 358 case EXPORT_MODE_ICON_ATTR: 359 case EXPORT_MODE_ICON_MIME_ATTR: { 360 BNode node(&ref); 361 FlatIconExporter iconExporter; 362 const char* attrName 363 = exportMode == EXPORT_MODE_ICON_ATTR ? 364 kVectorAttrNodeName : kVectorAttrMimeName; 365 iconExporter.Export(fDocument->Icon(), &node, attrName); 366 return; 367 } 368 369 case EXPORT_MODE_ICON_RDEF: 370 exporter = new RDefExporter(); 371 break; 372 373 case EXPORT_MODE_BITMAP: 374 exporter = new BitmapExporter(64); 375 break; 376 377 case EXPORT_MODE_BITMAP_SET: { 378 entry_ref smallRef(ref); 379 // 64x64 380 char name[B_OS_NAME_LENGTH]; 381 sprintf(name, "%s_64.png", ref.name); 382 smallRef.set_name(name); 383 exporter = new BitmapExporter(64); 384 exporter->SetSelfDestroy(true); 385 exporter->Export(fDocument, smallRef); 386 // 16x16 387 sprintf(name, "%s_16.png", ref.name); 388 smallRef.set_name(name); 389 Exporter* smallExporter = new BitmapExporter(16); 390 smallExporter->SetSelfDestroy(true); 391 smallExporter->Export(fDocument, smallRef); 392 // 32x32 393 sprintf(name, "%s_32.png", ref.name); 394 smallRef.set_name(name); 395 smallExporter = new BitmapExporter(32); 396 smallExporter->SetSelfDestroy(true); 397 smallExporter->Export(fDocument, smallRef); 398 return; 399 } 400 401 case EXPORT_MODE_SVG: 402 exporter = new SVGExporter(); 403 break; 404 405 case EXPORT_MODE_MESSAGE: 406 default: 407 exporter = new MessageExporter(); 408 fDocument->SetRef(ref); 409 break; 410 } 411 412 exporter->SetSelfDestroy(true); 413 // we don't wait for the export thread to finish here 414 exporter->Export(fDocument, ref); 415 } 416 417 // _SyncPanels 418 void 419 IconEditorApp::_SyncPanels(BFilePanel* from, BFilePanel* to) 420 { 421 if (from->Window()->Lock()) { 422 // location 423 if (to->Window()->Lock()) { 424 BRect frame = from->Window()->Frame(); 425 to->Window()->MoveTo(frame.left, frame.top); 426 to->Window()->ResizeTo(frame.Width(), frame.Height()); 427 to->Window()->Unlock(); 428 } 429 // current folder 430 entry_ref panelDir; 431 from->GetPanelDirectory(&panelDir); 432 to->SetPanelDirectory(&panelDir); 433 from->Window()->Unlock(); 434 } 435 } 436