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 "ShapeListView.h" 87 #include "StrokeTransformer.h" 88 #include "Style.h" 89 #include "VectorPath.h" 90 91 #include "StyledTextImporter.h" 92 93 94 #undef B_TRANSLATION_CONTEXT 95 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Main" 96 97 98 using std::nothrow; 99 100 enum { 101 MSG_UNDO = 'undo', 102 MSG_REDO = 'redo', 103 MSG_UNDO_STACK_CHANGED = 'usch', 104 105 MSG_PATH_SELECTED = 'vpsl', 106 MSG_STYLE_SELECTED = 'stsl', 107 MSG_SHAPE_SELECTED = 'spsl', 108 109 MSG_SHAPE_RESET_TRANSFORMATION = 'rtsh', 110 MSG_STYLE_RESET_TRANSFORMATION = 'rtst', 111 112 MSG_MOUSE_FILTER_MODE = 'mfmd', 113 114 MSG_RENAME_OBJECT = 'rnam', 115 }; 116 117 118 MainWindow::MainWindow(BRect frame, IconEditorApp* app, 119 const BMessage* settings) 120 : 121 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"), 122 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 123 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 124 fApp(app), 125 fDocument(new Document(B_TRANSLATE("Untitled"))), 126 fCurrentColor(new CurrentColor()), 127 fIcon(NULL), 128 fMessageAfterSave(NULL) 129 { 130 _Init(); 131 132 RestoreSettings(settings); 133 } 134 135 136 MainWindow::~MainWindow() 137 { 138 SetIcon(NULL); 139 140 delete fState; 141 142 // Make sure there are no listeners attached to the document anymore. 143 while (BView* child = ChildAt(0L)) { 144 child->RemoveSelf(); 145 delete child; 146 } 147 148 fDocument->CommandStack()->RemoveObserver(this); 149 150 // NOTE: it is important that the GUI has been deleted 151 // at this point, so that all the listener/observer 152 // stuff is properly detached 153 delete fDocument; 154 155 delete fCurrentColor; 156 delete fMessageAfterSave; 157 } 158 159 160 // #pragma mark - 161 162 163 void 164 MainWindow::MessageReceived(BMessage* message) 165 { 166 bool discard = false; 167 168 // Figure out if we need the write lock on the Document. For most 169 // messages we do, but exporting takes place in another thread and 170 // locking is taken care of there. 171 bool requiresWriteLock = true; 172 switch (message->what) { 173 case MSG_SAVE: 174 case MSG_EXPORT: 175 case MSG_SAVE_AS: 176 case MSG_EXPORT_AS: 177 requiresWriteLock = false; 178 break; 179 default: 180 break; 181 } 182 if (requiresWriteLock && !fDocument->WriteLock()) { 183 BWindow::MessageReceived(message); 184 return; 185 } 186 187 if (message->WasDropped()) { 188 const rgb_color* color; 189 ssize_t length; 190 // create styles from dropped colors 191 for (int32 i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i, 192 (const void**)&color, &length) == B_OK; i++) { 193 if (length != sizeof(rgb_color)) 194 continue; 195 char name[30]; 196 sprintf(name, 197 B_TRANSLATE_COMMENT("Color (#%02x%02x%02x)", 198 "Style name after dropping a color"), 199 color->red, color->green, color->blue); 200 Style* style = new (nothrow) Style(*color); 201 style->SetName(name); 202 Style* styles[1] = { style }; 203 AddCommand<Style>* styleCommand = new (nothrow) AddCommand<Style>( 204 fDocument->Icon()->Styles(), styles, 1, true, 205 fDocument->Icon()->Styles()->CountItems()); 206 fDocument->CommandStack()->Perform(styleCommand); 207 // don't handle anything else, 208 // or we might paste the clipboard on B_PASTE 209 discard = true; 210 } 211 } 212 213 switch (message->what) { 214 215 case B_REFS_RECEIVED: 216 case B_SIMPLE_DATA: 217 { 218 entry_ref ref; 219 if (message->FindRef("refs", &ref) != B_OK) 220 break; 221 222 // Check if this is best represented by a ReferenceImage 223 BMimeType type; 224 if (BMimeType::GuessMimeType(&ref, &type) == B_OK) { 225 BMimeType superType; 226 if (type.GetSupertype(&superType) == B_OK 227 && superType == BMimeType("image") 228 && !(type == BMimeType("image/svg+xml")) 229 && !(type == BMimeType("image/x-hvif"))) { 230 AddReferenceImage(ref); 231 break; 232 } 233 } 234 235 // If our icon is empty, open the file in this window, 236 // otherwise forward to the application which will open 237 // it in another window, unless we append. 238 message->what = B_REFS_RECEIVED; 239 if (fDocument->Icon()->Styles()->CountItems() == 0 240 && fDocument->Icon()->Paths()->CountItems() == 0 241 && fDocument->Icon()->Shapes()->CountItems() == 0) { 242 Open(ref); 243 break; 244 } 245 if (modifiers() & B_SHIFT_KEY) { 246 // We want the icon appended to this window. 247 message->AddBool("append", true); 248 message->AddPointer("window", this); 249 } 250 be_app->PostMessage(message); 251 break; 252 } 253 254 case B_PASTE: 255 case B_MIME_DATA: 256 { 257 BMessage* clip = message; 258 status_t err; 259 260 if (discard) 261 break; 262 263 if (message->what == B_PASTE) { 264 if (!be_clipboard->Lock()) 265 break; 266 clip = be_clipboard->Data(); 267 } 268 269 if (!clip || !clip->HasData("text/plain", B_MIME_TYPE)) { 270 if (message->what == B_PASTE) 271 be_clipboard->Unlock(); 272 break; 273 } 274 275 Icon* icon = new (std::nothrow) Icon(*fDocument->Icon()); 276 if (icon != NULL) { 277 StyledTextImporter importer; 278 err = importer.Import(icon, clip); 279 if (err >= B_OK) { 280 AutoWriteLocker locker(fDocument); 281 282 SetIcon(NULL); 283 284 // incorporate the loaded icon into the document 285 // (either replace it or append to it) 286 fDocument->MakeEmpty(false); 287 // if append, the document savers are preserved 288 fDocument->SetIcon(icon); 289 SetIcon(icon); 290 } 291 } 292 293 if (message->what == B_PASTE) 294 be_clipboard->Unlock(); 295 break; 296 } 297 298 case MSG_OPEN: 299 { 300 // If our icon is empty, we want the icon to open in this 301 // window. 302 bool emptyDocument = fDocument->Icon()->Styles()->CountItems() == 0 303 && fDocument->Icon()->Paths()->CountItems() == 0 304 && fDocument->Icon()->Shapes()->CountItems() == 0; 305 306 bool openingReferenceImage; 307 if (message->FindBool("reference image", &openingReferenceImage) != B_OK) 308 openingReferenceImage = false; 309 310 if (emptyDocument || openingReferenceImage) 311 message->AddPointer("window", this); 312 313 be_app->PostMessage(message); 314 break; 315 } 316 317 case MSG_SAVE: 318 case MSG_EXPORT: 319 { 320 DocumentSaver* saver; 321 if (message->what == MSG_SAVE) 322 saver = fDocument->NativeSaver(); 323 else 324 saver = fDocument->ExportSaver(); 325 if (saver != NULL) { 326 saver->Save(fDocument); 327 _PickUpActionBeforeSave(); 328 break; 329 } // else fall through 330 } 331 case MSG_SAVE_AS: 332 case MSG_EXPORT_AS: 333 { 334 int32 exportMode; 335 if (message->FindInt32("export mode", &exportMode) < B_OK) 336 exportMode = EXPORT_MODE_MESSAGE; 337 entry_ref ref; 338 const char* name; 339 if (message->FindRef("directory", &ref) == B_OK 340 && message->FindString("name", &name) == B_OK) { 341 // this message comes from the file panel 342 BDirectory dir(&ref); 343 BEntry entry; 344 if (dir.InitCheck() >= B_OK 345 && entry.SetTo(&dir, name, true) >= B_OK 346 && entry.GetRef(&ref) >= B_OK) { 347 348 // create the document saver and remember it for later 349 DocumentSaver* saver = _CreateSaver(ref, exportMode); 350 if (saver != NULL) { 351 if (fDocument->WriteLock()) { 352 if (exportMode == EXPORT_MODE_MESSAGE) 353 fDocument->SetNativeSaver(saver); 354 else 355 fDocument->SetExportSaver(saver); 356 _UpdateWindowTitle(); 357 fDocument->WriteUnlock(); 358 } 359 saver->Save(fDocument); 360 _PickUpActionBeforeSave(); 361 } 362 } 363 // TODO: ... 364 // _SyncPanels(fSavePanel, fOpenPanel); 365 } else { 366 // configure the file panel 367 uint32 requestRefWhat = MSG_SAVE_AS; 368 bool isExportMode = message->what == MSG_EXPORT_AS 369 || message->what == MSG_EXPORT; 370 if (isExportMode) 371 requestRefWhat = MSG_EXPORT_AS; 372 const char* saveText = _FileName(isExportMode); 373 374 BMessage requestRef(requestRefWhat); 375 if (saveText != NULL) 376 requestRef.AddString("save text", saveText); 377 requestRef.AddMessenger("target", BMessenger(this, this)); 378 be_app->PostMessage(&requestRef); 379 } 380 break; 381 } 382 case B_CANCEL: 383 // FilePanel was canceled, do not execute the fMessageAfterSave 384 // next time a file panel is used, in case it was set! 385 delete fMessageAfterSave; 386 fMessageAfterSave = NULL; 387 break; 388 389 case MSG_UNDO: 390 fDocument->CommandStack()->Undo(); 391 break; 392 case MSG_REDO: 393 fDocument->CommandStack()->Redo(); 394 break; 395 case MSG_UNDO_STACK_CHANGED: 396 { 397 // relable Undo item and update enabled status 398 BString label(B_TRANSLATE("Undo: %action%")); 399 BString temp; 400 fUndoMI->SetEnabled(fDocument->CommandStack()->GetUndoName(temp)); 401 label.ReplaceFirst("%action%", temp); 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: %action%")); 411 temp.SetTo(""); 412 fRedoMI->SetEnabled(fDocument->CommandStack()->GetRedoName(temp)); 413 label.ReplaceFirst("%action%", temp); 414 if (fRedoMI->IsEnabled()) 415 fRedoMI->SetLabel(label.String()); 416 else { 417 fRedoMI->SetLabel(B_TRANSLATE_CONTEXT("<nothing to redo>", 418 "Icon-O-Matic-Menu-Edit")); 419 } 420 break; 421 } 422 423 case MSG_MOUSE_FILTER_MODE: 424 { 425 uint32 mode; 426 if (message->FindInt32("mode", (int32*)&mode) == B_OK) 427 fCanvasView->SetMouseFilterMode(mode); 428 break; 429 } 430 431 case MSG_ADD_SHAPE: { 432 AddStylesCommand* styleCommand = NULL; 433 Style* style = NULL; 434 if (message->HasBool("style")) { 435 new_style(fCurrentColor->Color(), 436 fDocument->Icon()->Styles(), &style, &styleCommand); 437 } 438 439 AddPathsCommand* pathCommand = NULL; 440 VectorPath* path = NULL; 441 if (message->HasBool("path")) { 442 new_path(fDocument->Icon()->Paths(), &path, &pathCommand); 443 } 444 445 if (!style) { 446 // use current or first style 447 int32 currentStyle = fStyleListView->CurrentSelection(0); 448 style = fDocument->Icon()->Styles()->ItemAt(currentStyle); 449 if (!style) 450 style = fDocument->Icon()->Styles()->ItemAt(0); 451 } 452 453 PathSourceShape* shape = new (nothrow) PathSourceShape(style); 454 AddShapesCommand* shapeCommand = new (nothrow) AddShapesCommand( 455 fDocument->Icon()->Shapes(), (Shape**) &shape, 1, 456 fDocument->Icon()->Shapes()->CountItems()); 457 458 if (path && shape) 459 shape->Paths()->AddItem(path); 460 461 ::Command* command = NULL; 462 if (styleCommand || pathCommand) { 463 if (styleCommand && pathCommand) { 464 Command** commands = new Command*[3]; 465 commands[0] = styleCommand; 466 commands[1] = pathCommand; 467 commands[2] = shapeCommand; 468 command = new CompoundCommand(commands, 3, 469 B_TRANSLATE_CONTEXT("Add shape with path & style", 470 "Icon-O-Matic-Menu-Shape"), 471 0); 472 } else if (styleCommand) { 473 Command** commands = new Command*[2]; 474 commands[0] = styleCommand; 475 commands[1] = shapeCommand; 476 command = new CompoundCommand(commands, 2, 477 B_TRANSLATE_CONTEXT("Add shape with style", 478 "Icon-O-Matic-Menu-Shape"), 479 0); 480 } else { 481 Command** commands = new Command*[2]; 482 commands[0] = pathCommand; 483 commands[1] = shapeCommand; 484 command = new CompoundCommand(commands, 2, 485 B_TRANSLATE_CONTEXT("Add shape with path", 486 "Icon-O-Matic-Menu-Shape"), 487 0); 488 } 489 } else { 490 command = shapeCommand; 491 } 492 fDocument->CommandStack()->Perform(command); 493 break; 494 } 495 496 // TODO: listen to selection in CanvasView to add a manipulator 497 case MSG_PATH_SELECTED: { 498 VectorPath* path; 499 if (message->FindPointer("path", (void**)&path) < B_OK) 500 path = NULL; 501 502 fPathListView->SetCurrentShape(NULL); 503 fStyleListView->SetCurrentShape(NULL); 504 fTransformerListView->SetShape(NULL); 505 506 fState->DeleteManipulators(); 507 if (fDocument->Icon()->Paths()->HasItem(path)) { 508 PathManipulator* pathManipulator = new (nothrow) PathManipulator(path); 509 fState->AddManipulator(pathManipulator); 510 } 511 break; 512 } 513 case MSG_STYLE_SELECTED: 514 case MSG_STYLE_TYPE_CHANGED: { 515 Style* style; 516 if (message->FindPointer("style", (void**)&style) < B_OK) 517 style = NULL; 518 if (!fDocument->Icon()->Styles()->HasItem(style)) 519 style = NULL; 520 521 fStyleView->SetStyle(style); 522 fPathListView->SetCurrentShape(NULL); 523 fStyleListView->SetCurrentShape(NULL); 524 fTransformerListView->SetShape(NULL); 525 526 fState->DeleteManipulators(); 527 Gradient* gradient = style ? style->Gradient() : NULL; 528 if (gradient != NULL) { 529 TransformGradientBox* transformBox 530 = new (nothrow) TransformGradientBox(fCanvasView, gradient, NULL); 531 fState->AddManipulator(transformBox); 532 } 533 break; 534 } 535 case MSG_SHAPE_SELECTED: { 536 Shape* shape; 537 if (message->FindPointer("shape", (void**)&shape) < B_OK) 538 shape = NULL; 539 if (!fIcon || !fIcon->Shapes()->HasItem(shape)) 540 shape = NULL; 541 542 fPathListView->SetCurrentShape(shape); 543 fStyleListView->SetCurrentShape(shape); 544 fTransformerListView->SetShape(shape); 545 546 BList selectedShapes; 547 Container<Shape>* shapes = fDocument->Icon()->Shapes(); 548 int32 count = shapes->CountItems(); 549 for (int32 i = 0; i < count; i++) { 550 shape = shapes->ItemAtFast(i); 551 if (shape->IsSelected()) { 552 selectedShapes.AddItem((void*)shape); 553 } 554 } 555 556 fState->DeleteManipulators(); 557 if (selectedShapes.CountItems() > 0) { 558 TransformShapesBox* transformBox = new (nothrow) TransformShapesBox( 559 fCanvasView, 560 (const Shape**)selectedShapes.Items(), 561 selectedShapes.CountItems()); 562 fState->AddManipulator(transformBox); 563 } 564 break; 565 } 566 case MSG_RENAME_OBJECT: 567 fPropertyListView->FocusNameProperty(); 568 break; 569 570 default: 571 BWindow::MessageReceived(message); 572 } 573 574 if (requiresWriteLock) 575 fDocument->WriteUnlock(); 576 } 577 578 579 void 580 MainWindow::Show() 581 { 582 BWindow::Show(); 583 BMenuBar* bar = static_cast<BMenuBar*>(FindView("main menu")); 584 SetKeyMenuBar(bar); 585 } 586 587 588 bool 589 MainWindow::QuitRequested() 590 { 591 if (!_CheckSaveIcon(CurrentMessage())) 592 return false; 593 594 BMessage message(MSG_WINDOW_CLOSED); 595 596 BMessage settings; 597 StoreSettings(&settings); 598 message.AddMessage("settings", &settings); 599 message.AddRect("window frame", Frame()); 600 601 be_app->PostMessage(&message); 602 603 return true; 604 } 605 606 607 void 608 MainWindow::WorkspaceActivated(int32 workspace, bool active) 609 { 610 BWindow::WorkspaceActivated(workspace, active); 611 612 if (active) 613 _WorkspaceEntered(); 614 } 615 616 617 void 618 MainWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 619 { 620 BWindow::WorkspacesChanged(oldWorkspaces, newWorkspaces); 621 622 if((1 << current_workspace() & newWorkspaces) != 0) 623 _WorkspaceEntered(); 624 } 625 626 627 // #pragma mark - 628 629 630 void 631 MainWindow::ObjectChanged(const Observable* object) 632 { 633 if (!fDocument || !fDocument->ReadLock()) 634 return; 635 636 if (object == fDocument->CommandStack()) 637 PostMessage(MSG_UNDO_STACK_CHANGED); 638 639 fDocument->ReadUnlock(); 640 } 641 642 643 // #pragma mark - 644 645 646 void 647 MainWindow::MakeEmpty() 648 { 649 fPathListView->SetCurrentShape(NULL); 650 fStyleListView->SetCurrentShape(NULL); 651 fStyleView->SetStyle(NULL); 652 653 fTransformerListView->SetShape(NULL); 654 655 fState->DeleteManipulators(); 656 } 657 658 659 void 660 MainWindow::Open(const entry_ref& ref, bool append) 661 { 662 BFile file(&ref, B_READ_ONLY); 663 if (file.InitCheck() < B_OK) 664 return; 665 666 Icon* icon; 667 if (append) 668 icon = new (nothrow) Icon(*fDocument->Icon()); 669 else 670 icon = new (nothrow) Icon(); 671 672 if (icon == NULL) { 673 // TODO: Report error to user. 674 return; 675 } 676 677 enum { 678 REF_NONE = 0, 679 REF_MESSAGE, 680 REF_FLAT, 681 REF_FLAT_ATTR, 682 REF_SVG 683 }; 684 uint32 refMode = REF_NONE; 685 686 // try different file types 687 FlatIconImporter flatImporter; 688 status_t ret = flatImporter.Import(icon, &file); 689 if (ret >= B_OK) { 690 refMode = REF_FLAT; 691 } else { 692 file.Seek(0, SEEK_SET); 693 MessageImporter msgImporter; 694 ret = msgImporter.Import(icon, &file); 695 if (ret >= B_OK) { 696 refMode = REF_MESSAGE; 697 } else { 698 file.Seek(0, SEEK_SET); 699 SVGImporter svgImporter; 700 ret = svgImporter.Import(icon, &ref); 701 if (ret >= B_OK) { 702 refMode = REF_SVG; 703 } else { 704 // fall back to flat icon format but use the icon attribute 705 ret = B_OK; 706 attr_info attrInfo; 707 if (file.GetAttrInfo(kVectorAttrNodeName, &attrInfo) == B_OK) { 708 if (attrInfo.type != B_VECTOR_ICON_TYPE) 709 ret = B_ERROR; 710 // If the attribute is there, we must succeed in reading 711 // an icon! Otherwise we may overwrite an existing icon 712 // when the user saves. 713 uint8* buffer = NULL; 714 if (ret == B_OK) { 715 buffer = new(nothrow) uint8[attrInfo.size]; 716 if (buffer == NULL) 717 ret = B_NO_MEMORY; 718 } 719 if (ret == B_OK) { 720 ssize_t bytesRead = file.ReadAttr(kVectorAttrNodeName, 721 B_VECTOR_ICON_TYPE, 0, buffer, attrInfo.size); 722 if (bytesRead != (ssize_t)attrInfo.size) { 723 ret = bytesRead < 0 ? (status_t)bytesRead 724 : B_IO_ERROR; 725 } 726 } 727 if (ret == B_OK) { 728 ret = flatImporter.Import(icon, buffer, attrInfo.size); 729 if (ret == B_OK) 730 refMode = REF_FLAT_ATTR; 731 } 732 733 delete[] buffer; 734 } else { 735 // If there is no icon attribute, simply fall back 736 // to creating an icon for this file. TODO: We may or may 737 // not want to display an alert asking the user if that is 738 // what he wants to do. 739 refMode = REF_FLAT_ATTR; 740 } 741 } 742 } 743 } 744 745 if (ret < B_OK) { 746 // inform user of failure at this point 747 BString helper(B_TRANSLATE("Opening the document failed!")); 748 helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); 749 BAlert* alert = new BAlert( 750 B_TRANSLATE_CONTEXT("Bad news", "Title of error alert"), 751 helper.String(), 752 B_TRANSLATE_COMMENT("Bummer", 753 "Cancel button - error alert"), 754 NULL, NULL); 755 // launch alert asynchronously 756 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 757 alert->Go(NULL); 758 759 delete icon; 760 return; 761 } 762 763 AutoWriteLocker locker(fDocument); 764 765 // incorporate the loaded icon into the document 766 // (either replace it or append to it) 767 fDocument->MakeEmpty(!append); 768 // if append, the document savers are preserved 769 fDocument->SetIcon(icon); 770 if (!append) { 771 // document got replaced, but we have at 772 // least one ref already 773 switch (refMode) { 774 case REF_MESSAGE: 775 fDocument->SetNativeSaver(new NativeSaver(ref)); 776 break; 777 case REF_FLAT: 778 fDocument->SetExportSaver( 779 new SimpleFileSaver(new FlatIconExporter(), ref)); 780 break; 781 case REF_FLAT_ATTR: 782 fDocument->SetNativeSaver( 783 new AttributeSaver(ref, kVectorAttrNodeName)); 784 break; 785 case REF_SVG: 786 fDocument->SetExportSaver( 787 new SimpleFileSaver(new SVGExporter(), ref)); 788 break; 789 } 790 } 791 792 locker.Unlock(); 793 794 SetIcon(icon); 795 796 _UpdateWindowTitle(); 797 } 798 799 800 void 801 MainWindow::Open(const BMessenger& externalObserver, const uint8* data, 802 size_t size) 803 { 804 if (!_CheckSaveIcon(CurrentMessage())) 805 return; 806 807 if (!externalObserver.IsValid()) 808 return; 809 810 Icon* icon = new (nothrow) Icon(); 811 if (!icon) 812 return; 813 814 if (data && size > 0) { 815 // try to open the icon from the provided data 816 FlatIconImporter flatImporter; 817 status_t ret = flatImporter.Import(icon, const_cast<uint8*>(data), 818 size); 819 // NOTE: the const_cast is a bit ugly, but no harm is done 820 // the reason is that the LittleEndianBuffer knows read and write 821 // mode, in this case it is used read-only, and it does not assume 822 // ownership of the buffer 823 824 if (ret < B_OK) { 825 // inform user of failure at this point 826 BString helper(B_TRANSLATE("Opening the icon failed!")); 827 helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); 828 BAlert* alert = new BAlert( 829 B_TRANSLATE_CONTEXT("Bad news", "Title of error alert"), 830 helper.String(), 831 B_TRANSLATE_COMMENT("Bummer", 832 "Cancel button - error alert"), 833 NULL, NULL); 834 // launch alert asynchronously 835 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 836 alert->Go(NULL); 837 838 delete icon; 839 return; 840 } 841 } 842 843 AutoWriteLocker locker(fDocument); 844 845 SetIcon(NULL); 846 847 // incorporate the loaded icon into the document 848 // (either replace it or append to it) 849 fDocument->MakeEmpty(); 850 fDocument->SetIcon(icon); 851 852 fDocument->SetNativeSaver(new MessengerSaver(externalObserver)); 853 854 locker.Unlock(); 855 856 SetIcon(icon); 857 } 858 859 860 void 861 MainWindow::AddReferenceImage(const entry_ref& ref) 862 { 863 BBitmap* image = BTranslationUtils::GetBitmap(&ref); 864 if (image == NULL) 865 return; 866 Shape* shape = new (nothrow) ReferenceImage(image); 867 if (shape == NULL) 868 return; 869 870 AddShapesCommand* shapeCommand = new (nothrow) AddShapesCommand( 871 fDocument->Icon()->Shapes(), &shape, 1, 872 fDocument->Icon()->Shapes()->CountItems()); 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