From dad7391aaa953728d576ee37cfb31900578b961e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Thu, 16 Oct 2008 20:45:48 +0000 Subject: [PATCH] * Implement a method to track the need to save changes. Enabled the Save menu item accordingly and ask the user to save changes when he quits the window and there are any changes. Most changes are tracked in a robust way, only icons are tracked with a more fuzzy method (the actual data is not compared). Whenever an icon (of the app itself or one of its types) is changed, then the preflet is convinced of the need to save, regardless of the icon is removed again or is changed to the visually same icon. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28182 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../filetypes/ApplicationTypeWindow.cpp | 287 +++++++++++++++--- .../filetypes/ApplicationTypeWindow.h | 35 +++ 2 files changed, 275 insertions(+), 47 deletions(-) diff --git a/src/preferences/filetypes/ApplicationTypeWindow.cpp b/src/preferences/filetypes/ApplicationTypeWindow.cpp index 7022cd6f00..63387428a9 100644 --- a/src/preferences/filetypes/ApplicationTypeWindow.cpp +++ b/src/preferences/filetypes/ApplicationTypeWindow.cpp @@ -12,7 +12,6 @@ #include "StringView.h" #include "TypeListWindow.h" -#include #include #include #include @@ -38,12 +37,18 @@ const uint32 kMsgSave = 'save'; -const uint32 kMsgFlagsChanged = 'flgc'; +const uint32 kMsgSignatureChanged = 'sgch'; +const uint32 kMsgToggleAppFlags = 'tglf'; +const uint32 kMsgAppFlagsChanged = 'afch'; + +const uint32 kMsgIconChanged = 'icch'; +const uint32 kMsgTypeIconsChanged = 'tich'; const uint32 kMsgTypeSelected = 'tpsl'; const uint32 kMsgAddType = 'adtp'; const uint32 kMsgTypeAdded = 'tpad'; const uint32 kMsgRemoveType = 'rmtp'; +const uint32 kMsgTypeRemoved = 'tprm'; class SupportedTypeItem : public BStringItem { @@ -193,7 +198,8 @@ SupportedTypeListView::AcceptsDrag(const BMessage* message) ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entry) : BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position), "Application Type", B_TITLED_WINDOW, - B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) + B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS), + fChangedProperties(0) { // add the menu @@ -201,7 +207,9 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr AddChild(menuBar); BMenu* menu = new BMenu("File"); - menu->AddItem(new BMenuItem("Save", new BMessage(kMsgSave), 'S', B_COMMAND_KEY)); + fSaveMenuItem = new BMenuItem("Save", new BMessage(kMsgSave), 'S'); + fSaveMenuItem->SetEnabled(false); + menu->AddItem(fSaveMenuItem); BMenuItem* item; menu->AddItem(item = new BMenuItem("Save Into Resource File" B_UTF8_ELLIPSIS, NULL)); @@ -222,7 +230,9 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect = topView->Bounds().InsetByCopy(8.0f, 8.0f); fSignatureControl = new BTextControl(rect, "signature", "Signature:", NULL, - NULL, B_FOLLOW_LEFT_RIGHT); + new BMessage(kMsgSignatureChanged), B_FOLLOW_LEFT_RIGHT); + fSignatureControl->SetModificationMessage( + new BMessage(kMsgSignatureChanged)); fSignatureControl->SetDivider(fSignatureControl->StringWidth( fSignatureControl->Label()) + 4.0f); float width, height; @@ -255,38 +265,44 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr topView->AddChild(box); fFlagsCheckBox = new BCheckBox(rect, "flags", "Application Flags", - new BMessage(kMsgFlagsChanged)); + new BMessage(kMsgToggleAppFlags)); fFlagsCheckBox->SetValue(B_CONTROL_ON); fFlagsCheckBox->ResizeToPreferred(); box->SetLabel(fFlagsCheckBox); rect.top = fFlagsCheckBox->Bounds().Height() + 4.0f; - fSingleLaunchButton = new BRadioButton(rect, "single", "Single Launch", NULL); + fSingleLaunchButton = new BRadioButton(rect, "single", "Single Launch", + new BMessage(kMsgAppFlagsChanged)); fSingleLaunchButton->ResizeToPreferred(); box->AddChild(fSingleLaunchButton); rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f); - fMultipleLaunchButton = new BRadioButton(rect, "multiple", "Multiple Launch", NULL); + fMultipleLaunchButton = new BRadioButton(rect, "multiple", + "Multiple Launch", new BMessage(kMsgAppFlagsChanged)); fMultipleLaunchButton->ResizeToPreferred(); box->AddChild(fMultipleLaunchButton); rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f); - fExclusiveLaunchButton = new BRadioButton(rect, "exclusive", "Exclusive Launch", NULL); + fExclusiveLaunchButton = new BRadioButton(rect, "exclusive", + "Exclusive Launch", new BMessage(kMsgAppFlagsChanged)); fExclusiveLaunchButton->ResizeToPreferred(); box->AddChild(fExclusiveLaunchButton); rect.top = fSingleLaunchButton->Frame().top; rect.left = fExclusiveLaunchButton->Frame().right + 4.0f; - fArgsOnlyCheckBox = new BCheckBox(rect, "args only", "Args Only", NULL); + fArgsOnlyCheckBox = new BCheckBox(rect, "args only", "Args Only", + new BMessage(kMsgAppFlagsChanged)); fArgsOnlyCheckBox->ResizeToPreferred(); box->AddChild(fArgsOnlyCheckBox); rect.top += fArgsOnlyCheckBox->Bounds().Height(); - fBackgroundAppCheckBox = new BCheckBox(rect, "background", "Background App", NULL); + fBackgroundAppCheckBox = new BCheckBox(rect, "background", + "Background App", new BMessage(kMsgAppFlagsChanged)); fBackgroundAppCheckBox->ResizeToPreferred(); box->AddChild(fBackgroundAppCheckBox); - box->ResizeTo(box->Bounds().Width(), fExclusiveLaunchButton->Frame().bottom + 8.0f); + box->ResizeTo(box->Bounds().Width(), + fExclusiveLaunchButton->Frame().bottom + 8.0f); // "Icon" group @@ -315,6 +331,7 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr if (rect.top < fontHeight.ascent + fontHeight.descent + 4.0f) rect.top = fontHeight.ascent + fontHeight.descent + 4.0f; fIconView = new IconView(rect, "icon"); + fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); box->AddChild(fIconView); // "Supported Types" group @@ -366,6 +383,7 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect.right = rect.left + B_LARGE_ICON - 1.0f; rect.bottom = rect.top + B_LARGE_ICON - 1.0f; fTypeIconView = new IconView(rect, "type icon", B_FOLLOW_RIGHT | B_FOLLOW_TOP); + fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); typeBox->AddChild(fTypeIconView); // "Version Info" group @@ -560,7 +578,10 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) // Set Controls + fSignatureControl->SetModificationMessage(NULL); fSignatureControl->SetText(signature); + fSignatureControl->SetModificationMessage( + new BMessage(kMsgSignatureChanged)); // flags @@ -593,7 +614,9 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) else fIcon.Unset(); + fIconView->SetModificationMessage(NULL); fIconView->SetTo(&fIcon); + fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); // supported types @@ -613,7 +636,9 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) fTypeListView->AddItem(item); } fTypeListView->SortItems(&SupportedTypeItem::Compare); + fTypeIconView->SetModificationMessage(NULL); fTypeIconView->SetTo(NULL); + fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); fTypeIconView->SetEnabled(false); fRemoveTypeButton->SetEnabled(false); @@ -638,6 +663,18 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) fShortDescriptionControl->SetText(versionInfo.short_info); fLongDescriptionView->SetText(versionInfo.long_info); + + // store original data + + fOriginalInfo.signature = signature; + fOriginalInfo.flags = flags; + fOriginalInfo.versionInfo = versionInfo; + fOriginalInfo.supportedTypes = supportedTypes; + fOriginalInfo.iconChanged = false; + fOriginalInfo.typeIconsChanged = false; + + fChangedProperties = 0; + _CheckSaveMenuItem(0); } @@ -683,40 +720,9 @@ ApplicationTypeWindow::_Save() // Retrieve Info - uint32 flags = 0; - if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { - if (fSingleLaunchButton->Value() != B_CONTROL_OFF) - flags |= B_SINGLE_LAUNCH; - else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) - flags |= B_MULTIPLE_LAUNCH; - else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) - flags |= B_EXCLUSIVE_LAUNCH; - - if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) - flags |= B_ARGV_ONLY; - if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) - flags |= B_BACKGROUND_APP; - } - - BMessage supportedTypes; - for (int32 i = 0; i < fTypeListView->CountItems(); i++) { - SupportedTypeItem* item = dynamic_cast( - fTypeListView->ItemAt(i)); - - if (item != NULL) - supportedTypes.AddString("types", item->Type()); - } - - version_info versionInfo; - versionInfo.major = atol(fMajorVersionControl->Text()); - versionInfo.middle = atol(fMiddleVersionControl->Text()); - versionInfo.minor = atol(fMinorVersionControl->Text()); - versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); - versionInfo.internal = atol(fInternalVersionControl->Text()); - strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), - sizeof(versionInfo.short_info)); - strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), - sizeof(versionInfo.long_info)); + uint32 flags = _Flags(); + BMessage supportedTypes = _SupportedTypes(); + version_info versionInfo = _VersionInfo(); // Save @@ -739,9 +745,147 @@ ApplicationTypeWindow::_Save() if (item != NULL) item->Icon().CopyTo(info, item->Type(), true); } + + // reset the saved info + fOriginalInfo.signature = fSignatureControl->Text(); + fOriginalInfo.flags = flags; + fOriginalInfo.versionInfo = versionInfo; + fOriginalInfo.supportedTypes = supportedTypes; + fOriginalInfo.iconChanged = false; + fOriginalInfo.typeIconsChanged = false; + + fChangedProperties = 0; + _CheckSaveMenuItem(0); } +void +ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags) +{ + fChangedProperties = _NeedsSaving(flags); + fSaveMenuItem->SetEnabled(fChangedProperties != 0); +} + + +bool +operator!=(const version_info& a, const version_info& b) +{ + return a.major != b.major || a.middle != b.middle || a.minor != b.minor + || a.variety != b.variety || a.internal != b.internal + || strcmp(a.short_info, b.short_info) != 0 + || strcmp(a.long_info, b.long_info) != 0; +} + + +uint32 +ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const +{ + uint32 flags = fChangedProperties; + if (_flags & CHECK_SIGNATUR) { + if (fOriginalInfo.signature != fSignatureControl->Text()) + flags |= CHECK_SIGNATUR; + else + flags &= ~CHECK_SIGNATUR; + } + + if (_flags & CHECK_FLAGS) { + if (fOriginalInfo.flags != _Flags()) + flags |= CHECK_FLAGS; + else + flags &= ~CHECK_FLAGS; + } + + if (_flags & CHECK_VERSION) { + if (fOriginalInfo.versionInfo != _VersionInfo()) + flags |= CHECK_VERSION; + else + flags &= ~CHECK_VERSION; + } + + if (_flags & CHECK_ICON) { + if (fOriginalInfo.iconChanged) + flags |= CHECK_ICON; + else + flags &= ~CHECK_ICON; + } + + if (_flags & CHECK_TYPES) { + if (!fOriginalInfo.supportedTypes.CompareData(_SupportedTypes())) + flags |= CHECK_TYPES; + else + flags &= ~CHECK_TYPES; + } + + if (_flags & CHECK_TYPE_ICONS) { + if (fOriginalInfo.typeIconsChanged) + flags |= CHECK_TYPE_ICONS; + else + flags &= ~CHECK_TYPE_ICONS; + } + + return flags; +} + + +// #pragma mark - + + +uint32 +ApplicationTypeWindow::_Flags() const +{ + uint32 flags = 0; + if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { + if (fSingleLaunchButton->Value() != B_CONTROL_OFF) + flags |= B_SINGLE_LAUNCH; + else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) + flags |= B_MULTIPLE_LAUNCH; + else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) + flags |= B_EXCLUSIVE_LAUNCH; + + if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) + flags |= B_ARGV_ONLY; + if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) + flags |= B_BACKGROUND_APP; + } + return flags; +} + + +BMessage +ApplicationTypeWindow::_SupportedTypes() const +{ + BMessage supportedTypes; + for (int32 i = 0; i < fTypeListView->CountItems(); i++) { + SupportedTypeItem* item = dynamic_cast( + fTypeListView->ItemAt(i)); + + if (item != NULL) + supportedTypes.AddString("types", item->Type()); + } + return supportedTypes; +} + + +version_info +ApplicationTypeWindow::_VersionInfo() const +{ + version_info versionInfo; + versionInfo.major = atol(fMajorVersionControl->Text()); + versionInfo.middle = atol(fMiddleVersionControl->Text()); + versionInfo.minor = atol(fMinorVersionControl->Text()); + versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); + versionInfo.internal = atol(fInternalVersionControl->Text()); + strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), + sizeof(versionInfo.short_info)); + strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), + sizeof(versionInfo.long_info)); + return versionInfo; +} + + +// #pragma mark - + + void ApplicationTypeWindow::FrameResized(float width, float height) { @@ -754,8 +898,27 @@ void ApplicationTypeWindow::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgFlagsChanged: + case kMsgToggleAppFlags: _UpdateAppFlagsEnabled(); + _CheckSaveMenuItem(CHECK_FLAGS); + break; + + case kMsgSignatureChanged: + _CheckSaveMenuItem(CHECK_SIGNATUR); + break; + + case kMsgAppFlagsChanged: + _CheckSaveMenuItem(CHECK_FLAGS); + break; + + case kMsgIconChanged: + fOriginalInfo.iconChanged = true; + _CheckSaveMenuItem(CHECK_ICON); + break; + + case kMsgTypeIconsChanged: + fOriginalInfo.typeIconsChanged = true; + _CheckSaveMenuItem(CHECK_TYPE_ICONS); break; case kMsgSave: @@ -768,9 +931,14 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) if (message->FindInt32("index", &index) == B_OK) { SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index); + fTypeIconView->SetModificationMessage(NULL); fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); + fTypeIconView->SetModificationMessage( + new BMessage(kMsgTypeIconsChanged)); fTypeIconView->SetEnabled(item != NULL); fRemoveTypeButton->SetEnabled(item != NULL); + + _CheckSaveMenuItem(CHECK_TYPES); } break; } @@ -817,6 +985,8 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) fTypeListView->AddItem(newItem, insertAt); fTypeListView->Select(insertAt); + + _CheckSaveMenuItem(CHECK_TYPES); break; } @@ -827,9 +997,14 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) break; delete fTypeListView->RemoveItem(index); + fTypeIconView->SetModificationMessage(NULL); fTypeIconView->SetTo(NULL); + fTypeIconView->SetModificationMessage( + new BMessage(kMsgTypeIconsChanged)); fTypeIconView->SetEnabled(false); fRemoveTypeButton->SetEnabled(false); + + _CheckSaveMenuItem(CHECK_TYPES); break; } @@ -852,6 +1027,8 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) // TODO: update supported types names // if (which == B_MIME_TYPE_DELETED) + +// _CheckSaveMenuItem(...); break; default: @@ -863,6 +1040,22 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) bool ApplicationTypeWindow::QuitRequested() { + if (_NeedsSaving(CHECK_ALL) != 0) { + BAlert* alert = new BAlert("Save Request", "Do you want to save " + "the changes?", "Quit, Don't save", "Cancel", "Save", + B_WIDTH_AS_USUAL, B_WARNING_ALERT); + int32 choice = alert->Go(); + switch (choice) { + case 0: + break; + case 1: + return false; + case 2: + _Save(); + break; + } + } + be_app->PostMessage(kMsgTypeWindowClosed); return true; } diff --git a/src/preferences/filetypes/ApplicationTypeWindow.h b/src/preferences/filetypes/ApplicationTypeWindow.h index 169b6e6a61..11ab5155ed 100644 --- a/src/preferences/filetypes/ApplicationTypeWindow.h +++ b/src/preferences/filetypes/ApplicationTypeWindow.h @@ -8,6 +8,7 @@ #include "IconView.h" +#include #include #include #include @@ -39,8 +40,39 @@ class ApplicationTypeWindow : public BWindow { void _MakeNumberTextControl(BTextControl* control); void _Save(); + uint32 _Flags() const; + BMessage _SupportedTypes() const; + version_info _VersionInfo() const; + + void _CheckSaveMenuItem(uint32 flags); + uint32 _NeedsSaving(uint32 flags) const; + + private: + struct AppInfo { + BString signature; + uint32 flags; + version_info versionInfo; + + BMessage supportedTypes; + + bool iconChanged; + bool typeIconsChanged; + }; + enum { + CHECK_SIGNATUR = 1 << 0, + CHECK_FLAGS = 1 << 1, + CHECK_VERSION = 1 << 2, + CHECK_ICON = 1 << 3, + + CHECK_TYPES = 1 << 4, + CHECK_TYPE_ICONS = 1 << 5, + + CHECK_ALL = 0xffffffff + }; + private: BEntry fEntry; + AppInfo fOriginalInfo; BTextControl* fSignatureControl; IconView* fIconView; @@ -65,6 +97,9 @@ class ApplicationTypeWindow : public BWindow { BTextControl* fInternalVersionControl; BTextControl* fShortDescriptionControl; BTextView* fLongDescriptionView; + + BMenuItem* fSaveMenuItem; + uint32 fChangedProperties; }; #endif // APPLICATION_TYPE_WINDOW_H