1 /* 2 * Copyright 2006-2011, Stephan Aßmus <superstippi@gmx.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "MainWindow.h" 7 8 #include <new> 9 #include <stdio.h> 10 11 #include <Alert.h> 12 #include <Catalog.h> 13 #include <Clipboard.h> 14 #include <GridLayout.h> 15 #include <GroupLayout.h> 16 #include <GroupView.h> 17 #include <Directory.h> 18 #include <Entry.h> 19 #include <File.h> 20 #include <fs_attr.h> 21 #include <LayoutBuilder.h> 22 #include <Locale.h> 23 #include <Menu.h> 24 #include <MenuBar.h> 25 #include <MenuField.h> 26 #include <MenuItem.h> 27 #include <Message.h> 28 #include <Screen.h> 29 #include <ScrollView.h> 30 31 #include "support_ui.h" 32 33 #include "AddPathsCommand.h" 34 #include "AddShapesCommand.h" 35 #include "AddStylesCommand.h" 36 #include "AttributeSaver.h" 37 #include "BitmapExporter.h" 38 #include "BitmapSetSaver.h" 39 #include "CanvasView.h" 40 #include "CommandStack.h" 41 #include "CompoundCommand.h" 42 #include "CurrentColor.h" 43 #include "Document.h" 44 #include "FlatIconExporter.h" 45 #include "FlatIconFormat.h" 46 #include "FlatIconImporter.h" 47 #include "IconObjectListView.h" 48 #include "IconEditorApp.h" 49 #include "IconView.h" 50 #include "MessageExporter.h" 51 #include "MessageImporter.h" 52 #include "MessengerSaver.h" 53 #include "NativeSaver.h" 54 #include "PathListView.h" 55 #include "RDefExporter.h" 56 #include "ScrollView.h" 57 #include "SimpleFileSaver.h" 58 #include "ShapeListView.h" 59 #include "SourceExporter.h" 60 #include "StyleListView.h" 61 #include "StyleView.h" 62 #include "SVGExporter.h" 63 #include "SVGImporter.h" 64 #include "SwatchGroup.h" 65 #include "TransformerListView.h" 66 #include "TransformGradientBox.h" 67 #include "TransformShapesBox.h" 68 #include "Util.h" 69 70 // TODO: just for testing 71 #include "AffineTransformer.h" 72 #include "GradientTransformable.h" 73 #include "Icon.h" 74 #include "MultipleManipulatorState.h" 75 #include "PathManipulator.h" 76 #include "Shape.h" 77 #include "ShapeContainer.h" 78 #include "ShapeListView.h" 79 #include "StrokeTransformer.h" 80 #include "Style.h" 81 #include "StyleContainer.h" 82 #include "VectorPath.h" 83 84 #include "StyledTextImporter.h" 85 86 87 #undef B_TRANSLATION_CONTEXT 88 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Main" 89 90 91 using std::nothrow; 92 93 enum { 94 MSG_UNDO = 'undo', 95 MSG_REDO = 'redo', 96 MSG_UNDO_STACK_CHANGED = 'usch', 97 98 MSG_PATH_SELECTED = 'vpsl', 99 MSG_STYLE_SELECTED = 'stsl', 100 MSG_SHAPE_SELECTED = 'spsl', 101 102 MSG_SHAPE_RESET_TRANSFORMATION = 'rtsh', 103 MSG_STYLE_RESET_TRANSFORMATION = 'rtst', 104 105 MSG_MOUSE_FILTER_MODE = 'mfmd', 106 107 MSG_RENAME_OBJECT = 'rnam', 108 }; 109 110 111 MainWindow::MainWindow(BRect frame, IconEditorApp* app, 112 const BMessage* settings) 113 : 114 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"), 115 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 116 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 117 fApp(app), 118 fDocument(new Document(B_TRANSLATE("Untitled"))), 119 fCurrentColor(new CurrentColor()), 120 fIcon(NULL), 121 fMessageAfterSave(NULL) 122 { 123 _Init(); 124 125 RestoreSettings(settings); 126 } 127 128 129 MainWindow::~MainWindow() 130 { 131 SetIcon(NULL); 132 133 delete fState; 134 135 // Make sure there are no listeners attached to the document anymore. 136 while (BView* child = ChildAt(0L)) { 137 child->RemoveSelf(); 138 delete child; 139 } 140 141 fDocument->CommandStack()->RemoveObserver(this); 142 143 // NOTE: it is important that the GUI has been deleted 144 // at this point, so that all the listener/observer 145 // stuff is properly detached 146 delete fDocument; 147 148 delete fMessageAfterSave; 149 } 150 151 152 // #pragma mark - 153 154 155 void 156 MainWindow::MessageReceived(BMessage* message) 157 { 158 bool discard = false; 159 160 // Figure out if we need the write lock on the Document. For most 161 // messages we do, but exporting takes place in another thread and 162 // locking is taken care of there. 163 bool requiresWriteLock = true; 164 switch (message->what) { 165 case MSG_SAVE: 166 case MSG_EXPORT: 167 case MSG_SAVE_AS: 168 case MSG_EXPORT_AS: 169 requiresWriteLock = false; 170 break; 171 default: 172 break; 173 } 174 if (requiresWriteLock && !fDocument->WriteLock()) { 175 BWindow::MessageReceived(message); 176 return; 177 } 178 179 if (message->WasDropped()) { 180 const rgb_color* color; 181 ssize_t length; 182 // create styles from dropped colors 183 for (int32 i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i, 184 (const void**)&color, &length) == B_OK; i++) { 185 if (length != sizeof(rgb_color)) 186 continue; 187 char name[30]; 188 sprintf(name, 189 B_TRANSLATE_CONTEXT("Color (#%02x%02x%02x)", 190 "Style name after dropping a color"), 191 color->red, color->green, color->blue); 192 Style* style = new (nothrow) Style(*color); 193 style->SetName(name); 194 Style* styles[1] = { style }; 195 AddStylesCommand* styleCommand = new (nothrow) AddStylesCommand( 196 fDocument->Icon()->Styles(), styles, 1, 197 fDocument->Icon()->Styles()->CountStyles()); 198 fDocument->CommandStack()->Perform(styleCommand); 199 // don't handle anything else, 200 // or we might paste the clipboard on B_PASTE 201 discard = true; 202 } 203 } 204 205 switch (message->what) { 206 207 case B_REFS_RECEIVED: 208 case B_SIMPLE_DATA: 209 // If our icon is empty, open the file in this window, 210 // otherwise forward to the application which will open 211 // it in another window, unless we append. 212 message->what = B_REFS_RECEIVED; 213 if (fDocument->Icon()->Styles()->CountStyles() == 0 214 && fDocument->Icon()->Paths()->CountPaths() == 0 215 && fDocument->Icon()->Shapes()->CountShapes() == 0) { 216 entry_ref ref; 217 if (message->FindRef("refs", &ref) == B_OK) 218 Open(ref); 219 break; 220 } 221 if (modifiers() & B_SHIFT_KEY) { 222 // We want the icon appended to this window. 223 message->AddBool("append", true); 224 message->AddPointer("window", this); 225 } 226 be_app->PostMessage(message); 227 break; 228 229 case B_PASTE: 230 case B_MIME_DATA: 231 { 232 BMessage* clip = message; 233 status_t err; 234 235 if (discard) 236 break; 237 238 if (message->what == B_PASTE) { 239 if (!be_clipboard->Lock()) 240 break; 241 clip = be_clipboard->Data(); 242 } 243 244 if (!clip || !clip->HasData("text/plain", B_MIME_TYPE)) { 245 if (message->what == B_PASTE) 246 be_clipboard->Unlock(); 247 break; 248 } 249 250 Icon* icon = new (std::nothrow) Icon(*fDocument->Icon()); 251 if (icon != NULL) { 252 StyledTextImporter importer; 253 err = importer.Import(icon, clip); 254 if (err >= B_OK) { 255 AutoWriteLocker locker(fDocument); 256 257 SetIcon(NULL); 258 259 // incorporate the loaded icon into the document 260 // (either replace it or append to it) 261 fDocument->MakeEmpty(false); 262 // if append, the document savers are preserved 263 fDocument->SetIcon(icon); 264 SetIcon(icon); 265 } 266 } 267 268 if (message->what == B_PASTE) 269 be_clipboard->Unlock(); 270 break; 271 } 272 273 case MSG_OPEN: 274 // If our icon is empty, we want the icon to open in this 275 // window. 276 if (fDocument->Icon()->Styles()->CountStyles() == 0 277 && fDocument->Icon()->Paths()->CountPaths() == 0 278 && fDocument->Icon()->Shapes()->CountShapes() == 0) { 279 message->AddPointer("window", this); 280 } 281 be_app->PostMessage(message); 282 break; 283 284 case MSG_SAVE: 285 case MSG_EXPORT: 286 { 287 DocumentSaver* saver; 288 if (message->what == MSG_SAVE) 289 saver = fDocument->NativeSaver(); 290 else 291 saver = fDocument->ExportSaver(); 292 if (saver != NULL) { 293 saver->Save(fDocument); 294 _PickUpActionBeforeSave(); 295 break; 296 } // else fall through 297 } 298 case MSG_SAVE_AS: 299 case MSG_EXPORT_AS: 300 { 301 int32 exportMode; 302 if (message->FindInt32("export mode", &exportMode) < B_OK) 303 exportMode = EXPORT_MODE_MESSAGE; 304 entry_ref ref; 305 const char* name; 306 if (message->FindRef("directory", &ref) == B_OK 307 && message->FindString("name", &name) == B_OK) { 308 // this message comes from the file panel 309 BDirectory dir(&ref); 310 BEntry entry; 311 if (dir.InitCheck() >= B_OK 312 && entry.SetTo(&dir, name, true) >= B_OK 313 && entry.GetRef(&ref) >= B_OK) { 314 315 // create the document saver and remember it for later 316 DocumentSaver* saver = _CreateSaver(ref, exportMode); 317 if (saver != NULL) { 318 if (fDocument->WriteLock()) { 319 if (exportMode == EXPORT_MODE_MESSAGE) 320 fDocument->SetNativeSaver(saver); 321 else 322 fDocument->SetExportSaver(saver); 323 _UpdateWindowTitle(); 324 fDocument->WriteUnlock(); 325 } 326 saver->Save(fDocument); 327 _PickUpActionBeforeSave(); 328 } 329 } 330 // TODO: ... 331 // _SyncPanels(fSavePanel, fOpenPanel); 332 } else { 333 // configure the file panel 334 uint32 requestRefWhat = MSG_SAVE_AS; 335 bool isExportMode = message->what == MSG_EXPORT_AS 336 || message->what == MSG_EXPORT; 337 if (isExportMode) 338 requestRefWhat = MSG_EXPORT_AS; 339 const char* saveText = _FileName(isExportMode); 340 341 BMessage requestRef(requestRefWhat); 342 if (saveText != NULL) 343 requestRef.AddString("save text", saveText); 344 requestRef.AddMessenger("target", BMessenger(this, this)); 345 be_app->PostMessage(&requestRef); 346 } 347 break; 348 } 349 case B_CANCEL: 350 // FilePanel was canceled, do not execute the fMessageAfterSave 351 // next time a file panel is used, in case it was set! 352 delete fMessageAfterSave; 353 fMessageAfterSave = NULL; 354 break; 355 356 case MSG_UNDO: 357 fDocument->CommandStack()->Undo(); 358 break; 359 case MSG_REDO: 360 fDocument->CommandStack()->Redo(); 361 break; 362 case MSG_UNDO_STACK_CHANGED: 363 { 364 // relable Undo item and update enabled status 365 BString label(B_TRANSLATE("Undo")); 366 fUndoMI->SetEnabled(fDocument->CommandStack()->GetUndoName(label)); 367 if (fUndoMI->IsEnabled()) 368 fUndoMI->SetLabel(label.String()); 369 else { 370 fUndoMI->SetLabel(B_TRANSLATE_CONTEXT("<nothing to undo>", 371 "Icon-O-Matic-Menu-Edit")); 372 } 373 374 // relable Redo item and update enabled status 375 label.SetTo(B_TRANSLATE("Redo")); 376 fRedoMI->SetEnabled(fDocument->CommandStack()->GetRedoName(label)); 377 if (fRedoMI->IsEnabled()) 378 fRedoMI->SetLabel(label.String()); 379 else { 380 fRedoMI->SetLabel(B_TRANSLATE_CONTEXT("<nothing to redo>", 381 "Icon-O-Matic-Menu-Edit")); 382 } 383 break; 384 } 385 386 case MSG_MOUSE_FILTER_MODE: 387 { 388 uint32 mode; 389 if (message->FindInt32("mode", (int32*)&mode) == B_OK) 390 fCanvasView->SetMouseFilterMode(mode); 391 break; 392 } 393 394 case MSG_ADD_SHAPE: { 395 AddStylesCommand* styleCommand = NULL; 396 Style* style = NULL; 397 if (message->HasBool("style")) { 398 new_style(fCurrentColor->Color(), 399 fDocument->Icon()->Styles(), &style, &styleCommand); 400 } 401 402 AddPathsCommand* pathCommand = NULL; 403 VectorPath* path = NULL; 404 if (message->HasBool("path")) { 405 new_path(fDocument->Icon()->Paths(), &path, &pathCommand); 406 } 407 408 if (!style) { 409 // use current or first style 410 int32 currentStyle = fStyleListView->CurrentSelection(0); 411 style = fDocument->Icon()->Styles()->StyleAt(currentStyle); 412 if (!style) 413 style = fDocument->Icon()->Styles()->StyleAt(0); 414 } 415 416 Shape* shape = new (nothrow) Shape(style); 417 Shape* shapes[1]; 418 shapes[0] = shape; 419 AddShapesCommand* shapeCommand = new (nothrow) AddShapesCommand( 420 fDocument->Icon()->Shapes(), shapes, 1, 421 fDocument->Icon()->Shapes()->CountShapes(), 422 fDocument->Selection()); 423 424 if (path && shape) 425 shape->Paths()->AddPath(path); 426 427 ::Command* command = NULL; 428 if (styleCommand || pathCommand) { 429 if (styleCommand && pathCommand) { 430 Command** commands = new Command*[3]; 431 commands[0] = styleCommand; 432 commands[1] = pathCommand; 433 commands[2] = shapeCommand; 434 command = new CompoundCommand(commands, 3, 435 B_TRANSLATE_CONTEXT("Add shape with path & style", 436 "Icon-O-Matic-Menu-Shape"), 437 0); 438 } else if (styleCommand) { 439 Command** commands = new Command*[2]; 440 commands[0] = styleCommand; 441 commands[1] = shapeCommand; 442 command = new CompoundCommand(commands, 2, 443 B_TRANSLATE_CONTEXT("Add shape with style", 444 "Icon-O-Matic-Menu-Shape"), 445 0); 446 } else { 447 Command** commands = new Command*[2]; 448 commands[0] = pathCommand; 449 commands[1] = shapeCommand; 450 command = new CompoundCommand(commands, 2, 451 B_TRANSLATE_CONTEXT("Add shape with path", 452 "Icon-O-Matic-Menu-Shape"), 453 0); 454 } 455 } else { 456 command = shapeCommand; 457 } 458 fDocument->CommandStack()->Perform(command); 459 break; 460 } 461 462 // TODO: listen to selection in CanvasView to add a manipulator 463 case MSG_PATH_SELECTED: { 464 VectorPath* path; 465 if (message->FindPointer("path", (void**)&path) < B_OK) 466 path = NULL; 467 468 fPathListView->SetCurrentShape(NULL); 469 fStyleListView->SetCurrentShape(NULL); 470 fTransformerListView->SetShape(NULL); 471 472 fState->DeleteManipulators(); 473 if (fDocument->Icon()->Paths()->HasPath(path)) { 474 PathManipulator* pathManipulator = new (nothrow) PathManipulator(path); 475 fState->AddManipulator(pathManipulator); 476 } 477 break; 478 } 479 case MSG_STYLE_SELECTED: 480 case MSG_STYLE_TYPE_CHANGED: { 481 Style* style; 482 if (message->FindPointer("style", (void**)&style) < B_OK) 483 style = NULL; 484 if (!fDocument->Icon()->Styles()->HasStyle(style)) 485 style = NULL; 486 487 fStyleView->SetStyle(style); 488 fPathListView->SetCurrentShape(NULL); 489 fStyleListView->SetCurrentShape(NULL); 490 fTransformerListView->SetShape(NULL); 491 492 fState->DeleteManipulators(); 493 Gradient* gradient = style ? style->Gradient() : NULL; 494 if (gradient != NULL) { 495 TransformGradientBox* transformBox 496 = new (nothrow) TransformGradientBox(fCanvasView, gradient, NULL); 497 fState->AddManipulator(transformBox); 498 } 499 break; 500 } 501 case MSG_SHAPE_SELECTED: { 502 Shape* shape; 503 if (message->FindPointer("shape", (void**)&shape) < B_OK) 504 shape = NULL; 505 if (!fIcon || !fIcon->Shapes()->HasShape(shape)) 506 shape = NULL; 507 508 fPathListView->SetCurrentShape(shape); 509 fStyleListView->SetCurrentShape(shape); 510 fTransformerListView->SetShape(shape); 511 512 BList selectedShapes; 513 ShapeContainer* shapes = fDocument->Icon()->Shapes(); 514 int32 count = shapes->CountShapes(); 515 for (int32 i = 0; i < count; i++) { 516 shape = shapes->ShapeAtFast(i); 517 if (shape->IsSelected()) { 518 selectedShapes.AddItem((void*)shape); 519 } 520 } 521 522 fState->DeleteManipulators(); 523 if (selectedShapes.CountItems() > 0) { 524 TransformShapesBox* transformBox = new (nothrow) TransformShapesBox( 525 fCanvasView, 526 (const Shape**)selectedShapes.Items(), 527 selectedShapes.CountItems()); 528 fState->AddManipulator(transformBox); 529 } 530 break; 531 } 532 case MSG_RENAME_OBJECT: 533 fPropertyListView->FocusNameProperty(); 534 break; 535 536 default: 537 BWindow::MessageReceived(message); 538 } 539 540 if (requiresWriteLock) 541 fDocument->WriteUnlock(); 542 } 543 544 545 void 546 MainWindow::Show() 547 { 548 BWindow::Show(); 549 BMenuBar* bar = static_cast<BMenuBar*>(FindView("main menu")); 550 SetKeyMenuBar(bar); 551 } 552 553 554 bool 555 MainWindow::QuitRequested() 556 { 557 if (!_CheckSaveIcon(CurrentMessage())) 558 return false; 559 560 BMessage message(MSG_WINDOW_CLOSED); 561 562 BMessage settings; 563 StoreSettings(&settings); 564 message.AddMessage("settings", &settings); 565 message.AddRect("window frame", Frame()); 566 567 be_app->PostMessage(&message); 568 569 return true; 570 } 571 572 573 void 574 MainWindow::WorkspaceActivated(int32 workspace, bool active) 575 { 576 BWindow::WorkspaceActivated(workspace, active); 577 578 // NOTE: hack to workaround buggy BScreen::DesktopColor() on R5 579 580 uint32 workspaces = Workspaces(); 581 if (!active || ((1 << workspace) & workspaces) == 0) 582 return; 583 584 WorkspacesChanged(workspaces, workspaces); 585 } 586 587 588 void 589 MainWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 590 { 591 if (oldWorkspaces != newWorkspaces) 592 BWindow::WorkspacesChanged(oldWorkspaces, newWorkspaces); 593 594 BScreen screen(this); 595 596 // Unfortunately, this is buggy on R5: screen.DesktopColor() 597 // as well as ui_color(B_DESKTOP_COLOR) return the color 598 // of the *active* screen, not the one on which this window 599 // is. So this trick only works when you drag this window 600 // from another workspace onto the current workspace, not 601 // when you drag the window from the current workspace onto 602 // another workspace and then switch to the other workspace. 603 604 fIconPreview32Desktop->SetIconBGColor(screen.DesktopColor()); 605 fIconPreview64->SetIconBGColor(screen.DesktopColor()); 606 } 607 608 609 // #pragma mark - 610 611 612 void 613 MainWindow::ObjectChanged(const Observable* object) 614 { 615 if (!fDocument || !fDocument->ReadLock()) 616 return; 617 618 if (object == fDocument->CommandStack()) 619 PostMessage(MSG_UNDO_STACK_CHANGED); 620 621 fDocument->ReadUnlock(); 622 } 623 624 625 // #pragma mark - 626 627 628 void 629 MainWindow::MakeEmpty() 630 { 631 fPathListView->SetCurrentShape(NULL); 632 fStyleListView->SetCurrentShape(NULL); 633 fStyleView->SetStyle(NULL); 634 635 fTransformerListView->SetShape(NULL); 636 637 fState->DeleteManipulators(); 638 } 639 640 641 void 642 MainWindow::Open(const entry_ref& ref, bool append) 643 { 644 BFile file(&ref, B_READ_ONLY); 645 if (file.InitCheck() < B_OK) 646 return; 647 648 Icon* icon; 649 if (append) 650 icon = new (nothrow) Icon(*fDocument->Icon()); 651 else 652 icon = new (nothrow) Icon(); 653 654 if (icon == NULL) { 655 // TODO: Report error to user. 656 return; 657 } 658 659 enum { 660 REF_NONE = 0, 661 REF_MESSAGE, 662 REF_FLAT, 663 REF_FLAT_ATTR, 664 REF_SVG 665 }; 666 uint32 refMode = REF_NONE; 667 668 // try different file types 669 FlatIconImporter flatImporter; 670 status_t ret = flatImporter.Import(icon, &file); 671 if (ret >= B_OK) { 672 refMode = REF_FLAT; 673 } else { 674 file.Seek(0, SEEK_SET); 675 MessageImporter msgImporter; 676 ret = msgImporter.Import(icon, &file); 677 if (ret >= B_OK) { 678 refMode = REF_MESSAGE; 679 } else { 680 file.Seek(0, SEEK_SET); 681 SVGImporter svgImporter; 682 ret = svgImporter.Import(icon, &ref); 683 if (ret >= B_OK) { 684 refMode = REF_SVG; 685 } else { 686 // fall back to flat icon format but use the icon attribute 687 ret = B_OK; 688 attr_info attrInfo; 689 if (file.GetAttrInfo(kVectorAttrNodeName, &attrInfo) == B_OK) { 690 if (attrInfo.type != B_VECTOR_ICON_TYPE) 691 ret = B_ERROR; 692 // If the attribute is there, we must succeed in reading 693 // an icon! Otherwise we may overwrite an existing icon 694 // when the user saves. 695 uint8* buffer = NULL; 696 if (ret == B_OK) { 697 buffer = new(nothrow) uint8[attrInfo.size]; 698 if (buffer == NULL) 699 ret = B_NO_MEMORY; 700 } 701 if (ret == B_OK) { 702 ssize_t bytesRead = file.ReadAttr(kVectorAttrNodeName, 703 B_VECTOR_ICON_TYPE, 0, buffer, attrInfo.size); 704 if (bytesRead != (ssize_t)attrInfo.size) { 705 ret = bytesRead < 0 ? (status_t)bytesRead 706 : B_IO_ERROR; 707 } 708 } 709 if (ret == B_OK) { 710 ret = flatImporter.Import(icon, buffer, attrInfo.size); 711 if (ret == B_OK) 712 refMode = REF_FLAT_ATTR; 713 } 714 715 delete[] buffer; 716 } else { 717 // If there is no icon attribute, simply fall back 718 // to creating an icon for this file. TODO: We may or may 719 // not want to display an alert asking the user if that is 720 // what he wants to do. 721 refMode = REF_FLAT_ATTR; 722 } 723 } 724 } 725 } 726 727 if (ret < B_OK) { 728 // inform user of failure at this point 729 BString helper(B_TRANSLATE("Opening the document failed!")); 730 helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); 731 BAlert* alert = new BAlert( 732 B_TRANSLATE_CONTEXT("bad news", "Title of error alert"), 733 helper.String(), 734 B_TRANSLATE_CONTEXT("Bummer", 735 "Cancel button - error alert"), 736 NULL, NULL); 737 // launch alert asynchronously 738 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 739 alert->Go(NULL); 740 741 delete icon; 742 return; 743 } 744 745 AutoWriteLocker locker(fDocument); 746 747 // incorporate the loaded icon into the document 748 // (either replace it or append to it) 749 fDocument->MakeEmpty(!append); 750 // if append, the document savers are preserved 751 fDocument->SetIcon(icon); 752 if (!append) { 753 // document got replaced, but we have at 754 // least one ref already 755 switch (refMode) { 756 case REF_MESSAGE: 757 fDocument->SetNativeSaver(new NativeSaver(ref)); 758 break; 759 case REF_FLAT: 760 fDocument->SetExportSaver( 761 new SimpleFileSaver(new FlatIconExporter(), ref)); 762 break; 763 case REF_FLAT_ATTR: 764 fDocument->SetNativeSaver( 765 new AttributeSaver(ref, kVectorAttrNodeName)); 766 break; 767 case REF_SVG: 768 fDocument->SetExportSaver( 769 new SimpleFileSaver(new SVGExporter(), ref)); 770 break; 771 } 772 } 773 774 locker.Unlock(); 775 776 SetIcon(icon); 777 778 _UpdateWindowTitle(); 779 } 780 781 782 void 783 MainWindow::Open(const BMessenger& externalObserver, const uint8* data, 784 size_t size) 785 { 786 if (!_CheckSaveIcon(CurrentMessage())) 787 return; 788 789 if (!externalObserver.IsValid()) 790 return; 791 792 Icon* icon = new (nothrow) Icon(); 793 if (!icon) 794 return; 795 796 if (data && size > 0) { 797 // try to open the icon from the provided data 798 FlatIconImporter flatImporter; 799 status_t ret = flatImporter.Import(icon, const_cast<uint8*>(data), 800 size); 801 // NOTE: the const_cast is a bit ugly, but no harm is done 802 // the reason is that the LittleEndianBuffer knows read and write 803 // mode, in this case it is used read-only, and it does not assume 804 // ownership of the buffer 805 806 if (ret < B_OK) { 807 // inform user of failure at this point 808 BString helper(B_TRANSLATE("Opening the icon failed!")); 809 helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); 810 BAlert* alert = new BAlert( 811 B_TRANSLATE_CONTEXT("bad news", "Title of error alert"), 812 helper.String(), 813 B_TRANSLATE_CONTEXT("Bummer", 814 "Cancel button - error alert"), 815 NULL, NULL); 816 // launch alert asynchronously 817 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 818 alert->Go(NULL); 819 820 delete icon; 821 return; 822 } 823 } 824 825 AutoWriteLocker locker(fDocument); 826 827 SetIcon(NULL); 828 829 // incorporate the loaded icon into the document 830 // (either replace it or append to it) 831 fDocument->MakeEmpty(); 832 fDocument->SetIcon(icon); 833 834 fDocument->SetNativeSaver(new MessengerSaver(externalObserver)); 835 836 locker.Unlock(); 837 838 SetIcon(icon); 839 } 840 841 842 void 843 MainWindow::SetIcon(Icon* icon) 844 { 845 if (fIcon == icon) 846 return; 847 848 Icon* oldIcon = fIcon; 849 850 fIcon = icon; 851 852 if (fIcon != NULL) 853 fIcon->AcquireReference(); 854 else 855 MakeEmpty(); 856 857 fCanvasView->SetIcon(fIcon); 858 859 fPathListView->SetPathContainer(fIcon != NULL ? fIcon->Paths() : NULL); 860 fPathListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); 861 862 fStyleListView->SetStyleContainer(fIcon != NULL ? fIcon->Styles() : NULL); 863 fStyleListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); 864 865 fShapeListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); 866 fShapeListView->SetStyleContainer(fIcon != NULL ? fIcon->Styles() : NULL); 867 fShapeListView->SetPathContainer(fIcon != NULL ? fIcon->Paths() : NULL); 868 869 // icon previews 870 fIconPreview16Folder->SetIcon(fIcon); 871 fIconPreview16Menu->SetIcon(fIcon); 872 fIconPreview32Folder->SetIcon(fIcon); 873 fIconPreview32Desktop->SetIcon(fIcon); 874 // fIconPreview48->SetIcon(fIcon); 875 fIconPreview64->SetIcon(fIcon); 876 877 // keep this last 878 if (oldIcon != NULL) 879 oldIcon->ReleaseReference(); 880 } 881 882 883 // #pragma mark - 884 885 886 void 887 MainWindow::StoreSettings(BMessage* archive) 888 { 889 if (archive->ReplaceUInt32("mouse filter mode", 890 fCanvasView->MouseFilterMode()) != B_OK) { 891 archive->AddUInt32("mouse filter mode", 892 fCanvasView->MouseFilterMode()); 893 } 894 } 895 896 897 void 898 MainWindow::RestoreSettings(const BMessage* archive) 899 { 900 uint32 mouseFilterMode; 901 if (archive->FindUInt32("mouse filter mode", &mouseFilterMode) == B_OK) { 902 fCanvasView->SetMouseFilterMode(mouseFilterMode); 903 fMouseFilterOffMI->SetMarked(mouseFilterMode == SNAPPING_OFF); 904 fMouseFilter64MI->SetMarked(mouseFilterMode == SNAPPING_64); 905 fMouseFilter32MI->SetMarked(mouseFilterMode == SNAPPING_32); 906 fMouseFilter16MI->SetMarked(mouseFilterMode == SNAPPING_16); 907 } 908 } 909 910 911 // #pragma mark - 912 913 914 void 915 MainWindow::_Init() 916 { 917 // create the GUI 918 _CreateGUI(); 919 920 // fix up scrollbar layout in listviews 921 _ImproveScrollBarLayout(fPathListView); 922 _ImproveScrollBarLayout(fStyleListView); 923 _ImproveScrollBarLayout(fShapeListView); 924 _ImproveScrollBarLayout(fTransformerListView); 925 926 // TODO: move this to CanvasView? 927 fState = new MultipleManipulatorState(fCanvasView); 928 fCanvasView->SetState(fState); 929 930 fCanvasView->SetCatchAllEvents(true); 931 fCanvasView->SetCommandStack(fDocument->CommandStack()); 932 fCanvasView->SetMouseFilterMode(SNAPPING_64); 933 fMouseFilter64MI->SetMarked(true); 934 // fCanvasView->SetSelection(fDocument->Selection()); 935 936 fPathListView->SetMenu(fPathMenu); 937 fPathListView->SetCommandStack(fDocument->CommandStack()); 938 fPathListView->SetSelection(fDocument->Selection()); 939 940 fStyleListView->SetMenu(fStyleMenu); 941 fStyleListView->SetCommandStack(fDocument->CommandStack()); 942 fStyleListView->SetSelection(fDocument->Selection()); 943 fStyleListView->SetCurrentColor(fCurrentColor); 944 945 fStyleView->SetCommandStack(fDocument->CommandStack()); 946 fStyleView->SetCurrentColor(fCurrentColor); 947 948 fShapeListView->SetMenu(fShapeMenu); 949 fShapeListView->SetCommandStack(fDocument->CommandStack()); 950 fShapeListView->SetSelection(fDocument->Selection()); 951 952 fTransformerListView->SetMenu(fTransformerMenu); 953 fTransformerListView->SetCommandStack(fDocument->CommandStack()); 954 fTransformerListView->SetSelection(fDocument->Selection()); 955 956 fPropertyListView->SetCommandStack(fDocument->CommandStack()); 957 fPropertyListView->SetSelection(fDocument->Selection()); 958 fPropertyListView->SetMenu(fPropertyMenu); 959 960 fDocument->CommandStack()->AddObserver(this); 961 962 fSwatchGroup->SetCurrentColor(fCurrentColor); 963 964 SetIcon(fDocument->Icon()); 965 966 AddShortcut('Y', 0, new BMessage(MSG_UNDO)); 967 AddShortcut('Y', B_SHIFT_KEY, new BMessage(MSG_REDO)); 968 AddShortcut('E', 0, new BMessage(MSG_RENAME_OBJECT)); 969 } 970 971 972 void 973 MainWindow::_CreateGUI() 974 { 975 SetLayout(new BGroupLayout(B_HORIZONTAL)); 976 977 BGridLayout* layout = new BGridLayout(); 978 layout->SetSpacing(0, 0); 979 BView* rootView = new BView("root view", 0, layout); 980 AddChild(rootView); 981 rootView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 982 983 BGroupView* leftTopView = new BGroupView(B_VERTICAL, 0); 984 layout->AddView(leftTopView, 0, 0); 985 986 // views along the left side 987 BMenuBar* mainMenuBar = _CreateMenuBar(); 988 leftTopView->AddChild(mainMenuBar); 989 990 float splitWidth = 13 * be_plain_font->Size(); 991 BSize minSize = leftTopView->MinSize(); 992 splitWidth = std::max(splitWidth, minSize.width); 993 leftTopView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNSET)); 994 leftTopView->SetExplicitMinSize(BSize(splitWidth, B_SIZE_UNSET)); 995 996 BGroupView* iconPreviews = new BGroupView(B_HORIZONTAL); 997 iconPreviews->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 998 iconPreviews->GroupLayout()->SetSpacing(5); 999 1000 // icon previews 1001 fIconPreview16Folder = new IconView(BRect(0, 0, 15, 15), 1002 "icon preview 16 folder"); 1003 fIconPreview16Menu = new IconView(BRect(0, 0, 15, 15), 1004 "icon preview 16 menu"); 1005 fIconPreview16Menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR)); 1006 1007 fIconPreview32Folder = new IconView(BRect(0, 0, 31, 31), 1008 "icon preview 32 folder"); 1009 fIconPreview32Desktop = new IconView(BRect(0, 0, 31, 31), 1010 "icon preview 32 desktop"); 1011 fIconPreview32Desktop->SetLowColor(ui_color(B_DESKTOP_COLOR)); 1012 1013 fIconPreview64 = new IconView(BRect(0, 0, 63, 63), "icon preview 64"); 1014 fIconPreview64->SetLowColor(ui_color(B_DESKTOP_COLOR)); 1015 1016 1017 BGroupView* smallPreviews = new BGroupView(B_VERTICAL); 1018 smallPreviews->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1019 smallPreviews->GroupLayout()->SetSpacing(5); 1020 1021 smallPreviews->AddChild(fIconPreview16Folder); 1022 smallPreviews->AddChild(fIconPreview16Menu); 1023 1024 BGroupView* mediumPreviews = new BGroupView(B_VERTICAL); 1025 mediumPreviews->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1026 mediumPreviews->GroupLayout()->SetSpacing(5); 1027 1028 mediumPreviews->AddChild(fIconPreview32Folder); 1029 mediumPreviews->AddChild(fIconPreview32Desktop); 1030 1031 // iconPreviews->AddChild(fIconPreview48); 1032 1033 iconPreviews->AddChild(smallPreviews); 1034 iconPreviews->AddChild(mediumPreviews); 1035 iconPreviews->AddChild(fIconPreview64); 1036 iconPreviews->SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED)); 1037 1038 leftTopView->AddChild(iconPreviews); 1039 1040 1041 BGroupView* leftSideView = new BGroupView(B_VERTICAL, 0); 1042 layout->AddView(leftSideView, 0, 1); 1043 leftSideView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNSET)); 1044 1045 fPathListView = new PathListView(BRect(0, 0, splitWidth, 100), 1046 "path list view", new BMessage(MSG_PATH_SELECTED), this); 1047 fShapeListView = new ShapeListView(BRect(0, 0, splitWidth, 100), 1048 "shape list view", new BMessage(MSG_SHAPE_SELECTED), this); 1049 fTransformerListView = new TransformerListView(BRect(0, 0, splitWidth, 100), 1050 "transformer list view"); 1051 fPropertyListView = new IconObjectListView(); 1052 1053 BLayoutBuilder::Group<>(leftSideView) 1054 .AddGroup(B_VERTICAL, 0) 1055 .SetInsets(-2, -1, -1, -1) 1056 .Add(new BMenuField(NULL, fPathMenu)) 1057 .End() 1058 .Add(new BScrollView("path scroll view", fPathListView, 1059 B_FOLLOW_NONE, 0, false, true, B_NO_BORDER)) 1060 .AddGroup(B_VERTICAL, 0) 1061 .SetInsets(-2, -2, -1, -1) 1062 .Add(new BMenuField(NULL, fShapeMenu)) 1063 .End() 1064 .Add(new BScrollView("shape scroll view", fShapeListView, 1065 B_FOLLOW_NONE, 0, false, true, B_NO_BORDER)) 1066 .AddGroup(B_VERTICAL, 0) 1067 .SetInsets(-2, -2, -1, -1) 1068 .Add(new BMenuField(NULL, fTransformerMenu)) 1069 .End() 1070 .Add(new BScrollView("transformer scroll view", 1071 fTransformerListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER)) 1072 .AddGroup(B_VERTICAL, 0) 1073 .SetInsets(-2, -2, -1, -1) 1074 .Add(new BMenuField(NULL, fPropertyMenu)) 1075 .End() 1076 .Add(new ScrollView(fPropertyListView, SCROLL_VERTICAL, 1077 BRect(0, 0, splitWidth, 100), "property scroll view", 1078 B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER, 1079 BORDER_RIGHT)) 1080 .End(); 1081 1082 BGroupLayout* topSide = new BGroupLayout(B_HORIZONTAL); 1083 topSide->SetSpacing(0); 1084 BView* topSideView = new BView("top side view", 0, topSide); 1085 layout->AddView(topSideView, 1, 0); 1086 1087 // canvas view 1088 BRect canvasBounds = BRect(0, 0, 200, 200); 1089 fCanvasView = new CanvasView(canvasBounds); 1090 1091 // scroll view around canvas view 1092 canvasBounds.bottom += B_H_SCROLL_BAR_HEIGHT; 1093 canvasBounds.right += B_V_SCROLL_BAR_WIDTH; 1094 ScrollView* canvasScrollView = new ScrollView(fCanvasView, SCROLL_VERTICAL 1095 | SCROLL_HORIZONTAL | SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS, 1096 canvasBounds, "canvas scroll view", B_FOLLOW_NONE, 1097 B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER); 1098 layout->AddView(canvasScrollView, 1, 1); 1099 1100 // views along the top 1101 1102 BGroupView* styleGroupView = new BGroupView(B_VERTICAL, 0); 1103 topSide->AddView(styleGroupView); 1104 1105 fStyleListView = new StyleListView(BRect(0, 0, splitWidth, 100), 1106 "style list view", new BMessage(MSG_STYLE_SELECTED), this); 1107 1108 BScrollView* scrollView = new BScrollView("style list scroll view", 1109 fStyleListView, B_FOLLOW_NONE, 0, false, true, B_NO_BORDER); 1110 scrollView->SetExplicitMaxSize(BSize(splitWidth, B_SIZE_UNLIMITED)); 1111 1112 BLayoutBuilder::Group<>(styleGroupView) 1113 .AddGroup(B_VERTICAL, 0) 1114 .SetInsets(-2, -2, -1, -1) 1115 .Add(new BMenuField(NULL, fStyleMenu)) 1116 .End() 1117 .Add(scrollView) 1118 .End(); 1119 1120 // style view 1121 fStyleView = new StyleView(BRect(0, 0, 200, 100)); 1122 topSide->AddView(fStyleView); 1123 1124 // swatch group 1125 BGroupLayout* swatchGroup = new BGroupLayout(B_VERTICAL); 1126 swatchGroup->SetSpacing(0); 1127 BView* swatchGroupView = new BView("swatch group", 0, swatchGroup); 1128 topSide->AddView(swatchGroupView); 1129 1130 BMenuBar* menuBar = new BMenuBar("swatches menu bar"); 1131 menuBar->AddItem(fSwatchMenu); 1132 swatchGroup->AddView(menuBar); 1133 1134 fSwatchGroup = new SwatchGroup(BRect(0, 0, 100, 100)); 1135 swatchGroup->AddView(fSwatchGroup); 1136 1137 swatchGroupView->SetExplicitMaxSize(swatchGroupView->MinSize()); 1138 1139 // make sure the top side has fixed height 1140 topSideView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1141 swatchGroupView->MinSize().height)); 1142 } 1143 1144 1145 BMenuBar* 1146 MainWindow::_CreateMenuBar() 1147 { 1148 BMenuBar* menuBar = new BMenuBar("main menu"); 1149 1150 1151 #undef B_TRANSLATION_CONTEXT 1152 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menus" 1153 1154 1155 BMenu* fileMenu = new BMenu(B_TRANSLATE("File")); 1156 BMenu* editMenu = new BMenu(B_TRANSLATE("Edit")); 1157 BMenu* settingsMenu = new BMenu(B_TRANSLATE("Settings")); 1158 fPathMenu = new BMenu(B_TRANSLATE("Path")); 1159 fStyleMenu = new BMenu(B_TRANSLATE("Style")); 1160 fShapeMenu = new BMenu(B_TRANSLATE("Shape")); 1161 fTransformerMenu = new BMenu(B_TRANSLATE("Transformer")); 1162 fPropertyMenu = new BMenu(B_TRANSLATE("Properties")); 1163 fSwatchMenu = new BMenu(B_TRANSLATE("Swatches")); 1164 1165 menuBar->AddItem(fileMenu); 1166 menuBar->AddItem(editMenu); 1167 menuBar->AddItem(settingsMenu); 1168 1169 1170 // File 1171 #undef B_TRANSLATION_CONTEXT 1172 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-File" 1173 1174 1175 BMenuItem* item = new BMenuItem(B_TRANSLATE("New"), 1176 new BMessage(MSG_NEW), 'N'); 1177 fileMenu->AddItem(item); 1178 item->SetTarget(be_app); 1179 item = new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS), 1180 new BMessage(MSG_OPEN), 'O'); 1181 fileMenu->AddItem(item); 1182 BMessage* appendMessage = new BMessage(MSG_APPEND); 1183 appendMessage->AddPointer("window", this); 1184 item = new BMenuItem(B_TRANSLATE("Append" B_UTF8_ELLIPSIS), 1185 appendMessage, 'O', B_SHIFT_KEY); 1186 fileMenu->AddItem(item); 1187 item->SetTarget(be_app); 1188 fileMenu->AddSeparatorItem(); 1189 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save"), 1190 new BMessage(MSG_SAVE), 'S')); 1191 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), 1192 new BMessage(MSG_SAVE_AS), 'S', B_SHIFT_KEY)); 1193 fileMenu->AddSeparatorItem(); 1194 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Export"), 1195 new BMessage(MSG_EXPORT), 'P')); 1196 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Export as" B_UTF8_ELLIPSIS), 1197 new BMessage(MSG_EXPORT_AS), 'P', B_SHIFT_KEY)); 1198 fileMenu->AddSeparatorItem(); 1199 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 1200 new BMessage(B_QUIT_REQUESTED), 'W')); 1201 item = new BMenuItem(B_TRANSLATE("Quit"), 1202 new BMessage(B_QUIT_REQUESTED), 'Q'); 1203 fileMenu->AddItem(item); 1204 item->SetTarget(be_app); 1205 1206 // Edit 1207 #undef B_TRANSLATION_CONTEXT 1208 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-Edit" 1209 1210 1211 fUndoMI = new BMenuItem(B_TRANSLATE("<nothing to undo>"), 1212 new BMessage(MSG_UNDO), 'Z'); 1213 fRedoMI = new BMenuItem(B_TRANSLATE("<nothing to redo>"), 1214 new BMessage(MSG_REDO), 'Z', B_SHIFT_KEY); 1215 1216 fUndoMI->SetEnabled(false); 1217 fRedoMI->SetEnabled(false); 1218 1219 editMenu->AddItem(fUndoMI); 1220 editMenu->AddItem(fRedoMI); 1221 1222 1223 // Settings 1224 #undef B_TRANSLATION_CONTEXT 1225 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-Settings" 1226 1227 1228 BMenu* filterModeMenu = new BMenu(B_TRANSLATE("Snap to grid")); 1229 BMessage* message = new BMessage(MSG_MOUSE_FILTER_MODE); 1230 message->AddInt32("mode", SNAPPING_OFF); 1231 fMouseFilterOffMI = new BMenuItem(B_TRANSLATE("Off"), message, '4'); 1232 filterModeMenu->AddItem(fMouseFilterOffMI); 1233 1234 message = new BMessage(MSG_MOUSE_FILTER_MODE); 1235 message->AddInt32("mode", SNAPPING_64); 1236 fMouseFilter64MI = new BMenuItem("64 x 64", message, '3'); 1237 filterModeMenu->AddItem(fMouseFilter64MI); 1238 1239 message = new BMessage(MSG_MOUSE_FILTER_MODE); 1240 message->AddInt32("mode", SNAPPING_32); 1241 fMouseFilter32MI = new BMenuItem("32 x 32", message, '2'); 1242 filterModeMenu->AddItem(fMouseFilter32MI); 1243 1244 message = new BMessage(MSG_MOUSE_FILTER_MODE); 1245 message->AddInt32("mode", SNAPPING_16); 1246 fMouseFilter16MI = new BMenuItem("16 x 16", message, '1'); 1247 filterModeMenu->AddItem(fMouseFilter16MI); 1248 1249 filterModeMenu->SetRadioMode(true); 1250 1251 settingsMenu->AddItem(filterModeMenu); 1252 1253 return menuBar; 1254 } 1255 1256 1257 void 1258 MainWindow::_ImproveScrollBarLayout(BView* target) 1259 { 1260 // NOTE: The BListViews for which this function is used 1261 // are directly below a BMenuBar. If the BScrollBar and 1262 // the BMenuBar share bottom/top border respectively, the 1263 // GUI looks a little more polished. This trick can be 1264 // removed if/when the BScrollViews are embedded in a 1265 // surounding border like in WonderBrush. 1266 1267 if (BScrollBar* scrollBar = target->ScrollBar(B_VERTICAL)) { 1268 scrollBar->MoveBy(0, -1); 1269 scrollBar->ResizeBy(0, 1); 1270 } 1271 } 1272 1273 1274 // #pragma mark - 1275 1276 1277 bool 1278 MainWindow::_CheckSaveIcon(const BMessage* currentMessage) 1279 { 1280 if (fDocument->IsEmpty() || fDocument->CommandStack()->IsSaved()) 1281 return true; 1282 1283 // Make sure the user sees us. 1284 Activate(); 1285 1286 BAlert* alert = new BAlert("save", 1287 B_TRANSLATE("Save changes to current icon before closing?"), 1288 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), 1289 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1290 B_WARNING_ALERT); 1291 alert->SetShortcut(0, B_ESCAPE); 1292 alert->SetShortcut(1, 'd'); 1293 alert->SetShortcut(2, 's'); 1294 int32 choice = alert->Go(); 1295 switch (choice) { 1296 case 0: 1297 // cancel 1298 return false; 1299 case 1: 1300 // don't save 1301 return true; 1302 case 2: 1303 default: 1304 // cancel (save first) but pick up what we were doing before 1305 PostMessage(MSG_SAVE); 1306 if (currentMessage != NULL) { 1307 delete fMessageAfterSave; 1308 fMessageAfterSave = new BMessage(*currentMessage); 1309 } 1310 return false; 1311 } 1312 } 1313 1314 1315 void 1316 MainWindow::_PickUpActionBeforeSave() 1317 { 1318 if (fDocument->WriteLock()) { 1319 fDocument->CommandStack()->Save(); 1320 fDocument->WriteUnlock(); 1321 } 1322 1323 if (fMessageAfterSave == NULL) 1324 return; 1325 1326 PostMessage(fMessageAfterSave); 1327 delete fMessageAfterSave; 1328 fMessageAfterSave = NULL; 1329 } 1330 1331 1332 // #pragma mark - 1333 1334 1335 void 1336 MainWindow::_MakeIconEmpty() 1337 { 1338 if (!_CheckSaveIcon(CurrentMessage())) 1339 return; 1340 1341 AutoWriteLocker locker(fDocument); 1342 1343 MakeEmpty(); 1344 fDocument->MakeEmpty(); 1345 1346 locker.Unlock(); 1347 } 1348 1349 1350 DocumentSaver* 1351 MainWindow::_CreateSaver(const entry_ref& ref, uint32 exportMode) 1352 { 1353 DocumentSaver* saver; 1354 1355 switch (exportMode) { 1356 case EXPORT_MODE_FLAT_ICON: 1357 saver = new SimpleFileSaver(new FlatIconExporter(), ref); 1358 break; 1359 1360 case EXPORT_MODE_ICON_ATTR: 1361 case EXPORT_MODE_ICON_MIME_ATTR: { 1362 const char* attrName 1363 = exportMode == EXPORT_MODE_ICON_ATTR ? 1364 kVectorAttrNodeName : kVectorAttrMimeName; 1365 saver = new AttributeSaver(ref, attrName); 1366 break; 1367 } 1368 1369 case EXPORT_MODE_ICON_RDEF: 1370 saver = new SimpleFileSaver(new RDefExporter(), ref); 1371 break; 1372 case EXPORT_MODE_ICON_SOURCE: 1373 saver = new SimpleFileSaver(new SourceExporter(), ref); 1374 break; 1375 1376 case EXPORT_MODE_BITMAP_16: 1377 saver = new SimpleFileSaver(new BitmapExporter(16), ref); 1378 break; 1379 case EXPORT_MODE_BITMAP_32: 1380 saver = new SimpleFileSaver(new BitmapExporter(32), ref); 1381 break; 1382 case EXPORT_MODE_BITMAP_64: 1383 saver = new SimpleFileSaver(new BitmapExporter(64), ref); 1384 break; 1385 1386 case EXPORT_MODE_BITMAP_SET: 1387 saver = new BitmapSetSaver(ref); 1388 break; 1389 1390 case EXPORT_MODE_SVG: 1391 saver = new SimpleFileSaver(new SVGExporter(), ref); 1392 break; 1393 1394 case EXPORT_MODE_MESSAGE: 1395 default: 1396 saver = new NativeSaver(ref); 1397 break; 1398 } 1399 1400 return saver; 1401 } 1402 1403 1404 const char* 1405 MainWindow::_FileName(bool preferExporter) const 1406 { 1407 FileSaver* saver1; 1408 FileSaver* saver2; 1409 if (preferExporter) { 1410 saver1 = dynamic_cast<FileSaver*>(fDocument->ExportSaver()); 1411 saver2 = dynamic_cast<FileSaver*>(fDocument->NativeSaver()); 1412 } else { 1413 saver1 = dynamic_cast<FileSaver*>(fDocument->NativeSaver()); 1414 saver2 = dynamic_cast<FileSaver*>(fDocument->ExportSaver()); 1415 } 1416 const char* fileName = NULL; 1417 if (saver1 != NULL) 1418 fileName = saver1->Ref()->name; 1419 if ((fileName == NULL || fileName[0] == '\0') && saver2 != NULL) 1420 fileName = saver2->Ref()->name; 1421 return fileName; 1422 } 1423 1424 1425 void 1426 MainWindow::_UpdateWindowTitle() 1427 { 1428 const char* fileName = _FileName(false); 1429 if (fileName != NULL) 1430 SetTitle(fileName); 1431 else 1432 SetTitle(B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic")); 1433 } 1434 1435