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