2 * Copyright 2006-2011, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "MainWindow.h"
13 #include <Clipboard.h>
14 #include <GridLayout.h>
15 #include <GroupLayout.h>
16 #include <GroupView.h>
17 #include <Directory.h>
27 #include <ScrollView.h>
29 #include "support_ui.h"
31 #include "AddPathsCommand.h"
32 #include "AddShapesCommand.h"
33 #include "AddStylesCommand.h"
34 #include "AttributeSaver.h"
35 #include "BitmapExporter.h"
36 #include "BitmapSetSaver.h"
37 #include "CanvasView.h"
38 #include "CommandStack.h"
39 #include "CompoundCommand.h"
40 #include "CurrentColor.h"
42 #include "FlatIconExporter.h"
43 #include "FlatIconFormat.h"
44 #include "FlatIconImporter.h"
45 #include "IconObjectListView.h"
46 #include "IconEditorApp.h"
48 #include "MessageExporter.h"
49 #include "MessageImporter.h"
50 #include "MessengerSaver.h"
51 #include "NativeSaver.h"
52 #include "PathListView.h"
53 #include "RDefExporter.h"
54 #include "ScrollView.h"
55 #include "SimpleFileSaver.h"
56 #include "ShapeListView.h"
57 #include "SourceExporter.h"
58 #include "StyleListView.h"
59 #include "StyleView.h"
60 #include "SVGExporter.h"
61 #include "SVGImporter.h"
62 #include "SwatchGroup.h"
63 #include "TransformerListView.h"
64 #include "TransformGradientBox.h"
65 #include "TransformShapesBox.h"
68 // TODO: just for testing
69 #include "AffineTransformer.h"
70 #include "GradientTransformable.h"
72 #include "MultipleManipulatorState.h"
73 #include "PathManipulator.h"
75 #include "ShapeContainer.h"
76 #include "ShapeListView.h"
77 #include "StrokeTransformer.h"
79 #include "StyleContainer.h"
80 #include "VectorPath.h"
82 #include "StyledTextImporter.h"
85 #undef B_TRANSLATION_CONTEXT
86 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Main"
94 MSG_UNDO_STACK_CHANGED
= 'usch',
96 MSG_PATH_SELECTED
= 'vpsl',
97 MSG_STYLE_SELECTED
= 'stsl',
98 MSG_SHAPE_SELECTED
= 'spsl',
100 MSG_SHAPE_RESET_TRANSFORMATION
= 'rtsh',
101 MSG_STYLE_RESET_TRANSFORMATION
= 'rtst',
103 MSG_MOUSE_FILTER_MODE
= 'mfmd',
105 MSG_RENAME_OBJECT
= 'rnam',
109 MainWindow::MainWindow(BRect frame
, IconEditorApp
* app
,
110 const BMessage
* settings
)
112 BWindow(frame
, B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"),
113 B_DOCUMENT_WINDOW_LOOK
, B_NORMAL_WINDOW_FEEL
,
114 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
116 fDocument(new Document(B_TRANSLATE("Untitled"))),
117 fCurrentColor(new CurrentColor()),
119 fMessageAfterSave(NULL
)
123 RestoreSettings(settings
);
127 MainWindow::~MainWindow()
133 // Make sure there are no listeners attached to the document anymore.
134 while (BView
* child
= ChildAt(0L)) {
139 fDocument
->CommandStack()->RemoveObserver(this);
141 // NOTE: it is important that the GUI has been deleted
142 // at this point, so that all the listener/observer
143 // stuff is properly detached
146 delete fMessageAfterSave
;
154 MainWindow::MessageReceived(BMessage
* message
)
156 bool discard
= false;
158 // Figure out if we need the write lock on the Document. For most
159 // messages we do, but exporting takes place in another thread and
160 // locking is taken care of there.
161 bool requiresWriteLock
= true;
162 switch (message
->what
) {
167 requiresWriteLock
= false;
172 if (requiresWriteLock
&& !fDocument
->WriteLock()) {
173 BWindow::MessageReceived(message
);
177 if (message
->WasDropped()) {
178 const rgb_color
* color
;
180 // create styles from dropped colors
181 for (int32 i
= 0; message
->FindData("RGBColor", B_RGB_COLOR_TYPE
, i
,
182 (const void**)&color
, &length
) == B_OK
; i
++) {
183 if (length
!= sizeof(rgb_color
))
187 B_TRANSLATE_CONTEXT("Color (#%02x%02x%02x)",
188 "Style name after dropping a color"),
189 color
->red
, color
->green
, color
->blue
);
190 Style
* style
= new (nothrow
) Style(*color
);
191 style
->SetName(name
);
192 Style
* styles
[1] = { style
};
193 AddStylesCommand
* styleCommand
= new (nothrow
) AddStylesCommand(
194 fDocument
->Icon()->Styles(), styles
, 1,
195 fDocument
->Icon()->Styles()->CountStyles());
196 fDocument
->CommandStack()->Perform(styleCommand
);
197 // don't handle anything else,
198 // or we might paste the clipboard on B_PASTE
203 switch (message
->what
) {
205 case B_REFS_RECEIVED
:
207 // If our icon is empty, open the file in this window,
208 // otherwise forward to the application which will open
209 // it in another window, unless we append.
210 message
->what
= B_REFS_RECEIVED
;
211 if (fDocument
->Icon()->Styles()->CountStyles() == 0
212 && fDocument
->Icon()->Paths()->CountPaths() == 0
213 && fDocument
->Icon()->Shapes()->CountShapes() == 0) {
215 if (message
->FindRef("refs", &ref
) == B_OK
)
219 if (modifiers() & B_SHIFT_KEY
) {
220 // We want the icon appended to this window.
221 message
->AddBool("append", true);
222 message
->AddPointer("window", this);
224 be_app
->PostMessage(message
);
230 BMessage
* clip
= message
;
236 if (message
->what
== B_PASTE
) {
237 if (!be_clipboard
->Lock())
239 clip
= be_clipboard
->Data();
242 if (!clip
|| !clip
->HasData("text/plain", B_MIME_TYPE
)) {
243 if (message
->what
== B_PASTE
)
244 be_clipboard
->Unlock();
248 Icon
* icon
= new (std::nothrow
) Icon(*fDocument
->Icon());
250 StyledTextImporter importer
;
251 err
= importer
.Import(icon
, clip
);
253 AutoWriteLocker
locker(fDocument
);
257 // incorporate the loaded icon into the document
258 // (either replace it or append to it)
259 fDocument
->MakeEmpty(false);
260 // if append, the document savers are preserved
261 fDocument
->SetIcon(icon
);
266 if (message
->what
== B_PASTE
)
267 be_clipboard
->Unlock();
272 // If our icon is empty, we want the icon to open in this
274 if (fDocument
->Icon()->Styles()->CountStyles() == 0
275 && fDocument
->Icon()->Paths()->CountPaths() == 0
276 && fDocument
->Icon()->Shapes()->CountShapes() == 0) {
277 message
->AddPointer("window", this);
279 be_app
->PostMessage(message
);
285 DocumentSaver
* saver
;
286 if (message
->what
== MSG_SAVE
)
287 saver
= fDocument
->NativeSaver();
289 saver
= fDocument
->ExportSaver();
291 saver
->Save(fDocument
);
292 _PickUpActionBeforeSave();
294 } // else fall through
300 if (message
->FindInt32("export mode", &exportMode
) < B_OK
)
301 exportMode
= EXPORT_MODE_MESSAGE
;
304 if (message
->FindRef("directory", &ref
) == B_OK
305 && message
->FindString("name", &name
) == B_OK
) {
306 // this message comes from the file panel
307 BDirectory
dir(&ref
);
309 if (dir
.InitCheck() >= B_OK
310 && entry
.SetTo(&dir
, name
, true) >= B_OK
311 && entry
.GetRef(&ref
) >= B_OK
) {
313 // create the document saver and remember it for later
314 DocumentSaver
* saver
= _CreateSaver(ref
, exportMode
);
316 if (fDocument
->WriteLock()) {
317 if (exportMode
== EXPORT_MODE_MESSAGE
)
318 fDocument
->SetNativeSaver(saver
);
320 fDocument
->SetExportSaver(saver
);
321 _UpdateWindowTitle();
322 fDocument
->WriteUnlock();
324 saver
->Save(fDocument
);
325 _PickUpActionBeforeSave();
329 // _SyncPanels(fSavePanel, fOpenPanel);
331 // configure the file panel
332 uint32 requestRefWhat
= MSG_SAVE_AS
;
333 bool isExportMode
= message
->what
== MSG_EXPORT_AS
334 || message
->what
== MSG_EXPORT
;
336 requestRefWhat
= MSG_EXPORT_AS
;
337 const char* saveText
= _FileName(isExportMode
);
339 BMessage
requestRef(requestRefWhat
);
340 if (saveText
!= NULL
)
341 requestRef
.AddString("save text", saveText
);
342 requestRef
.AddMessenger("target", BMessenger(this, this));
343 be_app
->PostMessage(&requestRef
);
348 // FilePanel was canceled, do not execute the fMessageAfterSave
349 // next time a file panel is used, in case it was set!
350 delete fMessageAfterSave
;
351 fMessageAfterSave
= NULL
;
355 fDocument
->CommandStack()->Undo();
358 fDocument
->CommandStack()->Redo();
360 case MSG_UNDO_STACK_CHANGED
:
362 // relable Undo item and update enabled status
363 BString
label(B_TRANSLATE("Undo"));
364 fUndoMI
->SetEnabled(fDocument
->CommandStack()->GetUndoName(label
));
365 if (fUndoMI
->IsEnabled())
366 fUndoMI
->SetLabel(label
.String());
368 fUndoMI
->SetLabel(B_TRANSLATE_CONTEXT("<nothing to undo>",
369 "Icon-O-Matic-Menu-Edit"));
372 // relable Redo item and update enabled status
373 label
.SetTo(B_TRANSLATE("Redo"));
374 fRedoMI
->SetEnabled(fDocument
->CommandStack()->GetRedoName(label
));
375 if (fRedoMI
->IsEnabled())
376 fRedoMI
->SetLabel(label
.String());
378 fRedoMI
->SetLabel(B_TRANSLATE_CONTEXT("<nothing to redo>",
379 "Icon-O-Matic-Menu-Edit"));
384 case MSG_MOUSE_FILTER_MODE
:
387 if (message
->FindInt32("mode", (int32
*)&mode
) == B_OK
)
388 fCanvasView
->SetMouseFilterMode(mode
);
392 case MSG_ADD_SHAPE
: {
393 AddStylesCommand
* styleCommand
= NULL
;
395 if (message
->HasBool("style")) {
396 new_style(fCurrentColor
->Color(),
397 fDocument
->Icon()->Styles(), &style
, &styleCommand
);
400 AddPathsCommand
* pathCommand
= NULL
;
401 VectorPath
* path
= NULL
;
402 if (message
->HasBool("path")) {
403 new_path(fDocument
->Icon()->Paths(), &path
, &pathCommand
);
407 // use current or first style
408 int32 currentStyle
= fStyleListView
->CurrentSelection(0);
409 style
= fDocument
->Icon()->Styles()->StyleAt(currentStyle
);
411 style
= fDocument
->Icon()->Styles()->StyleAt(0);
414 Shape
* shape
= new (nothrow
) Shape(style
);
417 AddShapesCommand
* shapeCommand
= new (nothrow
) AddShapesCommand(
418 fDocument
->Icon()->Shapes(), shapes
, 1,
419 fDocument
->Icon()->Shapes()->CountShapes(),
420 fDocument
->Selection());
423 shape
->Paths()->AddPath(path
);
425 ::Command
* command
= NULL
;
426 if (styleCommand
|| pathCommand
) {
427 if (styleCommand
&& pathCommand
) {
428 Command
** commands
= new Command
*[3];
429 commands
[0] = styleCommand
;
430 commands
[1] = pathCommand
;
431 commands
[2] = shapeCommand
;
432 command
= new CompoundCommand(commands
, 3,
433 B_TRANSLATE_CONTEXT("Add shape with path & style",
434 "Icon-O-Matic-Menu-Shape"),
436 } else if (styleCommand
) {
437 Command
** commands
= new Command
*[2];
438 commands
[0] = styleCommand
;
439 commands
[1] = shapeCommand
;
440 command
= new CompoundCommand(commands
, 2,
441 B_TRANSLATE_CONTEXT("Add shape with style",
442 "Icon-O-Matic-Menu-Shape"),
445 Command
** commands
= new Command
*[2];
446 commands
[0] = pathCommand
;
447 commands
[1] = shapeCommand
;
448 command
= new CompoundCommand(commands
, 2,
449 B_TRANSLATE_CONTEXT("Add shape with path",
450 "Icon-O-Matic-Menu-Shape"),
454 command
= shapeCommand
;
456 fDocument
->CommandStack()->Perform(command
);
460 // TODO: listen to selection in CanvasView to add a manipulator
461 case MSG_PATH_SELECTED
: {
463 if (message
->FindPointer("path", (void**)&path
) < B_OK
)
466 fPathListView
->SetCurrentShape(NULL
);
467 fStyleListView
->SetCurrentShape(NULL
);
468 fTransformerListView
->SetShape(NULL
);
470 fState
->DeleteManipulators();
471 if (fDocument
->Icon()->Paths()->HasPath(path
)) {
472 PathManipulator
* pathManipulator
= new (nothrow
) PathManipulator(path
);
473 fState
->AddManipulator(pathManipulator
);
477 case MSG_STYLE_SELECTED
:
478 case MSG_STYLE_TYPE_CHANGED
: {
480 if (message
->FindPointer("style", (void**)&style
) < B_OK
)
482 if (!fDocument
->Icon()->Styles()->HasStyle(style
))
485 fStyleView
->SetStyle(style
);
486 fPathListView
->SetCurrentShape(NULL
);
487 fStyleListView
->SetCurrentShape(NULL
);
488 fTransformerListView
->SetShape(NULL
);
490 fState
->DeleteManipulators();
491 Gradient
* gradient
= style
? style
->Gradient() : NULL
;
492 if (gradient
!= NULL
) {
493 TransformGradientBox
* transformBox
494 = new (nothrow
) TransformGradientBox(fCanvasView
, gradient
, NULL
);
495 fState
->AddManipulator(transformBox
);
499 case MSG_SHAPE_SELECTED
: {
501 if (message
->FindPointer("shape", (void**)&shape
) < B_OK
)
503 if (!fIcon
|| !fIcon
->Shapes()->HasShape(shape
))
506 fPathListView
->SetCurrentShape(shape
);
507 fStyleListView
->SetCurrentShape(shape
);
508 fTransformerListView
->SetShape(shape
);
510 BList selectedShapes
;
511 ShapeContainer
* shapes
= fDocument
->Icon()->Shapes();
512 int32 count
= shapes
->CountShapes();
513 for (int32 i
= 0; i
< count
; i
++) {
514 shape
= shapes
->ShapeAtFast(i
);
515 if (shape
->IsSelected()) {
516 selectedShapes
.AddItem((void*)shape
);
520 fState
->DeleteManipulators();
521 if (selectedShapes
.CountItems() > 0) {
522 TransformShapesBox
* transformBox
= new (nothrow
) TransformShapesBox(
524 (const Shape
**)selectedShapes
.Items(),
525 selectedShapes
.CountItems());
526 fState
->AddManipulator(transformBox
);
530 case MSG_RENAME_OBJECT
:
531 fPropertyListView
->FocusNameProperty();
535 BWindow::MessageReceived(message
);
538 if (requiresWriteLock
)
539 fDocument
->WriteUnlock();
544 MainWindow::QuitRequested()
546 if (!_CheckSaveIcon(CurrentMessage()))
549 BMessage
message(MSG_WINDOW_CLOSED
);
552 StoreSettings(&settings
);
553 message
.AddMessage("settings", &settings
);
554 message
.AddRect("window frame", Frame());
556 be_app
->PostMessage(&message
);
563 MainWindow::WorkspaceActivated(int32 workspace
, bool active
)
565 BWindow::WorkspaceActivated(workspace
, active
);
567 // NOTE: hack to workaround buggy BScreen::DesktopColor() on R5
569 uint32 workspaces
= Workspaces();
570 if (!active
|| ((1 << workspace
) & workspaces
) == 0)
573 WorkspacesChanged(workspaces
, workspaces
);
578 MainWindow::WorkspacesChanged(uint32 oldWorkspaces
, uint32 newWorkspaces
)
580 if (oldWorkspaces
!= newWorkspaces
)
581 BWindow::WorkspacesChanged(oldWorkspaces
, newWorkspaces
);
583 BScreen
screen(this);
585 // Unfortunately, this is buggy on R5: screen.DesktopColor()
586 // as well as ui_color(B_DESKTOP_COLOR) return the color
587 // of the *active* screen, not the one on which this window
588 // is. So this trick only works when you drag this window
589 // from another workspace onto the current workspace, not
590 // when you drag the window from the current workspace onto
591 // another workspace and then switch to the other workspace.
593 fIconPreview32Desktop
->SetIconBGColor(screen
.DesktopColor());
594 fIconPreview64
->SetIconBGColor(screen
.DesktopColor());
602 MainWindow::ObjectChanged(const Observable
* object
)
604 if (!fDocument
|| !fDocument
->ReadLock())
607 if (object
== fDocument
->CommandStack())
608 PostMessage(MSG_UNDO_STACK_CHANGED
);
610 fDocument
->ReadUnlock();
618 MainWindow::MakeEmpty()
620 fPathListView
->SetCurrentShape(NULL
);
621 fStyleListView
->SetCurrentShape(NULL
);
622 fStyleView
->SetStyle(NULL
);
624 fTransformerListView
->SetShape(NULL
);
626 fState
->DeleteManipulators();
631 MainWindow::Open(const entry_ref
& ref
, bool append
)
633 BFile
file(&ref
, B_READ_ONLY
);
634 if (file
.InitCheck() < B_OK
)
639 icon
= new (nothrow
) Icon(*fDocument
->Icon());
641 icon
= new (nothrow
) Icon();
644 // TODO: Report error to user.
655 uint32 refMode
= REF_NONE
;
657 // try different file types
658 FlatIconImporter flatImporter
;
659 status_t ret
= flatImporter
.Import(icon
, &file
);
663 file
.Seek(0, SEEK_SET
);
664 MessageImporter msgImporter
;
665 ret
= msgImporter
.Import(icon
, &file
);
667 refMode
= REF_MESSAGE
;
669 file
.Seek(0, SEEK_SET
);
670 SVGImporter svgImporter
;
671 ret
= svgImporter
.Import(icon
, &ref
);
675 // fall back to flat icon format but use the icon attribute
678 if (file
.GetAttrInfo(kVectorAttrNodeName
, &attrInfo
) == B_OK
) {
679 if (attrInfo
.type
!= B_VECTOR_ICON_TYPE
)
681 // If the attribute is there, we must succeed in reading
682 // an icon! Otherwise we may overwrite an existing icon
683 // when the user saves.
684 uint8
* buffer
= NULL
;
686 buffer
= new(nothrow
) uint8
[attrInfo
.size
];
691 ssize_t bytesRead
= file
.ReadAttr(kVectorAttrNodeName
,
692 B_VECTOR_ICON_TYPE
, 0, buffer
, attrInfo
.size
);
693 if (bytesRead
!= (ssize_t
)attrInfo
.size
) {
694 ret
= bytesRead
< 0 ? (status_t
)bytesRead
699 ret
= flatImporter
.Import(icon
, buffer
, attrInfo
.size
);
701 refMode
= REF_FLAT_ATTR
;
706 // If there is no icon attribute, simply fall back
707 // to creating an icon for this file. TODO: We may or may
708 // not want to display an alert asking the user if that is
709 // what he wants to do.
710 refMode
= REF_FLAT_ATTR
;
717 // inform user of failure at this point
718 BString
helper(B_TRANSLATE("Opening the document failed!"));
719 helper
<< "\n\n" << B_TRANSLATE("Error: ") << strerror(ret
);
720 BAlert
* alert
= new BAlert(
721 B_TRANSLATE_CONTEXT("bad news", "Title of error alert"),
723 B_TRANSLATE_CONTEXT("Bummer",
724 "Cancel button - error alert"),
726 // launch alert asynchronously
727 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
734 AutoWriteLocker
locker(fDocument
);
736 // incorporate the loaded icon into the document
737 // (either replace it or append to it)
738 fDocument
->MakeEmpty(!append
);
739 // if append, the document savers are preserved
740 fDocument
->SetIcon(icon
);
742 // document got replaced, but we have at
743 // least one ref already
746 fDocument
->SetNativeSaver(new NativeSaver(ref
));
749 fDocument
->SetExportSaver(
750 new SimpleFileSaver(new FlatIconExporter(), ref
));
753 fDocument
->SetNativeSaver(
754 new AttributeSaver(ref
, kVectorAttrNodeName
));
757 fDocument
->SetExportSaver(
758 new SimpleFileSaver(new SVGExporter(), ref
));
767 _UpdateWindowTitle();
772 MainWindow::Open(const BMessenger
& externalObserver
, const uint8
* data
,
775 if (!_CheckSaveIcon(CurrentMessage()))
778 if (!externalObserver
.IsValid())
781 Icon
* icon
= new (nothrow
) Icon();
785 if (data
&& size
> 0) {
786 // try to open the icon from the provided data
787 FlatIconImporter flatImporter
;
788 status_t ret
= flatImporter
.Import(icon
, const_cast<uint8
*>(data
),
790 // NOTE: the const_cast is a bit ugly, but no harm is done
791 // the reason is that the LittleEndianBuffer knows read and write
792 // mode, in this case it is used read-only, and it does not assume
793 // ownership of the buffer
796 // inform user of failure at this point
797 BString
helper(B_TRANSLATE("Opening the icon failed!"));
798 helper
<< "\n\n" << B_TRANSLATE("Error: ") << strerror(ret
);
799 BAlert
* alert
= new BAlert(
800 B_TRANSLATE_CONTEXT("bad news", "Title of error alert"),
802 B_TRANSLATE_CONTEXT("Bummer",
803 "Cancel button - error alert"),
805 // launch alert asynchronously
806 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
814 AutoWriteLocker
locker(fDocument
);
818 // incorporate the loaded icon into the document
819 // (either replace it or append to it)
820 fDocument
->MakeEmpty();
821 fDocument
->SetIcon(icon
);
823 fDocument
->SetNativeSaver(new MessengerSaver(externalObserver
));
832 MainWindow::SetIcon(Icon
* icon
)
837 Icon
* oldIcon
= fIcon
;
842 fIcon
->AcquireReference();
846 fCanvasView
->SetIcon(fIcon
);
848 fPathListView
->SetPathContainer(fIcon
!= NULL
? fIcon
->Paths() : NULL
);
849 fPathListView
->SetShapeContainer(fIcon
!= NULL
? fIcon
->Shapes() : NULL
);
851 fStyleListView
->SetStyleContainer(fIcon
!= NULL
? fIcon
->Styles() : NULL
);
852 fStyleListView
->SetShapeContainer(fIcon
!= NULL
? fIcon
->Shapes() : NULL
);
854 fShapeListView
->SetShapeContainer(fIcon
!= NULL
? fIcon
->Shapes() : NULL
);
855 fShapeListView
->SetStyleContainer(fIcon
!= NULL
? fIcon
->Styles() : NULL
);
856 fShapeListView
->SetPathContainer(fIcon
!= NULL
? fIcon
->Paths() : NULL
);
859 fIconPreview16Folder
->SetIcon(fIcon
);
860 fIconPreview16Menu
->SetIcon(fIcon
);
861 fIconPreview32Folder
->SetIcon(fIcon
);
862 fIconPreview32Desktop
->SetIcon(fIcon
);
863 // fIconPreview48->SetIcon(fIcon);
864 fIconPreview64
->SetIcon(fIcon
);
868 oldIcon
->ReleaseReference();
876 MainWindow::StoreSettings(BMessage
* archive
)
878 if (archive
->ReplaceUInt32("mouse filter mode",
879 fCanvasView
->MouseFilterMode()) != B_OK
) {
880 archive
->AddUInt32("mouse filter mode",
881 fCanvasView
->MouseFilterMode());
887 MainWindow::RestoreSettings(const BMessage
* archive
)
889 uint32 mouseFilterMode
;
890 if (archive
->FindUInt32("mouse filter mode", &mouseFilterMode
) == B_OK
) {
891 fCanvasView
->SetMouseFilterMode(mouseFilterMode
);
892 fMouseFilterOffMI
->SetMarked(mouseFilterMode
== SNAPPING_OFF
);
893 fMouseFilter64MI
->SetMarked(mouseFilterMode
== SNAPPING_64
);
894 fMouseFilter32MI
->SetMarked(mouseFilterMode
== SNAPPING_32
);
895 fMouseFilter16MI
->SetMarked(mouseFilterMode
== SNAPPING_16
);
909 // fix up scrollbar layout in listviews
910 _ImproveScrollBarLayout(fPathListView
);
911 _ImproveScrollBarLayout(fStyleListView
);
912 _ImproveScrollBarLayout(fShapeListView
);
913 _ImproveScrollBarLayout(fTransformerListView
);
915 // TODO: move this to CanvasView?
916 fState
= new MultipleManipulatorState(fCanvasView
);
917 fCanvasView
->SetState(fState
);
919 fCanvasView
->SetCatchAllEvents(true);
920 fCanvasView
->SetCommandStack(fDocument
->CommandStack());
921 fCanvasView
->SetMouseFilterMode(SNAPPING_64
);
922 fMouseFilter64MI
->SetMarked(true);
923 // fCanvasView->SetSelection(fDocument->Selection());
925 fPathListView
->SetMenu(fPathMenu
);
926 fPathListView
->SetCommandStack(fDocument
->CommandStack());
927 fPathListView
->SetSelection(fDocument
->Selection());
929 fStyleListView
->SetMenu(fStyleMenu
);
930 fStyleListView
->SetCommandStack(fDocument
->CommandStack());
931 fStyleListView
->SetSelection(fDocument
->Selection());
932 fStyleListView
->SetCurrentColor(fCurrentColor
);
934 fStyleView
->SetCommandStack(fDocument
->CommandStack());
935 fStyleView
->SetCurrentColor(fCurrentColor
);
937 fShapeListView
->SetMenu(fShapeMenu
);
938 fShapeListView
->SetCommandStack(fDocument
->CommandStack());
939 fShapeListView
->SetSelection(fDocument
->Selection());
941 fTransformerListView
->SetMenu(fTransformerMenu
);
942 fTransformerListView
->SetCommandStack(fDocument
->CommandStack());
943 fTransformerListView
->SetSelection(fDocument
->Selection());
945 fPropertyListView
->SetCommandStack(fDocument
->CommandStack());
946 fPropertyListView
->SetSelection(fDocument
->Selection());
947 fPropertyListView
->SetMenu(fPropertyMenu
);
949 fDocument
->CommandStack()->AddObserver(this);
951 fSwatchGroup
->SetCurrentColor(fCurrentColor
);
953 SetIcon(fDocument
->Icon());
955 AddShortcut('Y', 0, new BMessage(MSG_UNDO
));
956 AddShortcut('Y', B_SHIFT_KEY
, new BMessage(MSG_REDO
));
957 AddShortcut('E', 0, new BMessage(MSG_RENAME_OBJECT
));
962 MainWindow::_CreateGUI()
964 SetLayout(new BGroupLayout(B_HORIZONTAL
));
966 BGridLayout
* layout
= new BGridLayout();
967 layout
->SetSpacing(0, 0);
968 BView
* rootView
= new BView("root view", 0, layout
);
970 rootView
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
972 BGroupView
* leftTopView
= new BGroupView(B_VERTICAL
, 0);
973 layout
->AddView(leftTopView
, 0, 0);
975 // views along the left side
976 leftTopView
->AddChild(_CreateMenuBar());
978 float splitWidth
= 13 * be_plain_font
->Size();
979 BSize minSize
= leftTopView
->MinSize();
980 splitWidth
= std::max(splitWidth
, minSize
.width
);
981 leftTopView
->SetExplicitMaxSize(BSize(splitWidth
, B_SIZE_UNSET
));
982 leftTopView
->SetExplicitMinSize(BSize(splitWidth
, B_SIZE_UNSET
));
984 BGroupView
* iconPreviews
= new BGroupView(B_HORIZONTAL
);
985 iconPreviews
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
986 iconPreviews
->GroupLayout()->SetSpacing(5);
989 fIconPreview16Folder
= new IconView(BRect(0, 0, 15, 15),
990 "icon preview 16 folder");
991 fIconPreview16Menu
= new IconView(BRect(0, 0, 15, 15),
992 "icon preview 16 menu");
993 fIconPreview16Menu
->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR
));
995 fIconPreview32Folder
= new IconView(BRect(0, 0, 31, 31),
996 "icon preview 32 folder");
997 fIconPreview32Desktop
= new IconView(BRect(0, 0, 31, 31),
998 "icon preview 32 desktop");
999 fIconPreview32Desktop
->SetLowColor(ui_color(B_DESKTOP_COLOR
));
1001 fIconPreview64
= new IconView(BRect(0, 0, 63, 63), "icon preview 64");
1002 fIconPreview64
->SetLowColor(ui_color(B_DESKTOP_COLOR
));
1005 BGroupView
* smallPreviews
= new BGroupView(B_VERTICAL
);
1006 smallPreviews
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
1007 smallPreviews
->GroupLayout()->SetSpacing(5);
1009 smallPreviews
->AddChild(fIconPreview16Folder
);
1010 smallPreviews
->AddChild(fIconPreview16Menu
);
1012 BGroupView
* mediumPreviews
= new BGroupView(B_VERTICAL
);
1013 mediumPreviews
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
1014 mediumPreviews
->GroupLayout()->SetSpacing(5);
1016 mediumPreviews
->AddChild(fIconPreview32Folder
);
1017 mediumPreviews
->AddChild(fIconPreview32Desktop
);
1019 // iconPreviews->AddChild(fIconPreview48);
1021 iconPreviews
->AddChild(smallPreviews
);
1022 iconPreviews
->AddChild(mediumPreviews
);
1023 iconPreviews
->AddChild(fIconPreview64
);
1024 iconPreviews
->SetExplicitMaxSize(BSize(B_SIZE_UNSET
, B_SIZE_UNLIMITED
));
1026 leftTopView
->AddChild(iconPreviews
);
1029 BGroupView
* leftSideView
= new BGroupView(B_VERTICAL
, 0);
1030 layout
->AddView(leftSideView
, 0, 1);
1031 leftSideView
->SetExplicitMaxSize(BSize(splitWidth
, B_SIZE_UNSET
));
1033 // path menu and list view
1034 BMenuBar
* menuBar
= new BMenuBar("path menu bar");
1035 menuBar
->AddItem(fPathMenu
);
1036 leftSideView
->AddChild(menuBar
);
1038 fPathListView
= new PathListView(BRect(0, 0, splitWidth
, 100),
1039 "path list view", new BMessage(MSG_PATH_SELECTED
), this);
1041 BView
* scrollView
= new BScrollView("path list scroll view",
1042 fPathListView
, B_FOLLOW_NONE
, 0, false, true, B_NO_BORDER
);
1043 leftSideView
->AddChild(scrollView
);
1046 menuBar
= new BMenuBar("shape menu bar");
1047 menuBar
->AddItem(fShapeMenu
);
1048 leftSideView
->AddChild(menuBar
);
1050 fShapeListView
= new ShapeListView(BRect(0, 0, splitWidth
, 100),
1051 "shape list view", new BMessage(MSG_SHAPE_SELECTED
), this);
1052 scrollView
= new BScrollView("shape list scroll view",
1053 fShapeListView
, B_FOLLOW_NONE
, 0, false, true, B_NO_BORDER
);
1054 leftSideView
->AddChild(scrollView
);
1056 // transformer list view
1057 menuBar
= new BMenuBar("transformer menu bar");
1058 menuBar
->AddItem(fTransformerMenu
);
1059 leftSideView
->AddChild(menuBar
);
1061 fTransformerListView
= new TransformerListView(BRect(0, 0, splitWidth
, 100),
1062 "transformer list view");
1063 scrollView
= new BScrollView("transformer list scroll view",
1064 fTransformerListView
, B_FOLLOW_NONE
, 0, false, true, B_NO_BORDER
);
1065 leftSideView
->AddChild(scrollView
);
1067 // property list view
1068 menuBar
= new BMenuBar("property menu bar");
1069 menuBar
->AddItem(fPropertyMenu
);
1070 leftSideView
->AddChild(menuBar
);
1072 fPropertyListView
= new IconObjectListView();
1074 // scroll view around property list view
1075 ScrollView
* propScrollView
= new ScrollView(fPropertyListView
,
1076 SCROLL_VERTICAL
, BRect(0, 0, splitWidth
, 100), "property scroll view",
1077 B_FOLLOW_NONE
, B_WILL_DRAW
| B_FRAME_EVENTS
, B_PLAIN_BORDER
,
1079 leftSideView
->AddChild(propScrollView
);
1081 BGroupLayout
* topSide
= new BGroupLayout(B_HORIZONTAL
);
1082 topSide
->SetSpacing(0);
1083 BView
* topSideView
= new BView("top side view", 0, topSide
);
1084 layout
->AddView(topSideView
, 1, 0);
1087 BRect canvasBounds
= BRect(0, 0, 200, 200);
1088 fCanvasView
= new CanvasView(canvasBounds
);
1090 // scroll view around canvas view
1091 canvasBounds
.bottom
+= B_H_SCROLL_BAR_HEIGHT
;
1092 canvasBounds
.right
+= B_V_SCROLL_BAR_WIDTH
;
1093 ScrollView
* canvasScrollView
= new ScrollView(fCanvasView
, SCROLL_VERTICAL
1094 | SCROLL_HORIZONTAL
| SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS
,
1095 canvasBounds
, "canvas scroll view", B_FOLLOW_NONE
,
1096 B_WILL_DRAW
| B_FRAME_EVENTS
, B_NO_BORDER
);
1097 layout
->AddView(canvasScrollView
, 1, 1);
1099 // views along the top
1101 BGroupLayout
* styleGroup
= new BGroupLayout(B_VERTICAL
, 0);
1102 BView
* styleGroupView
= new BView("style group", 0, styleGroup
);
1103 topSide
->AddView(styleGroupView
);
1106 menuBar
= new BMenuBar("style menu bar");
1107 menuBar
->AddItem(fStyleMenu
);
1108 styleGroup
->AddView(menuBar
);
1110 fStyleListView
= new StyleListView(BRect(0, 0, splitWidth
, 100),
1111 "style list view", new BMessage(MSG_STYLE_SELECTED
), this);
1112 scrollView
= new BScrollView("style list scroll view", fStyleListView
,
1113 B_FOLLOW_NONE
, 0, false, true, B_NO_BORDER
);
1114 scrollView
->SetExplicitMaxSize(BSize(splitWidth
, B_SIZE_UNLIMITED
));
1115 styleGroup
->AddView(scrollView
);
1118 fStyleView
= new StyleView(BRect(0, 0, 200, 100));
1119 topSide
->AddView(fStyleView
);
1122 BGroupLayout
* swatchGroup
= new BGroupLayout(B_VERTICAL
);
1123 swatchGroup
->SetSpacing(0);
1124 BView
* swatchGroupView
= new BView("swatch group", 0, swatchGroup
);
1125 topSide
->AddView(swatchGroupView
);
1127 menuBar
= new BMenuBar("swatches menu bar");
1128 menuBar
->AddItem(fSwatchMenu
);
1129 swatchGroup
->AddView(menuBar
);
1131 fSwatchGroup
= new SwatchGroup(BRect(0, 0, 100, 100));
1132 swatchGroup
->AddView(fSwatchGroup
);
1134 swatchGroupView
->SetExplicitMaxSize(swatchGroupView
->MinSize());
1136 // make sure the top side has fixed height
1137 topSideView
->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
,
1138 swatchGroupView
->MinSize().height
));
1142 MainWindow::_CreateMenuBar()
1144 BMenuBar
* menuBar
= new BMenuBar("main menu");
1147 #undef B_TRANSLATION_CONTEXT
1148 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menus"
1151 BMenu
* fileMenu
= new BMenu(B_TRANSLATE("File"));
1152 BMenu
* editMenu
= new BMenu(B_TRANSLATE("Edit"));
1153 BMenu
* settingsMenu
= new BMenu(B_TRANSLATE("Settings"));
1154 fPathMenu
= new BMenu(B_TRANSLATE("Path"));
1155 fStyleMenu
= new BMenu(B_TRANSLATE("Style"));
1156 fShapeMenu
= new BMenu(B_TRANSLATE("Shape"));
1157 fTransformerMenu
= new BMenu(B_TRANSLATE("Transformer"));
1158 fPropertyMenu
= new BMenu(B_TRANSLATE("Properties"));
1159 fSwatchMenu
= new BMenu(B_TRANSLATE("Swatches"));
1161 menuBar
->AddItem(fileMenu
);
1162 menuBar
->AddItem(editMenu
);
1163 menuBar
->AddItem(settingsMenu
);
1167 #undef B_TRANSLATION_CONTEXT
1168 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-File"
1171 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("New"),
1172 new BMessage(MSG_NEW
), 'N');
1173 fileMenu
->AddItem(item
);
1174 item
->SetTarget(be_app
);
1175 item
= new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS
),
1176 new BMessage(MSG_OPEN
), 'O');
1177 fileMenu
->AddItem(item
);
1178 BMessage
* appendMessage
= new BMessage(MSG_APPEND
);
1179 appendMessage
->AddPointer("window", this);
1180 item
= new BMenuItem(B_TRANSLATE("Append" B_UTF8_ELLIPSIS
),
1181 appendMessage
, 'O', B_SHIFT_KEY
);
1182 fileMenu
->AddItem(item
);
1183 item
->SetTarget(be_app
);
1184 fileMenu
->AddSeparatorItem();
1185 fileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Save"),
1186 new BMessage(MSG_SAVE
), 'S'));
1187 fileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS
),
1188 new BMessage(MSG_SAVE_AS
), 'S', B_SHIFT_KEY
));
1189 fileMenu
->AddSeparatorItem();
1190 fileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Export"),
1191 new BMessage(MSG_EXPORT
), 'P'));
1192 fileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Export as" B_UTF8_ELLIPSIS
),
1193 new BMessage(MSG_EXPORT_AS
), 'P', B_SHIFT_KEY
));
1194 fileMenu
->AddSeparatorItem();
1195 fileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1196 new BMessage(B_QUIT_REQUESTED
), 'W'));
1197 item
= new BMenuItem(B_TRANSLATE("Quit"),
1198 new BMessage(B_QUIT_REQUESTED
), 'Q');
1199 fileMenu
->AddItem(item
);
1200 item
->SetTarget(be_app
);
1203 #undef B_TRANSLATION_CONTEXT
1204 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-Edit"
1207 fUndoMI
= new BMenuItem(B_TRANSLATE("<nothing to undo>"),
1208 new BMessage(MSG_UNDO
), 'Z');
1209 fRedoMI
= new BMenuItem(B_TRANSLATE("<nothing to redo>"),
1210 new BMessage(MSG_REDO
), 'Z', B_SHIFT_KEY
);
1212 fUndoMI
->SetEnabled(false);
1213 fRedoMI
->SetEnabled(false);
1215 editMenu
->AddItem(fUndoMI
);
1216 editMenu
->AddItem(fRedoMI
);
1220 #undef B_TRANSLATION_CONTEXT
1221 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Menu-Settings"
1224 BMenu
* filterModeMenu
= new BMenu(B_TRANSLATE("Snap to grid"));
1225 BMessage
* message
= new BMessage(MSG_MOUSE_FILTER_MODE
);
1226 message
->AddInt32("mode", SNAPPING_OFF
);
1227 fMouseFilterOffMI
= new BMenuItem(B_TRANSLATE("Off"), message
, '4');
1228 filterModeMenu
->AddItem(fMouseFilterOffMI
);
1230 message
= new BMessage(MSG_MOUSE_FILTER_MODE
);
1231 message
->AddInt32("mode", SNAPPING_64
);
1232 fMouseFilter64MI
= new BMenuItem("64 x 64", message
, '3');
1233 filterModeMenu
->AddItem(fMouseFilter64MI
);
1235 message
= new BMessage(MSG_MOUSE_FILTER_MODE
);
1236 message
->AddInt32("mode", SNAPPING_32
);
1237 fMouseFilter32MI
= new BMenuItem("32 x 32", message
, '2');
1238 filterModeMenu
->AddItem(fMouseFilter32MI
);
1240 message
= new BMessage(MSG_MOUSE_FILTER_MODE
);
1241 message
->AddInt32("mode", SNAPPING_16
);
1242 fMouseFilter16MI
= new BMenuItem("16 x 16", message
, '1');
1243 filterModeMenu
->AddItem(fMouseFilter16MI
);
1245 filterModeMenu
->SetRadioMode(true);
1247 settingsMenu
->AddItem(filterModeMenu
);
1254 MainWindow::_ImproveScrollBarLayout(BView
* target
)
1256 // NOTE: The BListViews for which this function is used
1257 // are directly below a BMenuBar. If the BScrollBar and
1258 // the BMenuBar share bottom/top border respectively, the
1259 // GUI looks a little more polished. This trick can be
1260 // removed if/when the BScrollViews are embedded in a
1261 // surounding border like in WonderBrush.
1263 if (BScrollBar
* scrollBar
= target
->ScrollBar(B_VERTICAL
)) {
1264 scrollBar
->MoveBy(0, -1);
1265 scrollBar
->ResizeBy(0, 1);
1274 MainWindow::_CheckSaveIcon(const BMessage
* currentMessage
)
1276 if (fDocument
->IsEmpty() || fDocument
->CommandStack()->IsSaved())
1279 // Make sure the user sees us.
1282 BAlert
* alert
= new BAlert("save",
1283 B_TRANSLATE("Save changes to current icon before closing?"),
1284 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
1285 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL
, B_OFFSET_SPACING
,
1287 alert
->SetShortcut(0, B_ESCAPE
);
1288 alert
->SetShortcut(1, 'd');
1289 alert
->SetShortcut(2, 's');
1290 int32 choice
= alert
->Go();
1300 // cancel (save first) but pick up what we were doing before
1301 PostMessage(MSG_SAVE
);
1302 if (currentMessage
!= NULL
) {
1303 delete fMessageAfterSave
;
1304 fMessageAfterSave
= new BMessage(*currentMessage
);
1312 MainWindow::_PickUpActionBeforeSave()
1314 if (fDocument
->WriteLock()) {
1315 fDocument
->CommandStack()->Save();
1316 fDocument
->WriteUnlock();
1319 if (fMessageAfterSave
== NULL
)
1322 PostMessage(fMessageAfterSave
);
1323 delete fMessageAfterSave
;
1324 fMessageAfterSave
= NULL
;
1332 MainWindow::_MakeIconEmpty()
1334 if (!_CheckSaveIcon(CurrentMessage()))
1337 AutoWriteLocker
locker(fDocument
);
1340 fDocument
->MakeEmpty();
1347 MainWindow::_CreateSaver(const entry_ref
& ref
, uint32 exportMode
)
1349 DocumentSaver
* saver
;
1351 switch (exportMode
) {
1352 case EXPORT_MODE_FLAT_ICON
:
1353 saver
= new SimpleFileSaver(new FlatIconExporter(), ref
);
1356 case EXPORT_MODE_ICON_ATTR
:
1357 case EXPORT_MODE_ICON_MIME_ATTR
: {
1358 const char* attrName
1359 = exportMode
== EXPORT_MODE_ICON_ATTR
?
1360 kVectorAttrNodeName
: kVectorAttrMimeName
;
1361 saver
= new AttributeSaver(ref
, attrName
);
1365 case EXPORT_MODE_ICON_RDEF
:
1366 saver
= new SimpleFileSaver(new RDefExporter(), ref
);
1368 case EXPORT_MODE_ICON_SOURCE
:
1369 saver
= new SimpleFileSaver(new SourceExporter(), ref
);
1372 case EXPORT_MODE_BITMAP_16
:
1373 saver
= new SimpleFileSaver(new BitmapExporter(16), ref
);
1375 case EXPORT_MODE_BITMAP_32
:
1376 saver
= new SimpleFileSaver(new BitmapExporter(32), ref
);
1378 case EXPORT_MODE_BITMAP_64
:
1379 saver
= new SimpleFileSaver(new BitmapExporter(64), ref
);
1382 case EXPORT_MODE_BITMAP_SET
:
1383 saver
= new BitmapSetSaver(ref
);
1386 case EXPORT_MODE_SVG
:
1387 saver
= new SimpleFileSaver(new SVGExporter(), ref
);
1390 case EXPORT_MODE_MESSAGE
:
1392 saver
= new NativeSaver(ref
);
1401 MainWindow::_FileName(bool preferExporter
) const
1405 if (preferExporter
) {
1406 saver1
= dynamic_cast<FileSaver
*>(fDocument
->ExportSaver());
1407 saver2
= dynamic_cast<FileSaver
*>(fDocument
->NativeSaver());
1409 saver1
= dynamic_cast<FileSaver
*>(fDocument
->NativeSaver());
1410 saver2
= dynamic_cast<FileSaver
*>(fDocument
->ExportSaver());
1412 const char* fileName
= NULL
;
1414 fileName
= saver1
->Ref()->name
;
1415 if ((fileName
== NULL
|| fileName
[0] == '\0') && saver2
!= NULL
)
1416 fileName
= saver2
->Ref()->name
;
1422 MainWindow::_UpdateWindowTitle()
1424 const char* fileName
= _FileName(false);
1425 if (fileName
!= NULL
)
1428 SetTitle(B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"));