From 61ee19d10fcc38e93a80a29b90efe6e4e15604df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 10 Apr 2011 22:32:51 +0000 Subject: [PATCH] * A whole buch of refactoring and other changes to make Icon-O-Matic a multi-document application. This is already more useful than before, but will be even more useful once it is possible to copy more types of objects to the system clipboard and paste them into other open icons. * Fixed wrong snapping menu item being marked when restoring settings that enable a different snapping mode from the default. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41212 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/icon-o-matic/IconEditorApp.cpp | 616 ++++------------- src/apps/icon-o-matic/IconEditorApp.h | 30 +- src/apps/icon-o-matic/MainWindow.cpp | 621 +++++++++++++++--- src/apps/icon-o-matic/MainWindow.h | 29 +- src/apps/icon-o-matic/gui/GradientControl.cpp | 66 +- src/apps/icon-o-matic/gui/StyleListView.cpp | 28 +- src/apps/icon-o-matic/gui/StyleListView.h | 30 +- src/apps/icon-o-matic/gui/SwatchGroup.cpp | 8 +- src/apps/icon-o-matic/style/CurrentColor.cpp | 22 +- src/apps/icon-o-matic/style/CurrentColor.h | 19 +- 10 files changed, 802 insertions(+), 667 deletions(-) diff --git a/src/apps/icon-o-matic/IconEditorApp.cpp b/src/apps/icon-o-matic/IconEditorApp.cpp index fc8c171172..355f90d0cd 100644 --- a/src/apps/icon-o-matic/IconEditorApp.cpp +++ b/src/apps/icon-o-matic/IconEditorApp.cpp @@ -12,11 +12,7 @@ #include #include -#include -#include -#include #include -#include #include #include #include @@ -24,30 +20,10 @@ #include "support_settings.h" -#include "AttributeSaver.h" #include "AutoLocker.h" -#include "BitmapExporter.h" -#include "BitmapSetSaver.h" -#include "CommandStack.h" #include "Defines.h" -#include "Document.h" -#include "FlatIconExporter.h" -#include "FlatIconFormat.h" -#include "FlatIconImporter.h" -#include "Icon.h" #include "MainWindow.h" -#include "MessageExporter.h" -#include "MessageImporter.h" -#include "MessengerSaver.h" -#include "NativeSaver.h" -#include "PathContainer.h" -#include "RDefExporter.h" #include "SavePanel.h" -#include "ShapeContainer.h" -#include "SimpleFileSaver.h" -#include "SourceExporter.h" -#include "SVGExporter.h" -#include "SVGImporter.h" #undef B_TRANSLATE_CONTEXT @@ -60,33 +36,36 @@ static const char* kAppSig = "application/x-vnd.haiku-icon_o_matic"; IconEditorApp::IconEditorApp() - : BApplication(kAppSig), - fMainWindow(NULL), - fDocument(new Document("test")), + : + BApplication(kAppSig), + fWindowCount(0), + fLastWindowFrame(50, 50, 900, 750), - fOpenPanel(NULL), - fSavePanel(NULL), + fOpenPanel(NULL), + fSavePanel(NULL), - fLastOpenPath(""), - fLastSavePath(""), - fLastExportPath(""), - - fMessageAfterSave(NULL) + fLastOpenPath(""), + fLastSavePath(""), + fLastExportPath("") { + // create file panels + BMessenger messenger(this, this); + BMessage message(B_REFS_RECEIVED); + fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, B_FILE_NODE, + true, &message); + + message.what = MSG_SAVE_AS; + fSavePanel = new SavePanel("save panel", &messenger, NULL, B_FILE_NODE + | B_DIRECTORY_NODE | B_SYMLINK_NODE, false, &message); + + _RestoreSettings(); } IconEditorApp::~IconEditorApp() { - // NOTE: it is important that the GUI has been deleted - // at this point, so that all the listener/observer - // stuff is properly detached - delete fDocument; - delete fOpenPanel; delete fSavePanel; - - delete fMessageAfterSave; } @@ -96,15 +75,33 @@ IconEditorApp::~IconEditorApp() bool IconEditorApp::QuitRequested() { - if (!_CheckSaveIcon(CurrentMessage())) + // Run the QuitRequested() hook in each window's own thread. Otherwise + // the BAlert which a window shows when an icon is not saved will not + // repaint the window. (BAlerts check which thread is running Go() and + // will repaint windows when it's a BWindow.) + bool quit = true; + for (int32 i = 0; BWindow* window = WindowAt(i); i++) { + if (!window->Lock()) + continue; + // Try to cast the window while the pointer must be valid. + MainWindow* mainWindow = dynamic_cast(window); + window->Unlock(); + if (mainWindow == NULL) + continue; + BMessenger messenger(window, window); + BMessage reply; + if (messenger.SendMessage(B_QUIT_REQUESTED, &reply) != B_OK) + continue; + bool result; + if (reply.FindBool("result", &result) == B_OK && !result) + quit = false; + } + + if (!quit) return false; _StoreSettings(); - fMainWindow->Lock(); - fMainWindow->Quit(); - fMainWindow = NULL; - return true; } @@ -114,7 +111,7 @@ IconEditorApp::MessageReceived(BMessage* message) { switch (message->what) { case MSG_NEW: - _MakeIconEmpty(); + _NewWindow()->Show(); break; case MSG_OPEN: { BMessage openMessage(B_REFS_RECEIVED); @@ -123,80 +120,18 @@ IconEditorApp::MessageReceived(BMessage* message) break; } case MSG_APPEND: { + MainWindow* window; + if (message->FindPointer("window", (void**)&window) != B_OK) + break; BMessage openMessage(B_REFS_RECEIVED); openMessage.AddBool("append", true); + openMessage.AddPointer("window", window); fOpenPanel->SetMessage(&openMessage); fOpenPanel->Show(); break; } - case MSG_SAVE: - case MSG_EXPORT: { - DocumentSaver* saver; - if (message->what == MSG_SAVE) - saver = fDocument->NativeSaver(); - else - saver = fDocument->ExportSaver(); - if (saver != NULL) { - saver->Save(fDocument); - _PickUpActionBeforeSave(); - break; - } // else fall through - } - case MSG_SAVE_AS: - case MSG_EXPORT_AS: { - int32 exportMode; - if (message->FindInt32("export mode", &exportMode) < B_OK) - exportMode = EXPORT_MODE_MESSAGE; - entry_ref ref; - const char* name; - if (message->FindRef("directory", &ref) == B_OK - && message->FindString("name", &name) == B_OK) { - // this message comes from the file panel - BDirectory dir(&ref); - BEntry entry; - if (dir.InitCheck() >= B_OK - && entry.SetTo(&dir, name, true) >= B_OK - && entry.GetRef(&ref) >= B_OK) { - - // create the document saver and remember it for later - DocumentSaver* saver = _CreateSaver(ref, exportMode); - if (saver) { - if (exportMode == EXPORT_MODE_MESSAGE) - fDocument->SetNativeSaver(saver); - else - fDocument->SetExportSaver(saver); - saver->Save(fDocument); - _PickUpActionBeforeSave(); - } - } - _SyncPanels(fSavePanel, fOpenPanel); - } else { - printf("configure file panel\n"); - // configure the file panel - const char* saveText = NULL; - FileSaver* saver = dynamic_cast( - fDocument->NativeSaver()); - if (saver != NULL) - saveText = saver->Ref()->name; - - bool isExportMode = message->what == MSG_EXPORT_AS - || message->what == MSG_EXPORT; - if (isExportMode) { - saver = dynamic_cast( - fDocument->ExportSaver()); - if (saver != NULL && saver->Ref()->name != NULL) - saveText = saver->Ref()->name; - } - - fSavePanel->SetExportMode(isExportMode); -// fSavePanel->Refresh(); - if (saveText != NULL) - fSavePanel->SetSaveText(saveText); - fSavePanel->Show(); - } - break; - } - case B_EDIT_ICON_DATA: { + case B_EDIT_ICON_DATA: + { BMessenger messenger; if (message->FindMessenger("reply to", &messenger) < B_OK) { // required @@ -210,7 +145,41 @@ IconEditorApp::MessageReceived(BMessage* message) data = NULL; size = 0; } - _Open(messenger, data, size); + MainWindow* window = _NewWindow(); + window->Open(messenger, data, size); + window->Show(); + break; + } + case MSG_SAVE_AS: + case MSG_EXPORT_AS: + { + BMessenger messenger; + if (message->FindMessenger("target", &messenger) != B_OK) + break; + + fSavePanel->SetExportMode(message->what == MSG_EXPORT_AS); +// fSavePanel->Refresh(); + const char* saveText; + if (message->FindString("save text", &saveText) == B_OK) + fSavePanel->SetSaveText(saveText); + fSavePanel->SetTarget(messenger); + fSavePanel->Show(); + break; + } + + case MSG_WINDOW_CLOSED: + { + fWindowCount--; + if (fWindowCount == 0) + PostMessage(B_QUIT_REQUESTED); + BMessage settings; + if (message->FindMessage("settings", &settings) == B_OK) + fLastWindowSettings = settings; + BRect frame; + if (message->FindRect("window frame", &frame) == B_OK) { + fLastWindowFrame = frame; + fLastWindowFrame.OffsetBy(-10, -10); + } break; } @@ -224,22 +193,9 @@ IconEditorApp::MessageReceived(BMessage* message) void IconEditorApp::ReadyToRun() { - // create file panels - BMessenger messenger(this, this); - BMessage message(B_REFS_RECEIVED); - fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, B_FILE_NODE, - true, &message); - - message.what = MSG_SAVE_AS; - fSavePanel = new SavePanel("save panel", &messenger, NULL, - B_FILE_NODE | B_DIRECTORY_NODE | B_SYMLINK_NODE, false, &message); - // create main window - BMessage settings('stns'); - _RestoreSettings(settings); - - fMainWindow = new MainWindow(this, fDocument, &settings); - fMainWindow->Show(); + if (fWindowCount == 0) + _NewWindow()->Show(); _InstallDocumentMimeType(); } @@ -248,15 +204,28 @@ IconEditorApp::ReadyToRun() void IconEditorApp::RefsReceived(BMessage* message) { - // TODO: multiple documents (iterate over refs) bool append; if (message->FindBool("append", &append) < B_OK) append = false; entry_ref ref; - if (message->FindRef("refs", &ref) == B_OK) - _Open(ref, append); + if (append) { + MainWindow* window; + if (message->FindPointer("window", (void**)&window) != B_OK) + return; + if (!window->Lock()) + return; + for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) + window->Open(ref, true); + window->Unlock(); + } else { + for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { + MainWindow* window = _NewWindow(); + window->Open(ref, false); + window->Show(); + } + } - if (fOpenPanel && fSavePanel) + if (fOpenPanel != NULL && fSavePanel != NULL) _SyncPanels(fOpenPanel, fSavePanel); } @@ -267,357 +236,29 @@ IconEditorApp::ArgvReceived(int32 argc, char** argv) if (argc < 2) return; - // TODO: multiple documents (iterate over argv) entry_ref ref; - if (get_ref_for_path(argv[1], &ref) == B_OK) - _Open(ref); + + for (int32 i = 1; i < argc; i++) { + if (get_ref_for_path(argv[i], &ref) == B_OK) { + MainWindow* window = _NewWindow(); + window->Open(ref); + window->Show(); + } + } } // #pragma mark - -bool -IconEditorApp::_CheckSaveIcon(const BMessage* currentMessage) +MainWindow* +IconEditorApp::_NewWindow() { - if (fDocument->IsEmpty() || fDocument->CommandStack()->IsSaved()) - return true; - - BAlert* alert = new BAlert("save", - B_TRANSLATE("Save changes to current icon?"), B_TRANSLATE("Discard"), - B_TRANSLATE("Cancel"), B_TRANSLATE("Save")); - int32 choice = alert->Go(); - switch (choice) { - case 0: - // discard - return true; - case 1: - // cancel - return false; - case 2: - default: - // cancel (save first) but pick what we were doing before - PostMessage(MSG_SAVE); - if (currentMessage) { - delete fMessageAfterSave; - fMessageAfterSave = new BMessage(*currentMessage); - } - return false; - } -} - - -void -IconEditorApp::_PickUpActionBeforeSave() -{ - if (fDocument->WriteLock()) { - fDocument->CommandStack()->Save(); - fDocument->WriteUnlock(); - } - - if (fMessageAfterSave == NULL) - return; - - PostMessage(fMessageAfterSave); - delete fMessageAfterSave; - fMessageAfterSave = NULL; -} - - -// #pragma mark - - - -void -IconEditorApp::_MakeIconEmpty() -{ - if (!_CheckSaveIcon(CurrentMessage())) - return; - - bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); - - AutoWriteLocker locker(fDocument); - - if (fMainWindow) - fMainWindow->MakeEmpty(); - - fDocument->MakeEmpty(); - - locker.Unlock(); - - if (mainWindowLocked) - fMainWindow->Unlock(); -} - - -void -IconEditorApp::_Open(const entry_ref& ref, bool append) -{ - if (!_CheckSaveIcon(CurrentMessage())) - return; - - BFile file(&ref, B_READ_ONLY); - if (file.InitCheck() < B_OK) - return; - - Icon* icon; - if (append) - icon = new (nothrow) Icon(*fDocument->Icon()); - else - icon = new (nothrow) Icon(); - - if (!icon) - return; - - enum { - REF_NONE = 0, - REF_MESSAGE, - REF_FLAT, - REF_FLAT_ATTR, - REF_SVG - }; - uint32 refMode = REF_NONE; - - // try different file types - FlatIconImporter flatImporter; - status_t ret = flatImporter.Import(icon, &file); - if (ret >= B_OK) { - refMode = REF_FLAT; - } else { - file.Seek(0, SEEK_SET); - MessageImporter msgImporter; - ret = msgImporter.Import(icon, &file); - if (ret >= B_OK) { - refMode = REF_MESSAGE; - } else { - file.Seek(0, SEEK_SET); - SVGImporter svgImporter; - ret = svgImporter.Import(icon, &ref); - if (ret >= B_OK) { - refMode = REF_SVG; - } else { - // fall back to flat icon format but use the icon attribute - ret = B_OK; - attr_info attrInfo; - if (file.GetAttrInfo(kVectorAttrNodeName, &attrInfo) == B_OK) { - if (attrInfo.type != B_VECTOR_ICON_TYPE) - ret = B_ERROR; - // If the attribute is there, we must succeed in reading - // an icon! Otherwise we may overwrite an existing icon - // when the user saves. - uint8* buffer = NULL; - if (ret == B_OK) { - buffer = new(nothrow) uint8[attrInfo.size]; - if (buffer == NULL) - ret = B_NO_MEMORY; - } - if (ret == B_OK) { - ssize_t bytesRead = file.ReadAttr(kVectorAttrNodeName, - B_VECTOR_ICON_TYPE, 0, buffer, attrInfo.size); - if (bytesRead != (ssize_t)attrInfo.size) { - ret = bytesRead < 0 ? (status_t)bytesRead - : B_IO_ERROR; - } - } - if (ret == B_OK) { - ret = flatImporter.Import(icon, buffer, attrInfo.size); - if (ret == B_OK) - refMode = REF_FLAT_ATTR; - } - - delete[] buffer; - } else { - // If there is no icon attribute, simply fall back - // to creating an icon for this file. TODO: We may or may - // not want to display an alert asking the user if that is - // what he wants to do. - refMode = REF_FLAT_ATTR; - } - } - } - } - - if (ret < B_OK) { - // inform user of failure at this point - BString helper(B_TRANSLATE("Opening the document failed!")); - helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); - BAlert* alert = new BAlert( - B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"), - helper.String(), - B_TRANSLATE_WITH_CONTEXT("Bummer", - "Cancel button - error alert"), - NULL, NULL); - // launch alert asynchronously - alert->Go(NULL); - - delete icon; - return; - } - - // keep the mainwindow locked while switching icons - bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); - - AutoWriteLocker locker(fDocument); - - if (mainWindowLocked) - fMainWindow->SetIcon(NULL); - - // incorporate the loaded icon into the document - // (either replace it or append to it) - fDocument->MakeEmpty(!append); - // if append, the document savers are preserved - fDocument->SetIcon(icon); - if (!append) { - // document got replaced, but we have at - // least one ref already - switch (refMode) { - case REF_MESSAGE: - fDocument->SetNativeSaver(new NativeSaver(ref)); - break; - case REF_FLAT: - fDocument->SetExportSaver( - new SimpleFileSaver(new FlatIconExporter(), ref)); - break; - case REF_FLAT_ATTR: - fDocument->SetNativeSaver( - new AttributeSaver(ref, kVectorAttrNodeName)); - break; - case REF_SVG: - fDocument->SetExportSaver( - new SimpleFileSaver(new SVGExporter(), ref)); - break; - } - } - - locker.Unlock(); - - if (mainWindowLocked) { - fMainWindow->Unlock(); - // cause the mainwindow to adopt icon in - // it's own thread - fMainWindow->PostMessage(MSG_SET_ICON); - } -} - - -void -IconEditorApp::_Open(const BMessenger& externalObserver, const uint8* data, - size_t size) -{ - if (!_CheckSaveIcon(CurrentMessage())) - return; - - if (!externalObserver.IsValid()) - return; - - Icon* icon = new (nothrow) Icon(); - if (!icon) - return; - - if (data && size > 0) { - // try to open the icon from the provided data - FlatIconImporter flatImporter; - status_t ret = flatImporter.Import(icon, const_cast(data), - size); - // NOTE: the const_cast is a bit ugly, but no harm is done - // the reason is that the LittleEndianBuffer knows read and write - // mode, in this case it is used read-only, and it does not assume - // ownership of the buffer - - if (ret < B_OK) { - // inform user of failure at this point - BString helper(B_TRANSLATE("Opening the icon failed!")); - helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); - BAlert* alert = new BAlert( - B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"), - helper.String(), - B_TRANSLATE_WITH_CONTEXT("Bummer", - "Cancel button - error alert"), - NULL, NULL); - // launch alert asynchronously - alert->Go(NULL); - - delete icon; - return; - } - } - - // keep the mainwindow locked while switching icons - bool mainWindowLocked = fMainWindow && fMainWindow->Lock(); - - AutoWriteLocker locker(fDocument); - - if (mainWindowLocked) - fMainWindow->SetIcon(NULL); - - // incorporate the loaded icon into the document - // (either replace it or append to it) - fDocument->MakeEmpty(); - fDocument->SetIcon(icon); - - fDocument->SetNativeSaver(new MessengerSaver(externalObserver)); - - locker.Unlock(); - - if (mainWindowLocked) { - fMainWindow->Unlock(); - // cause the mainwindow to adopt icon in - // it's own thread - fMainWindow->PostMessage(MSG_SET_ICON); - } -} - - -DocumentSaver* -IconEditorApp::_CreateSaver(const entry_ref& ref, uint32 exportMode) -{ - DocumentSaver* saver; - - switch (exportMode) { - case EXPORT_MODE_FLAT_ICON: - saver = new SimpleFileSaver(new FlatIconExporter(), ref); - break; - - case EXPORT_MODE_ICON_ATTR: - case EXPORT_MODE_ICON_MIME_ATTR: { - const char* attrName - = exportMode == EXPORT_MODE_ICON_ATTR ? - kVectorAttrNodeName : kVectorAttrMimeName; - saver = new AttributeSaver(ref, attrName); - break; - } - - case EXPORT_MODE_ICON_RDEF: - saver = new SimpleFileSaver(new RDefExporter(), ref); - break; - case EXPORT_MODE_ICON_SOURCE: - saver = new SimpleFileSaver(new SourceExporter(), ref); - break; - - case EXPORT_MODE_BITMAP_16: - saver = new SimpleFileSaver(new BitmapExporter(16), ref); - break; - case EXPORT_MODE_BITMAP_32: - saver = new SimpleFileSaver(new BitmapExporter(32), ref); - break; - case EXPORT_MODE_BITMAP_64: - saver = new SimpleFileSaver(new BitmapExporter(64), ref); - break; - - case EXPORT_MODE_BITMAP_SET: - saver = new BitmapSetSaver(ref); - break; - - case EXPORT_MODE_SVG: - saver = new SimpleFileSaver(new SVGExporter(), ref); - break; - - case EXPORT_MODE_MESSAGE: - default: - saver = new NativeSaver(ref); - break; - } - - return saver; + fLastWindowFrame.OffsetBy(10, 10); + MainWindow* window = new MainWindow(fLastWindowFrame, this, + &fLastWindowSettings); + fWindowCount++; + return window; } @@ -687,20 +328,28 @@ IconEditorApp::_StoreSettings() { BMessage settings('stns'); - fMainWindow->StoreSettings(&settings); - - if (settings.ReplaceInt32("export mode", fSavePanel->ExportMode()) < B_OK) - settings.AddInt32("export mode", fSavePanel->ExportMode()); + settings.AddRect("window frame", fLastWindowFrame); + settings.AddMessage("window settings", &fLastWindowSettings); + settings.AddInt32("export mode", fSavePanel->ExportMode()); save_settings(&settings, "Icon-O-Matic"); } void -IconEditorApp::_RestoreSettings(BMessage& settings) +IconEditorApp::_RestoreSettings() { + BMessage settings('stns'); load_settings(&settings, "Icon-O-Matic"); + BRect frame; + if (settings.FindRect("window frame", &frame) == B_OK) { + fLastWindowFrame = frame; + // Compensate offset for next window... + fLastWindowFrame.OffsetBy(-10, -10); + } + settings.FindMessage("window settings", &fLastWindowSettings); + int32 mode; if (settings.FindInt32("export mode", &mode) >= B_OK) fSavePanel->SetExportMode(mode); @@ -765,3 +414,4 @@ IconEditorApp::_InstallDocumentMimeType() parseError.String()); } } + diff --git a/src/apps/icon-o-matic/IconEditorApp.h b/src/apps/icon-o-matic/IconEditorApp.h index 2ebaea61d4..44df93a1e1 100644 --- a/src/apps/icon-o-matic/IconEditorApp.h +++ b/src/apps/icon-o-matic/IconEditorApp.h @@ -11,23 +11,15 @@ class BFilePanel; -class Document; -class DocumentSaver; class MainWindow; class SavePanel; enum { MSG_NEW = 'newi', - - MSG_OPEN = 'open', - MSG_APPEND = 'apnd', - - MSG_SAVE = 'save', MSG_SAVE_AS = 'svas', - - MSG_EXPORT = 'xprt', MSG_EXPORT_AS = 'xpas', + MSG_WINDOW_CLOSED = 'wndc', }; @@ -68,16 +60,7 @@ public: // IconEditorApp private: - bool _CheckSaveIcon(const BMessage* currentMessage); - void _PickUpActionBeforeSave(); - - void _MakeIconEmpty(); - void _Open(const entry_ref& ref, - bool append = false); - void _Open(const BMessenger& externalObserver, - const uint8* data, size_t size); - DocumentSaver* _CreateSaver(const entry_ref& ref, - uint32 exportMode); + MainWindow* _NewWindow(); void _SyncPanels(BFilePanel* from, BFilePanel* to); @@ -85,12 +68,13 @@ private: const char* _LastFilePath(path_kind which); void _StoreSettings(); - void _RestoreSettings(BMessage& settings); + void _RestoreSettings(); void _InstallDocumentMimeType(); private: - MainWindow* fMainWindow; - Document* fDocument; + int32 fWindowCount; + BMessage fLastWindowSettings; + BRect fLastWindowFrame; BFilePanel* fOpenPanel; SavePanel* fSavePanel; @@ -98,8 +82,6 @@ private: BString fLastOpenPath; BString fLastSavePath; BString fLastExportPath; - - BMessage* fMessageAfterSave; }; diff --git a/src/apps/icon-o-matic/MainWindow.cpp b/src/apps/icon-o-matic/MainWindow.cpp index 21f7eeac02..93cea0a853 100644 --- a/src/apps/icon-o-matic/MainWindow.cpp +++ b/src/apps/icon-o-matic/MainWindow.cpp @@ -8,38 +8,57 @@ #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include -#include #include "support_ui.h" #include "AddPathsCommand.h" #include "AddShapesCommand.h" #include "AddStylesCommand.h" +#include "AttributeSaver.h" +#include "BitmapExporter.h" +#include "BitmapSetSaver.h" #include "CanvasView.h" #include "CommandStack.h" #include "CompoundCommand.h" #include "CurrentColor.h" #include "Document.h" -#include "Exporter.h" +#include "FlatIconExporter.h" +#include "FlatIconFormat.h" +#include "FlatIconImporter.h" #include "IconObjectListView.h" #include "IconEditorApp.h" #include "IconView.h" +#include "MessageExporter.h" +#include "MessageImporter.h" +#include "MessengerSaver.h" +#include "NativeSaver.h" #include "PathListView.h" +#include "RDefExporter.h" #include "ScrollView.h" +#include "SimpleFileSaver.h" #include "ShapeListView.h" +#include "SourceExporter.h" #include "StyleListView.h" #include "StyleView.h" +#include "SVGExporter.h" +#include "SVGImporter.h" #include "SwatchGroup.h" #include "TransformerListView.h" #include "TransformGradientBox.h" @@ -87,15 +106,17 @@ enum { }; -MainWindow::MainWindow(IconEditorApp* app, Document* document, +MainWindow::MainWindow(BRect frame, IconEditorApp* app, const BMessage* settings) : - BWindow(BRect(50, 50, 900, 750), B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"), + BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Icon-O-Matic"), B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), fApp(app), - fDocument(document), - fIcon(NULL) + fDocument(new Document(B_TRANSLATE("Untitled"))), + fCurrentColor(new CurrentColor()), + fIcon(NULL), + fMessageAfterSave(NULL) { _Init(); @@ -107,10 +128,22 @@ MainWindow::~MainWindow() { delete fState; - if (fIcon != NULL) - fIcon->Release(); + SetIcon(NULL); + + // Make sure there are no listeners attached to the document anymore. + while (BView* child = ChildAt(0L)) { + child->RemoveSelf(); + delete child; + } fDocument->CommandStack()->RemoveObserver(this); + + // NOTE: it is important that the GUI has been deleted + // at this point, so that all the listener/observer + // stuff is properly detached + delete fDocument; + + delete fMessageAfterSave; } @@ -122,19 +155,32 @@ MainWindow::MessageReceived(BMessage* message) { bool discard = false; - if (!fDocument || !fDocument->WriteLock()) { + // Figure out if we need the write lock on the Document. For most + // messages we do, but exporting takes place in another thread and + // locking is taken care of there. + bool requiresWriteLock = true; + switch (message->what) { + case MSG_SAVE: + case MSG_EXPORT: + case MSG_SAVE_AS: + case MSG_EXPORT_AS: + requiresWriteLock = false; + break; + default: + break; + } + if (requiresWriteLock && !fDocument->WriteLock()) { BWindow::MessageReceived(message); return; } if (message->WasDropped()) { - const rgb_color *color; - int32 len; - int32 i; + const rgb_color* color; + int32 length; // create styles from dropped colors - for (i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i, - (const void **)&color, &len) == B_OK; i++) { - if (len != sizeof(rgb_color)) + for (int32 i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i, + (const void**)&color, &length) == B_OK; i++) { + if (length != sizeof(rgb_color)) continue; char name[30]; sprintf(name, @@ -159,15 +205,17 @@ MainWindow::MessageReceived(BMessage* message) case B_REFS_RECEIVED: case B_SIMPLE_DATA: message->what = B_REFS_RECEIVED; - if (modifiers() & B_SHIFT_KEY) + if (modifiers() & B_SHIFT_KEY) { message->AddBool("append", true); + message->AddPointer("window", this); + } be_app->PostMessage(message); break; case B_PASTE: case B_MIME_DATA: { - BMessage *clip = message; + BMessage* clip = message; status_t err; if (discard) @@ -185,22 +233,21 @@ MainWindow::MessageReceived(BMessage* message) break; } - Icon* icon; - icon = new (nothrow) Icon(*fDocument->Icon()); - if (icon) { + Icon* icon = new (std::nothrow) Icon(*fDocument->Icon()); + if (icon != NULL) { StyledTextImporter importer; err = importer.Import(icon, clip); if (err >= B_OK) { - AutoWriteLocker locker(fDocument); + AutoWriteLocker locker(fDocument); - SetIcon(NULL); + SetIcon(NULL); - // incorporate the loaded icon into the document - // (either replace it or append to it) - fDocument->MakeEmpty(false); - // if append, the document savers are preserved - fDocument->SetIcon(icon); - SetIcon(icon); + // incorporate the loaded icon into the document + // (either replace it or append to it) + fDocument->MakeEmpty(false); + // if append, the document savers are preserved + fDocument->SetIcon(icon); + SetIcon(icon); } } @@ -209,6 +256,83 @@ MainWindow::MessageReceived(BMessage* message) break; } + case MSG_SAVE: + case MSG_EXPORT: + { + DocumentSaver* saver; + if (message->what == MSG_SAVE) + saver = fDocument->NativeSaver(); + else + saver = fDocument->ExportSaver(); + if (saver != NULL) { + saver->Save(fDocument); + _PickUpActionBeforeSave(); + break; + } // else fall through + } + case MSG_SAVE_AS: + case MSG_EXPORT_AS: + { + int32 exportMode; + if (message->FindInt32("export mode", &exportMode) < B_OK) + exportMode = EXPORT_MODE_MESSAGE; + entry_ref ref; + const char* name; + if (message->FindRef("directory", &ref) == B_OK + && message->FindString("name", &name) == B_OK) { + // this message comes from the file panel + printf("file panel result\n"); + BDirectory dir(&ref); + BEntry entry; + if (dir.InitCheck() >= B_OK + && entry.SetTo(&dir, name, true) >= B_OK + && entry.GetRef(&ref) >= B_OK) { + + // create the document saver and remember it for later + DocumentSaver* saver = _CreateSaver(ref, exportMode); + if (saver != NULL) { + if (fDocument->WriteLock()) { + if (exportMode == EXPORT_MODE_MESSAGE) + fDocument->SetNativeSaver(saver); + else + fDocument->SetExportSaver(saver); + fDocument->WriteUnlock(); + } + saver->Save(fDocument); + _PickUpActionBeforeSave(); + } + } +// TODO: ... +// _SyncPanels(fSavePanel, fOpenPanel); + } else { + printf("configure file panel\n"); + // configure the file panel + const char* saveText = NULL; + FileSaver* saver = dynamic_cast( + fDocument->NativeSaver()); + if (saver != NULL) + saveText = saver->Ref()->name; + + uint32 requestRefWhat = MSG_SAVE_AS; + bool isExportMode = message->what == MSG_EXPORT_AS + || message->what == MSG_EXPORT; + if (isExportMode) { + requestRefWhat = MSG_EXPORT_AS; + saver = dynamic_cast( + fDocument->ExportSaver()); + if (saver != NULL && saver->Ref()->name != NULL) + saveText = saver->Ref()->name; + } + + BMessage requestRef(requestRefWhat); + if (saveText != NULL) + requestRef.AddString("save text", saveText); + requestRef.AddMessenger("target", BMessenger(this, this)); + be_app->PostMessage(&requestRef); + } + break; + } + case MSG_UNDO: fDocument->CommandStack()->Undo(); break; @@ -247,16 +371,12 @@ MainWindow::MessageReceived(BMessage* message) break; } - case MSG_SET_ICON: - SetIcon(fDocument->Icon()); - break; - case MSG_ADD_SHAPE: { AddStylesCommand* styleCommand = NULL; Style* style = NULL; if (message->HasBool("style")) { - new_style(CurrentColor::Default()->Color(), - fDocument->Icon()->Styles(), &style, &styleCommand); + new_style(fCurrentColor->Color(), + fDocument->Icon()->Styles(), &style, &styleCommand); } AddPathsCommand* pathCommand = NULL; @@ -351,12 +471,9 @@ case MSG_STYLE_TYPE_CHANGED: { fState->DeleteManipulators(); Gradient* gradient = style ? style->Gradient() : NULL; - - if (gradient) { + if (gradient != NULL) { TransformGradientBox* transformBox - = new (nothrow) TransformGradientBox(fCanvasView, - gradient, - NULL); + = new (nothrow) TransformGradientBox(fCanvasView, gradient, NULL); fState->AddManipulator(transformBox); } break; @@ -400,18 +517,27 @@ case MSG_SHAPE_SELECTED: { BWindow::MessageReceived(message); } - fDocument->WriteUnlock(); + if (requiresWriteLock) + fDocument->WriteUnlock(); } bool MainWindow::QuitRequested() { - // forward this to app but return "false" in order - // to have a single code path for quitting - be_app->PostMessage(B_QUIT_REQUESTED); + if (!_CheckSaveIcon(CurrentMessage())) + return false; - return false; + BMessage message(MSG_WINDOW_CLOSED); + + BMessage settings; + StoreSettings(&settings); + message.AddMessage("settings", &settings); + message.AddRect("window frame", Frame()); + + be_app->PostMessage(&message); + + return true; } @@ -483,6 +609,203 @@ MainWindow::MakeEmpty() } +void +MainWindow::Open(const entry_ref& ref, bool append) +{ + BFile file(&ref, B_READ_ONLY); + if (file.InitCheck() < B_OK) + return; + + Icon* icon; + if (append) + icon = new (nothrow) Icon(*fDocument->Icon()); + else + icon = new (nothrow) Icon(); + + if (icon == NULL) { + // TODO: Report error to user. + return; + } + + enum { + REF_NONE = 0, + REF_MESSAGE, + REF_FLAT, + REF_FLAT_ATTR, + REF_SVG + }; + uint32 refMode = REF_NONE; + + // try different file types + FlatIconImporter flatImporter; + status_t ret = flatImporter.Import(icon, &file); + if (ret >= B_OK) { + refMode = REF_FLAT; + } else { + file.Seek(0, SEEK_SET); + MessageImporter msgImporter; + ret = msgImporter.Import(icon, &file); + if (ret >= B_OK) { + refMode = REF_MESSAGE; + } else { + file.Seek(0, SEEK_SET); + SVGImporter svgImporter; + ret = svgImporter.Import(icon, &ref); + if (ret >= B_OK) { + refMode = REF_SVG; + } else { + // fall back to flat icon format but use the icon attribute + ret = B_OK; + attr_info attrInfo; + if (file.GetAttrInfo(kVectorAttrNodeName, &attrInfo) == B_OK) { + if (attrInfo.type != B_VECTOR_ICON_TYPE) + ret = B_ERROR; + // If the attribute is there, we must succeed in reading + // an icon! Otherwise we may overwrite an existing icon + // when the user saves. + uint8* buffer = NULL; + if (ret == B_OK) { + buffer = new(nothrow) uint8[attrInfo.size]; + if (buffer == NULL) + ret = B_NO_MEMORY; + } + if (ret == B_OK) { + ssize_t bytesRead = file.ReadAttr(kVectorAttrNodeName, + B_VECTOR_ICON_TYPE, 0, buffer, attrInfo.size); + if (bytesRead != (ssize_t)attrInfo.size) { + ret = bytesRead < 0 ? (status_t)bytesRead + : B_IO_ERROR; + } + } + if (ret == B_OK) { + ret = flatImporter.Import(icon, buffer, attrInfo.size); + if (ret == B_OK) + refMode = REF_FLAT_ATTR; + } + + delete[] buffer; + } else { + // If there is no icon attribute, simply fall back + // to creating an icon for this file. TODO: We may or may + // not want to display an alert asking the user if that is + // what he wants to do. + refMode = REF_FLAT_ATTR; + } + } + } + } + + if (ret < B_OK) { + // inform user of failure at this point + BString helper(B_TRANSLATE("Opening the document failed!")); + helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); + BAlert* alert = new BAlert( + B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"), + helper.String(), + B_TRANSLATE_WITH_CONTEXT("Bummer", + "Cancel button - error alert"), + NULL, NULL); + // launch alert asynchronously + alert->Go(NULL); + + delete icon; + return; + } + + AutoWriteLocker locker(fDocument); + + // incorporate the loaded icon into the document + // (either replace it or append to it) + fDocument->MakeEmpty(!append); + // if append, the document savers are preserved + fDocument->SetIcon(icon); + if (!append) { + // document got replaced, but we have at + // least one ref already + switch (refMode) { + case REF_MESSAGE: + fDocument->SetNativeSaver(new NativeSaver(ref)); + break; + case REF_FLAT: + fDocument->SetExportSaver( + new SimpleFileSaver(new FlatIconExporter(), ref)); + break; + case REF_FLAT_ATTR: + fDocument->SetNativeSaver( + new AttributeSaver(ref, kVectorAttrNodeName)); + break; + case REF_SVG: + fDocument->SetExportSaver( + new SimpleFileSaver(new SVGExporter(), ref)); + break; + } + } + + locker.Unlock(); + + SetIcon(icon); +} + + +void +MainWindow::Open(const BMessenger& externalObserver, const uint8* data, + size_t size) +{ + if (!_CheckSaveIcon(CurrentMessage())) + return; + + if (!externalObserver.IsValid()) + return; + + Icon* icon = new (nothrow) Icon(); + if (!icon) + return; + + if (data && size > 0) { + // try to open the icon from the provided data + FlatIconImporter flatImporter; + status_t ret = flatImporter.Import(icon, const_cast(data), + size); + // NOTE: the const_cast is a bit ugly, but no harm is done + // the reason is that the LittleEndianBuffer knows read and write + // mode, in this case it is used read-only, and it does not assume + // ownership of the buffer + + if (ret < B_OK) { + // inform user of failure at this point + BString helper(B_TRANSLATE("Opening the icon failed!")); + helper << "\n\n" << B_TRANSLATE("Error: ") << strerror(ret); + BAlert* alert = new BAlert( + B_TRANSLATE_WITH_CONTEXT("bad news", "Title of error alert"), + helper.String(), + B_TRANSLATE_WITH_CONTEXT("Bummer", + "Cancel button - error alert"), + NULL, NULL); + // launch alert asynchronously + alert->Go(NULL); + + delete icon; + return; + } + } + + AutoWriteLocker locker(fDocument); + + SetIcon(NULL); + + // incorporate the loaded icon into the document + // (either replace it or append to it) + fDocument->MakeEmpty(); + fDocument->SetIcon(icon); + + fDocument->SetNativeSaver(new MessengerSaver(externalObserver)); + + locker.Unlock(); + + SetIcon(icon); +} + + void MainWindow::SetIcon(Icon* icon) { @@ -493,20 +816,20 @@ MainWindow::SetIcon(Icon* icon) fIcon = icon; - if (fIcon) + if (fIcon != NULL) fIcon->Acquire(); else MakeEmpty(); fCanvasView->SetIcon(fIcon); - fPathListView->SetPathContainer(fIcon ? fIcon->Paths() : NULL); - fPathListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL); + fPathListView->SetPathContainer(fIcon != NULL ? fIcon->Paths() : NULL); + fPathListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); - fStyleListView->SetStyleContainer(fIcon ? fIcon->Styles() : NULL); - fStyleListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL); + fStyleListView->SetStyleContainer(fIcon != NULL ? fIcon->Styles() : NULL); + fStyleListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); - fShapeListView->SetShapeContainer(fIcon ? fIcon->Shapes() : NULL); + fShapeListView->SetShapeContainer(fIcon != NULL ? fIcon->Shapes() : NULL); // icon previews fIconPreview16Folder->SetIcon(fIcon); @@ -517,7 +840,7 @@ MainWindow::SetIcon(Icon* icon) fIconPreview64->SetIcon(fIcon); // keep this last - if (oldIcon) + if (oldIcon != NULL) oldIcon->Release(); } @@ -528,8 +851,6 @@ MainWindow::SetIcon(Icon* icon) void MainWindow::StoreSettings(BMessage* archive) { - if (archive->ReplaceRect("main window frame", Frame()) != B_OK) - archive->AddRect("main window frame", Frame()); if (archive->ReplaceUInt32("mouse filter mode", fCanvasView->MouseFilterMode()) != B_OK) { archive->AddUInt32("mouse filter mode", @@ -541,15 +862,14 @@ MainWindow::StoreSettings(BMessage* archive) void MainWindow::RestoreSettings(const BMessage* archive) { - BRect frame; - if (archive->FindRect("main window frame", &frame) == B_OK) { - make_sure_frame_is_on_screen(frame, this); - MoveTo(frame.LeftTop()); - ResizeTo(frame.Width(), frame.Height()); - } uint32 mouseFilterMode; - if (archive->FindUInt32("mouse filter mode", &mouseFilterMode) == B_OK) + if (archive->FindUInt32("mouse filter mode", &mouseFilterMode) == B_OK) { fCanvasView->SetMouseFilterMode(mouseFilterMode); + fMouseFilterOffMI->SetMarked(mouseFilterMode == SNAPPING_OFF); + fMouseFilter64MI->SetMarked(mouseFilterMode == SNAPPING_64); + fMouseFilter32MI->SetMarked(mouseFilterMode == SNAPPING_32); + fMouseFilter16MI->SetMarked(mouseFilterMode == SNAPPING_16); + } } @@ -575,6 +895,7 @@ MainWindow::_Init() fCanvasView->SetCatchAllEvents(true); fCanvasView->SetCommandStack(fDocument->CommandStack()); fCanvasView->SetMouseFilterMode(SNAPPING_64); + fMouseFilter64MI->SetMarked(true); // fCanvasView->SetSelection(fDocument->Selection()); fPathListView->SetMenu(fPathMenu); @@ -584,9 +905,10 @@ MainWindow::_Init() fStyleListView->SetMenu(fStyleMenu); fStyleListView->SetCommandStack(fDocument->CommandStack()); fStyleListView->SetSelection(fDocument->Selection()); + fStyleListView->SetCurrentColor(fCurrentColor); fStyleView->SetCommandStack(fDocument->CommandStack()); - fStyleView->SetCurrentColor(CurrentColor::Default()); + fStyleView->SetCurrentColor(fCurrentColor); fShapeListView->SetMenu(fShapeMenu); fShapeListView->SetCommandStack(fDocument->CommandStack()); @@ -602,7 +924,7 @@ MainWindow::_Init() fDocument->CommandStack()->AddObserver(this); - fSwatchGroup->SetCurrentColor(CurrentColor::Default()); + fSwatchGroup->SetCurrentColor(fCurrentColor); SetIcon(fDocument->Icon()); @@ -821,13 +1143,21 @@ MainWindow::_CreateMenuBar() #undef B_TRANSLATE_CONTEXT #define B_TRANSLATE_CONTEXT "Icon-O-Matic-Menu-File" - - fileMenu->AddItem(new BMenuItem(B_TRANSLATE("New"), - new BMessage(MSG_NEW), 'N')); - fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Open"B_UTF8_ELLIPSIS), - new BMessage(MSG_OPEN), 'O')); - fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Append"B_UTF8_ELLIPSIS), - new BMessage(MSG_APPEND), 'O', B_SHIFT_KEY)); + + BMenuItem* item = new BMenuItem(B_TRANSLATE("New"), + new BMessage(MSG_NEW), 'N'); + fileMenu->AddItem(item); + item->SetTarget(be_app); + item = new BMenuItem(B_TRANSLATE("Open"B_UTF8_ELLIPSIS), + new BMessage(MSG_OPEN), 'O'); + fileMenu->AddItem(item); + item->SetTarget(be_app); + BMessage* appendMessage = new BMessage(MSG_APPEND); + appendMessage->AddPointer("window", this); + item = new BMenuItem(B_TRANSLATE("Append"B_UTF8_ELLIPSIS), + appendMessage, 'O', B_SHIFT_KEY); + fileMenu->AddItem(item); + item->SetTarget(be_app); fileMenu->AddSeparatorItem(); fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save"), new BMessage(MSG_SAVE), 'S')); @@ -839,10 +1169,12 @@ MainWindow::_CreateMenuBar() fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Export as"B_UTF8_ELLIPSIS), new BMessage(MSG_EXPORT_AS), 'P', B_SHIFT_KEY)); fileMenu->AddSeparatorItem(); - fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), - new BMessage(B_QUIT_REQUESTED), 'Q')); - fileMenu->SetTargetForItems(be_app); - + fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Close"), + new BMessage(B_QUIT_REQUESTED), 'W')); + item = new BMenuItem(B_TRANSLATE("Quit"), + new BMessage(B_QUIT_REQUESTED), 'Q'); + fileMenu->AddItem(item); + item->SetTarget(be_app); // Edit #undef B_TRANSLATE_CONTEXT @@ -869,21 +1201,24 @@ MainWindow::_CreateMenuBar() BMenu* filterModeMenu = new BMenu(B_TRANSLATE("Snap to grid")); BMessage* message = new BMessage(MSG_MOUSE_FILTER_MODE); message->AddInt32("mode", SNAPPING_OFF); - filterModeMenu->AddItem(new BMenuItem(B_TRANSLATE("Off"), message, '4')); + fMouseFilterOffMI = new BMenuItem(B_TRANSLATE("Off"), message, '4'); + filterModeMenu->AddItem(fMouseFilterOffMI); message = new BMessage(MSG_MOUSE_FILTER_MODE); message->AddInt32("mode", SNAPPING_64); - filterModeMenu->AddItem(new BMenuItem("64 x 64", message, '3')); + fMouseFilter64MI = new BMenuItem("64 x 64", message, '3'); + filterModeMenu->AddItem(fMouseFilter64MI); message = new BMessage(MSG_MOUSE_FILTER_MODE); message->AddInt32("mode", SNAPPING_32); - filterModeMenu->AddItem(new BMenuItem("32 x 32", message, '2')); + fMouseFilter32MI = new BMenuItem("32 x 32", message, '2'); + filterModeMenu->AddItem(fMouseFilter32MI); message = new BMessage(MSG_MOUSE_FILTER_MODE); message->AddInt32("mode", SNAPPING_16); - filterModeMenu->AddItem(new BMenuItem("16 x 16", message, '1')); + fMouseFilter16MI = new BMenuItem("16 x 16", message, '1'); + filterModeMenu->AddItem(fMouseFilter16MI); - filterModeMenu->ItemAt(1)->SetMarked(true); filterModeMenu->SetRadioMode(true); settingsMenu->AddItem(filterModeMenu); @@ -907,3 +1242,131 @@ MainWindow::_ImproveScrollBarLayout(BView* target) scrollBar->ResizeBy(0, 1); } } + + +// #pragma mark - + + +bool +MainWindow::_CheckSaveIcon(const BMessage* currentMessage) +{ + if (fDocument->IsEmpty() || fDocument->CommandStack()->IsSaved()) + return true; + + // Make sure the user sees us. + Activate(); + + BAlert* alert = new BAlert("save", + B_TRANSLATE("Save changes to current icon?"), B_TRANSLATE("Discard"), + B_TRANSLATE("Cancel"), B_TRANSLATE("Save")); + int32 choice = alert->Go(); + switch (choice) { + case 0: + // discard + return true; + case 1: + // cancel + return false; + case 2: + default: + // cancel (save first) but pick up what we were doing before + PostMessage(MSG_SAVE); + if (currentMessage != NULL) { + delete fMessageAfterSave; + fMessageAfterSave = new BMessage(*currentMessage); + } + return false; + } +} + + +void +MainWindow::_PickUpActionBeforeSave() +{ + if (fDocument->WriteLock()) { + fDocument->CommandStack()->Save(); + fDocument->WriteUnlock(); + } + + if (fMessageAfterSave == NULL) + return; + + PostMessage(fMessageAfterSave); + delete fMessageAfterSave; + fMessageAfterSave = NULL; +} + + +// #pragma mark - + + +void +MainWindow::_MakeIconEmpty() +{ + if (!_CheckSaveIcon(CurrentMessage())) + return; + + AutoWriteLocker locker(fDocument); + + MakeEmpty(); + fDocument->MakeEmpty(); + + locker.Unlock(); +} + + +DocumentSaver* +MainWindow::_CreateSaver(const entry_ref& ref, uint32 exportMode) +{ + DocumentSaver* saver; + + switch (exportMode) { + case EXPORT_MODE_FLAT_ICON: + saver = new SimpleFileSaver(new FlatIconExporter(), ref); + break; + + case EXPORT_MODE_ICON_ATTR: + case EXPORT_MODE_ICON_MIME_ATTR: { + const char* attrName + = exportMode == EXPORT_MODE_ICON_ATTR ? + kVectorAttrNodeName : kVectorAttrMimeName; + saver = new AttributeSaver(ref, attrName); + break; + } + + case EXPORT_MODE_ICON_RDEF: + saver = new SimpleFileSaver(new RDefExporter(), ref); + break; + case EXPORT_MODE_ICON_SOURCE: + saver = new SimpleFileSaver(new SourceExporter(), ref); + break; + + case EXPORT_MODE_BITMAP_16: + saver = new SimpleFileSaver(new BitmapExporter(16), ref); + break; + case EXPORT_MODE_BITMAP_32: + saver = new SimpleFileSaver(new BitmapExporter(32), ref); + break; + case EXPORT_MODE_BITMAP_64: + saver = new SimpleFileSaver(new BitmapExporter(64), ref); + break; + + case EXPORT_MODE_BITMAP_SET: + saver = new BitmapSetSaver(ref); + break; + + case EXPORT_MODE_SVG: + saver = new SimpleFileSaver(new SVGExporter(), ref); + break; + + case EXPORT_MODE_MESSAGE: + default: + saver = new NativeSaver(ref); + break; + } + + return saver; +} + + + diff --git a/src/apps/icon-o-matic/MainWindow.h b/src/apps/icon-o-matic/MainWindow.h index 109069f127..d859d50c31 100644 --- a/src/apps/icon-o-matic/MainWindow.h +++ b/src/apps/icon-o-matic/MainWindow.h @@ -17,7 +17,9 @@ class BMenu; class BMenuBar; class BMenuItem; class CanvasView; +class CurrentColor; class Document; +class DocumentSaver; class IconObjectListView; class IconEditorApp; class IconView; @@ -38,14 +40,16 @@ _USING_ICON_NAMESPACE class MultipleManipulatorState; enum { - MSG_SET_ICON = 'sicn', + MSG_OPEN = 'open', + MSG_APPEND = 'apnd', + MSG_SAVE = 'save', + MSG_EXPORT = 'xprt', }; class MainWindow : public BWindow, public Observer { public: - MainWindow(IconEditorApp* app, - Document* document, + MainWindow(BRect frame, IconEditorApp* app, const BMessage* settings); virtual ~MainWindow(); @@ -64,6 +68,11 @@ public: void MakeEmpty(); void SetIcon(Icon* icon); + void Open(const entry_ref& ref, + bool append = false); + void Open(const BMessenger& externalObserver, + const uint8* data, size_t size); + void StoreSettings(BMessage* archive); void RestoreSettings(const BMessage* archive); @@ -74,10 +83,20 @@ private: void _ImproveScrollBarLayout(BView* target); + bool _CheckSaveIcon(const BMessage* currentMessage); + void _PickUpActionBeforeSave(); + + void _MakeIconEmpty(); + DocumentSaver* _CreateSaver(const entry_ref& ref, + uint32 exportMode); + private: IconEditorApp* fApp; Document* fDocument; + CurrentColor* fCurrentColor; Icon* fIcon; + + BMessage* fMessageAfterSave; BMenu* fPathMenu; BMenu* fStyleMenu; @@ -88,6 +107,10 @@ private: BMenuItem* fUndoMI; BMenuItem* fRedoMI; + BMenuItem* fMouseFilterOffMI; + BMenuItem* fMouseFilter64MI; + BMenuItem* fMouseFilter32MI; + BMenuItem* fMouseFilter16MI; CanvasView* fCanvasView; SwatchGroup* fSwatchGroup; diff --git a/src/apps/icon-o-matic/gui/GradientControl.cpp b/src/apps/icon-o-matic/gui/GradientControl.cpp index 5d071ad18e..be88d4e183 100644 --- a/src/apps/icon-o-matic/gui/GradientControl.cpp +++ b/src/apps/icon-o-matic/gui/GradientControl.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -338,10 +339,10 @@ GradientControl::Draw(BRect updateRect) bool isFocus = IsFocus() && Window()->IsActive(); rgb_color bg = LowColor(); + rgb_color black; rgb_color shadow; rgb_color darkShadow; rgb_color light; - rgb_color black; if (fEnabled) { shadow = tint_color(bg, B_DARKEN_1_TINT); @@ -355,16 +356,25 @@ GradientControl::Draw(BRect updateRect) black = tint_color(bg, B_DARKEN_2_TINT); } - rgb_color focus = isFocus ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) - : black; + rgb_color focus = isFocus ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) : black; - stroke_frame(this, b, shadow, shadow, light, light); - b.InsetBy(1.0, 1.0); - if (isFocus) - stroke_frame(this, b, focus, focus, focus, focus); - else - stroke_frame(this, b, darkShadow, darkShadow, bg, bg); - b.InsetBy(1.0, 1.0); + uint32 flags = 0; + + if (be_control_look != NULL) { + if (!fEnabled) + flags |= BControlLook::B_DISABLED; + if (isFocus) + flags |= BControlLook::B_FOCUSED; + be_control_look->DrawTextControlBorder(this, b, updateRect, bg, flags); + } else { + stroke_frame(this, b, shadow, shadow, light, light); + b.InsetBy(1.0, 1.0); + if (isFocus) + stroke_frame(this, b, focus, focus, focus, focus); + else + stroke_frame(this, b, darkShadow, darkShadow, bg, bg); + b.InsetBy(1.0, 1.0); + } // DrawBitmapAsync(fGradientBitmap, b.LeftTop()); // Sync(); @@ -399,22 +409,30 @@ GradientControl::Draw(BRect updateRect) if (i == fDropIndex) SetLowColor(255, 0, 0, 255); - StrokeTriangle(markerPos, markerPos + leftBottom, - markerPos + rightBottom, B_SOLID_LOW); - if (fEnabled) { - SetHighColor(step->color); + if (be_control_look != NULL) { + // TODO: Drop indication! + BRect rect(markerPos.x + leftBottom.x, markerPos.y, + markerPos.x + rightBottom.x, markerPos.y + rightBottom.y); + be_control_look->DrawSliderTriangle(this, rect, updateRect, bg, + step->color, flags, B_HORIZONTAL); } else { - rgb_color c = step->color; - c.red = (uint8)(((uint32)bg.red + (uint32)c.red) / 2); - c.green = (uint8)(((uint32)bg.green + (uint32)c.green) / 2); - c.blue = (uint8)(((uint32)bg.blue + (uint32)c.blue) / 2); - SetHighColor(c); + StrokeTriangle(markerPos, markerPos + leftBottom, + markerPos + rightBottom, B_SOLID_LOW); + if (fEnabled) { + SetHighColor(step->color); + } else { + rgb_color c = step->color; + c.red = (uint8)(((uint32)bg.red + (uint32)c.red) / 2); + c.green = (uint8)(((uint32)bg.green + (uint32)c.green) / 2); + c.blue = (uint8)(((uint32)bg.blue + (uint32)c.blue) / 2); + SetHighColor(c); + } + FillTriangle(markerPos + BPoint(0.0, 1.0), + markerPos + leftBottom + BPoint(1.0, 0.0), + markerPos + rightBottom + BPoint(-1.0, 0.0)); + StrokeLine(markerPos + leftBottom + BPoint(0.0, 1.0), + markerPos + rightBottom + BPoint(0.0, 1.0), B_SOLID_LOW); } - FillTriangle(markerPos + BPoint(0.0, 1.0), - markerPos + leftBottom + BPoint(1.0, 0.0), - markerPos + rightBottom + BPoint(-1.0, 0.0)); - StrokeLine(markerPos + leftBottom + BPoint(0.0, 1.0), - markerPos + rightBottom + BPoint(0.0, 1.0), B_SOLID_LOW); } } diff --git a/src/apps/icon-o-matic/gui/StyleListView.cpp b/src/apps/icon-o-matic/gui/StyleListView.cpp index d479ff2f05..03132f7910 100644 --- a/src/apps/icon-o-matic/gui/StyleListView.cpp +++ b/src/apps/icon-o-matic/gui/StyleListView.cpp @@ -1,11 +1,9 @@ /* - * Copyright 2006-2009, Haiku, Inc. All rights reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Stephan Aßmus + * Copyright 2006-2009, 2011, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. */ + #include "StyleListView.h" #include @@ -255,6 +253,7 @@ StyleListView::StyleListView(BRect frame, fStyleContainer(NULL), fShapeContainer(NULL), fCommandStack(NULL), + fCurrentColor(NULL), fCurrentShape(NULL), fShapeListener(new ShapeStyleListener(this)), @@ -289,8 +288,16 @@ StyleListView::MessageReceived(BMessage* message) case MSG_ADD: { Style* style; AddStylesCommand* command; - new_style(CurrentColor::Default()->Color(), - fStyleContainer, &style, &command); + rgb_color color; + if (fCurrentColor != NULL) + color = fCurrentColor->Color(); + else { + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = 255; + } + new_style(color, fStyleContainer, &style, &command); fCommandStack->Perform(command); break; } @@ -672,6 +679,13 @@ StyleListView::SetCommandStack(CommandStack* stack) fCommandStack = stack; } +// SetCurrentColor +void +StyleListView::SetCurrentColor(CurrentColor* color) +{ + fCurrentColor = color; +} + // SetCurrentShape void StyleListView::SetCurrentShape(Shape* shape) diff --git a/src/apps/icon-o-matic/gui/StyleListView.h b/src/apps/icon-o-matic/gui/StyleListView.h index 642257963e..5029dce926 100644 --- a/src/apps/icon-o-matic/gui/StyleListView.h +++ b/src/apps/icon-o-matic/gui/StyleListView.h @@ -1,9 +1,6 @@ /* - * Copyright 2006-2007, Haiku. + * Copyright 2006-2007, 2011, Stephan Aßmus . * Distributed under the terms of the MIT License. - * - * Authors: - * Stephan Aßmus */ #ifndef STYLE_LIST_VIEW_H #define STYLE_LIST_VIEW_H @@ -16,6 +13,7 @@ class BMenu; class BMenuItem; class CommandStack; +class CurrentColor; class Selection; class ShapeStyleListener; class StyleListItem; @@ -30,13 +28,11 @@ _END_ICON_NAMESPACE _USING_ICON_NAMESPACE -class StyleListView : public SimpleListView, - public StyleContainerListener { - public: - StyleListView(BRect frame, - const char* name, - BMessage* selectionMessage = NULL, - BHandler* target = NULL); +class StyleListView : public SimpleListView, public StyleContainerListener { +public: + StyleListView(BRect frame, const char* name, + BMessage* selectionMessage = NULL, + BHandler* target = NULL); virtual ~StyleListView(); // SimpleListView interface @@ -48,9 +44,10 @@ class StyleListView : public SimpleListView, virtual void MakeDragMessage(BMessage* message) const; - virtual bool AcceptDragMessage(const BMessage* message) const; + virtual bool AcceptDragMessage( + const BMessage* message) const; virtual void SetDropTargetRect(const BMessage* message, - BPoint where); + BPoint where); virtual void MoveItems(BList& items, int32 toIndex); virtual void CopyItems(BList& items, int32 toIndex); @@ -70,18 +67,19 @@ class StyleListView : public SimpleListView, void SetStyleContainer(StyleContainer* container); void SetShapeContainer(ShapeContainer* container); void SetCommandStack(CommandStack* stack); + void SetCurrentColor(CurrentColor* color); void SetCurrentShape(Shape* shape); Shape* CurrentShape() const { return fCurrentShape; } - private: +private: bool _AddStyle(Style* style, int32 index); bool _RemoveStyle(Style* style); StyleListItem* _ItemForStyle(Style* style) const; - friend class ShapeStyleListener; + friend class ShapeStyleListener; void _UpdateMarks(); void _SetStyleMarked(Style* style, bool marked); void _UpdateMenu(); @@ -91,6 +89,7 @@ class StyleListView : public SimpleListView, StyleContainer* fStyleContainer; ShapeContainer* fShapeContainer; CommandStack* fCommandStack; + CurrentColor* fCurrentColor; Shape* fCurrentShape; // the style item will be marked that @@ -105,4 +104,5 @@ class StyleListView : public SimpleListView, BMenuItem* fRemoveMI; }; + #endif // STYLE_LIST_VIEW_H diff --git a/src/apps/icon-o-matic/gui/SwatchGroup.cpp b/src/apps/icon-o-matic/gui/SwatchGroup.cpp index 8ca4e05086..38f6ba1b18 100644 --- a/src/apps/icon-o-matic/gui/SwatchGroup.cpp +++ b/src/apps/icon-o-matic/gui/SwatchGroup.cpp @@ -52,7 +52,9 @@ SwatchGroup::SwatchGroup(BRect frame) float v = 1.0; rgb_color color; color.alpha = 255; - float r, g, b; + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; for (int32 i = 0; i < 20; i++) { if (i < 10) { h = ((float)i / 9.0) * 6.0; @@ -295,7 +297,9 @@ SwatchGroup::_SetColor(rgb_color color) void SwatchGroup::_SetColor(float h, float s, float v, uint8 a) { - float r, g, b; + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; HSV_to_RGB(h, s, v, r, g, b); rgb_color color; diff --git a/src/apps/icon-o-matic/style/CurrentColor.cpp b/src/apps/icon-o-matic/style/CurrentColor.cpp index d506469418..da2001d869 100644 --- a/src/apps/icon-o-matic/style/CurrentColor.cpp +++ b/src/apps/icon-o-matic/style/CurrentColor.cpp @@ -1,11 +1,9 @@ /* - * Copyright 2006, Haiku. - * Distributed under the terms of the MIT License. - * - * Authors: - * Stephan Aßmus + * Copyright 2006, 2011, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. */ + #include "CurrentColor.h" #include @@ -14,31 +12,19 @@ #include "ui_defines.h" -// init global CurrentColor instance -CurrentColor -CurrentColor::fDefaultInstance; - -// constructor CurrentColor::CurrentColor() : Observable(), fColor(kBlack) { } -// destructor + CurrentColor::~CurrentColor() { } -// Default -CurrentColor* -CurrentColor::Default() -{ - return &fDefaultInstance; -} -// SetColor void CurrentColor::SetColor(rgb_color color) { diff --git a/src/apps/icon-o-matic/style/CurrentColor.h b/src/apps/icon-o-matic/style/CurrentColor.h index 8b3000fcb7..2e5eaf9c0d 100644 --- a/src/apps/icon-o-matic/style/CurrentColor.h +++ b/src/apps/icon-o-matic/style/CurrentColor.h @@ -1,33 +1,28 @@ /* - * Copyright 2006, Haiku. - * Distributed under the terms of the MIT License. - * - * Authors: - * Stephan Aßmus + * Copyright 2006, 2011, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. */ - #ifndef CURRENT_COLOR_H #define CURRENT_COLOR_H + #include #include "Observable.h" + class CurrentColor : public Observable { - public: +public: CurrentColor(); virtual ~CurrentColor(); - static CurrentColor* Default(); - void SetColor(rgb_color color); inline rgb_color Color() const { return fColor; } - private: +private: rgb_color fColor; - - static CurrentColor fDefaultInstance; }; + #endif // CURRENT_COLOR_H