diff --git a/src/preferences/filetypes/ApplicationTypesWindow.cpp b/src/preferences/filetypes/ApplicationTypesWindow.cpp new file mode 100644 index 0000000000..5dfdd9ea22 --- /dev/null +++ b/src/preferences/filetypes/ApplicationTypesWindow.cpp @@ -0,0 +1,329 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +// TODO: think about adopting Tracker's info window style here (pressable path) + +#include "ApplicationTypesWindow.h" +#include "FileTypes.h" +#include "FileTypesWindow.h" +#include "MimeTypeListView.h" +#include "StringView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +const uint32 kMsgTypeSelected = 'typs'; +const uint32 kMsgRemoveUninstalled = 'runs'; + + +ApplicationTypesWindow::ApplicationTypesWindow(BRect frame) + : BWindow(frame, "Application Types", B_TITLED_WINDOW, + B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) +{ + // Application list + + BRect rect = Bounds(); + BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); + topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + AddChild(topView); + + BButton* button = new BButton(rect, "remove", "Remove Uninstalled", + new BMessage(kMsgRemoveUninstalled), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); + button->ResizeToPreferred(); + button->MoveTo(8.0f, rect.bottom - 8.0f - button->Bounds().Height()); + topView->AddChild(button); + + rect.bottom = button->Frame().top - 10.0f; + rect.top = 10.0f; + rect.left = 10.0f; + rect.right = 170; + + fTypeListView = new MimeTypeListView(rect, "listview", "application", true, true, + B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM); + fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); + + BScrollView* scrollView = new BScrollView("scrollview", fTypeListView, + B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_FRAME_EVENTS | B_WILL_DRAW, false, true); + topView->AddChild(scrollView); + + // "Description" group + + BFont font(be_bold_font); + float labelWidth = font.StringWidth("Icon"); + font_height fontHeight; + font.GetHeight(&fontHeight); + + rect.left = rect.right + 12.0f + B_V_SCROLL_BAR_WIDTH; + rect.top -= 2.0f; + rect.right = topView->Bounds().Width() - 8.0f; + rect.bottom = rect.top + ceilf(fontHeight.ascent) + 24.0f; + BBox* box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); + box->SetLabel("Description"); + topView->AddChild(box); + + BRect innerRect = box->Bounds().InsetByCopy(8.0f, 6.0f); + labelWidth = topView->StringWidth("Signature:") + 4.0f; + + innerRect.right = box->Bounds().Width() - 8.0f; + innerRect.top += ceilf(fontHeight.ascent); + fNameView = new StringView(innerRect, "name", "Name:", NULL, B_FOLLOW_LEFT_RIGHT); + fNameView->SetDivider(labelWidth); + float width, height; + fNameView->GetPreferredSize(&width, &height); + fNameView->ResizeTo(innerRect.Width(), height); + box->ResizeBy(0, fNameView->Bounds().Height() * 3.0f); + box->AddChild(fNameView); + + innerRect.OffsetBy(0, fNameView->Bounds().Height() + 5.0f); + fSignatureView = new StringView(innerRect, "signature", "Signature:", NULL, + B_FOLLOW_LEFT_RIGHT); + fSignatureView->SetDivider(labelWidth); + box->AddChild(fSignatureView); + + innerRect.OffsetBy(0, fNameView->Bounds().Height() + 5.0f); + fPathView = new StringView(innerRect, "path", "Path:", NULL, + B_FOLLOW_LEFT_RIGHT); + fPathView->SetDivider(labelWidth); + box->AddChild(fPathView); + + // Launch and Tracker buttons + + rect = box->Frame(); + rect.top = rect.bottom + 8.0f; + rect.bottom = rect.top + 20.0f; + fTrackerButton = new BButton(rect, "tracker", "Open In Tracker" B_UTF8_ELLIPSIS, NULL, + B_FOLLOW_RIGHT); + fTrackerButton->ResizeToPreferred(); + fTrackerButton->MoveTo(rect.right - fTrackerButton->Bounds().Width(), rect.top); + topView->AddChild(fTrackerButton); + + fLaunchButton = new BButton(rect, "launch", "Launch", NULL, + B_FOLLOW_RIGHT); + fLaunchButton->ResizeToPreferred(); + fLaunchButton->MoveTo(fTrackerButton->Frame().left - 6.0f + - fLaunchButton->Bounds().Width(), rect.top); + topView->AddChild(fLaunchButton); + + SetSizeLimits(scrollView->Frame().right + 22.0f + fTrackerButton->Frame().Width() + + fLaunchButton->Frame().Width(), 32767.0f, + fTrackerButton->Frame().bottom + 8.0f, 32767.0f); + + BMimeType::StartWatching(this); + _SetType(NULL); +} + + +ApplicationTypesWindow::~ApplicationTypesWindow() +{ + BMimeType::StopWatching(this); +} + + +void +ApplicationTypesWindow::_RemoveUninstalled() +{ + // Note: this runs in the looper's thread, which isn't that nice + + int32 removed = 0; + + for (int32 i = fTypeListView->FullListCountItems(); i-- > 0;) { + MimeTypeItem* item = dynamic_cast(fTypeListView->FullListItemAt(i)); + if (item == NULL) + continue; + + // search for application on all volumes + + bool found = false; + + BVolumeRoster volumeRoster; + BVolume volume; + while (volumeRoster.GetNextVolume(&volume) == B_OK) { + if (!volume.KnowsQuery()) + continue; + + BQuery query; + query.PushAttr("BEOS:APP_SIG"); + query.PushString(item->Type()); + query.PushOp(B_EQ); + + query.SetVolume(&volume); + query.Fetch(); + + entry_ref ref; + if (query.GetNextRef(&ref) == B_OK) { + found = true; + break; + } + } + + if (!found) { + BMimeType mimeType(item->Type()); + mimeType.Delete(); + + removed++; + + // We're blocking the message loop that received the MIME changes, + // so we dequeue all waiting messages from time to time + if (removed % 10 == 0) + UpdateIfNeeded(); + } + } + + char message[512]; + snprintf(message, sizeof(message), "%ld Application Type%s could be removed.", + removed, removed == 1 ? "" : "s"); + error_alert(message, B_OK, B_INFO_ALERT); +} + + +void +ApplicationTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) +{ + bool enabled = type != NULL; + bool appFound = true; + + // update controls + + if (type != NULL) { + if (fCurrentType == *type) { + if (!forceUpdate) + return; + } else + forceUpdate = B_EVERYTHING_CHANGED; + + if (&fCurrentType != type) + fCurrentType.SetTo(type->Type()); + + fSignatureView->SetText(type->Type()); + + char description[B_MIME_TYPE_LENGTH]; + + if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) { + if (type->GetShortDescription(description) != B_OK) + description[0] = '\0'; + fNameView->SetText(description); + } + + entry_ref ref; + // TODO: disabled because of somewhat broken BRoster::FindApp() behaviour + if (/*(forceUpdate & B_APP_HINT_CHANGED) != 0*/ + forceUpdate == B_EVERYTHING_CHANGED + && be_roster->FindApp(fCurrentType.Type(), &ref) == B_OK) { + // Set launch message + BMessenger tracker("application/x-vnd.Be-TRAK"); + BMessage* message = new BMessage(B_REFS_RECEIVED); + message->AddRef("refs", &ref); + + fLaunchButton->SetMessage(message); + fLaunchButton->SetTarget(tracker); + + // Set path + BPath path(&ref); + path.GetParent(&path); + fPathView->SetText(path.Path()); + + // Set "Open In Tracker" message + BEntry entry(path.Path()); + if (entry.GetRef(&ref) == B_OK) { + BMessenger tracker("application/x-vnd.Be-TRAK"); + message = new BMessage(B_REFS_RECEIVED); + message->AddRef("refs", &ref); + + fTrackerButton->SetMessage(message); + fTrackerButton->SetTarget(tracker); + } else { + fTrackerButton->SetMessage(NULL); + appFound = false; + } + } + } else { + fNameView->SetText(NULL); + fNameView->SetText(NULL); + fPathView->SetText(NULL); + } + + fNameView->SetEnabled(enabled); + fSignatureView->SetEnabled(enabled); + fPathView->SetEnabled(enabled); + + fTrackerButton->SetEnabled(enabled && appFound); + fLaunchButton->SetEnabled(enabled && appFound); +} + + +void +ApplicationTypesWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgTypeSelected: + { + int32 index; + if (message->FindInt32("index", &index) == B_OK) { + MimeTypeItem* item = (MimeTypeItem*)fTypeListView->ItemAt(index); + if (item != NULL) { + BMimeType type(item->Type()); + _SetType(&type); + } else + _SetType(NULL); + } + break; + } + + case kMsgRemoveUninstalled: + _RemoveUninstalled(); + break; + + case B_META_MIME_CHANGED: + { + const char* type; + int32 which; + if (message->FindString("be:type", &type) != B_OK + || message->FindInt32("be:which", &which) != B_OK) + break; + + if (fCurrentType.Type() == NULL) + break; + + if (!strcasecmp(fCurrentType.Type(), type)) { + if (which != B_MIME_TYPE_DELETED) + _SetType(&fCurrentType, which); + else + _SetType(NULL); + } + break; + } + + default: + BWindow::MessageReceived(message); + } +} + + +bool +ApplicationTypesWindow::QuitRequested() +{ + be_app->PostMessage(kMsgApplicationTypesWindowClosed); + return true; +} + + diff --git a/src/preferences/filetypes/ApplicationTypesWindow.h b/src/preferences/filetypes/ApplicationTypesWindow.h new file mode 100644 index 0000000000..280aa3f845 --- /dev/null +++ b/src/preferences/filetypes/ApplicationTypesWindow.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef APPLICATION_TYPES_WINDOW_H +#define APPLICATION_TYPES_WINDOW_H + + +#include +#include +#include + +class BButton; +class BListView; +class BMenuField; +class BMimeType; +class BOutlineListView; +class BStringView; + +class MimeTypeListView; +class StringView; + + +class ApplicationTypesWindow : public BWindow { + public: + ApplicationTypesWindow(BRect frame); + virtual ~ApplicationTypesWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + private: + void _SetType(BMimeType* type, int32 forceUpdate = 0); + void _UpdateCounter(); + void _RemoveUninstalled(); + + private: + BMimeType fCurrentType; + + MimeTypeListView* fTypeListView; + BButton* fRemoveTypeButton; + + StringView* fNameView; + StringView* fSignatureView; + StringView* fPathView; + + BButton* fTrackerButton; + BButton* fLaunchButton; +}; + +#endif // APPLICATION_TYPES_WINDOW_H diff --git a/src/preferences/filetypes/ExtensionWindow.cpp b/src/preferences/filetypes/ExtensionWindow.cpp new file mode 100644 index 0000000000..39010900b5 --- /dev/null +++ b/src/preferences/filetypes/ExtensionWindow.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "ExtensionWindow.h" +#include "FileTypesWindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + + +const uint32 kMsgExtensionUpdated = 'exup'; +const uint32 kMsgAccept = 'acpt'; + + +static int +compare_extensions(const void* _a, const void* _b) +{ + const char* a = *(const char **)_a; + const char* b = *(const char **)_b; + + int compare = strcasecmp(a, b); + if (compare != 0) + return compare; + + // sort lower case characters first + return -strcmp(a, b); +} + + +// #pragma mark - + + +ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type, + const char* extension) + : BWindow(BRect(100, 100, 350, 200), "Extension", B_MODAL_WINDOW_LOOK, + B_MODAL_SUBSET_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE + | B_ASYNCHRONOUS_CONTROLS), + fTarget(target), + fMimeType(type.Type()), + fExtension(extension) +{ + BRect rect = Bounds(); + BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); + topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + AddChild(topView); + + rect.InsetBy(8.0f, 8.0f); + fExtensionControl = new BTextControl(rect, "extension", "Extension:", extension, + NULL, B_FOLLOW_LEFT_RIGHT); + + float labelWidth = fExtensionControl->StringWidth(fExtensionControl->Label()) + 2.0f; + fExtensionControl->SetModificationMessage(new BMessage(kMsgExtensionUpdated)); + fExtensionControl->SetDivider(labelWidth); + fExtensionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); + + // filter out invalid characters that can't be part of an extension + BTextView* textView = fExtensionControl->TextView(); + const char* disallowedCharacters = "/:"; + for (int32 i = 0; disallowedCharacters[i]; i++) { + textView->DisallowChar(disallowedCharacters[i]); + } + + float width, height; + fExtensionControl->GetPreferredSize(&width, &height); + fExtensionControl->ResizeTo(rect.Width(), height); + topView->AddChild(fExtensionControl); + + fAcceptButton = new BButton(rect, "add", extension ? "Done" : "Add", + new BMessage(kMsgAccept), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); + fAcceptButton->ResizeToPreferred(); + fAcceptButton->MoveTo(Bounds().Width() - 8.0f - fAcceptButton->Bounds().Width(), + Bounds().Height() - 8.0f - fAcceptButton->Bounds().Height()); + fAcceptButton->SetEnabled(false); + topView->AddChild(fAcceptButton); + + BButton* button = new BButton(rect, "cancel", "Cancel", + new BMessage(B_QUIT_REQUESTED), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); + button->ResizeToPreferred(); + button->MoveTo(fAcceptButton->Frame().left - 10.0f - button->Bounds().Width(), + fAcceptButton->Frame().top); + topView->AddChild(button); + + ResizeTo(labelWidth * 4.0f + 24.0f, fExtensionControl->Bounds().Height() * 2.0f + 30.0f); + SetSizeLimits(button->Bounds().Width() + fAcceptButton->Bounds().Width() + 26.0f, + 32767.0f, Frame().Height(), Frame().Height()); + + // omit the leading dot + if (fExtension.ByteAt(0) == '.') + fExtension.Remove(0, 1); + + fAcceptButton->MakeDefault(true); + fExtensionControl->MakeFocus(true); + + target->PlaceSubWindow(this); + AddToSubset(target); +} + + +ExtensionWindow::~ExtensionWindow() +{ +} + + +void +ExtensionWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgExtensionUpdated: + { + bool enabled = fExtensionControl->Text() != NULL + && fExtensionControl->Text()[0] != '\0'; + if (enabled) { + // There is some text, but we only accept it, if it + // changed the previous extension + enabled = strcmp(fExtensionControl->Text(), fExtension.String()); + } + + if (fAcceptButton->IsEnabled() != enabled) + fAcceptButton->SetEnabled(enabled); + break; + } + + case kMsgAccept: + { + const char* newExtension = fExtensionControl->Text(); + // omit the leading dot + if (newExtension[0] == '.') + newExtension++; + + BMessage extensions; + status_t status = fMimeType.GetFileExtensions(&extensions); + if (status == B_OK) { + // replace the entry, and remove any equivalent entries + BList list; + list.AddItem((void *)newExtension); + + const char* extension; + for (int32 i = 0; extensions.FindString("extensions", i, + &extension) == B_OK; i++) { + if (!strcmp(fExtension.String(), extension) + || !strcmp(newExtension, extension)) { + // remove this item + continue; + } + + list.AddItem((void *)extension); + } + + list.SortItems(compare_extensions); + + // Copy them to a new message (their memory is still part of the + // original BMessage) + BMessage newExtensions; + for (int32 i = 0; i < list.CountItems(); i++) { + newExtensions.AddString("extensions", (const char*)list.ItemAt(i)); + } + + status = fMimeType.SetFileExtensions(&newExtensions); + } + + if (status != B_OK) + error_alert("Could not change file extensions", status); + + PostMessage(B_QUIT_REQUESTED); + break; + } + } +} + + +bool +ExtensionWindow::QuitRequested() +{ + fTarget.SendMessage(kMsgNewTypeWindowClosed); + return true; +} diff --git a/src/preferences/filetypes/ExtensionWindow.h b/src/preferences/filetypes/ExtensionWindow.h new file mode 100644 index 0000000000..0f03c0b121 --- /dev/null +++ b/src/preferences/filetypes/ExtensionWindow.h @@ -0,0 +1,36 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef EXTENSION_WINDOW_H +#define EXTENSION_WINDOW_H + + +#include +#include +#include +#include + +class BButton; +class BTextControl; + +class FileTypesWindow; + + +class ExtensionWindow : public BWindow { + public: + ExtensionWindow(FileTypesWindow* target, BMimeType& type, const char* extension); + virtual ~ExtensionWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + private: + BMessenger fTarget; + BMimeType fMimeType; + BString fExtension; + BTextControl* fExtensionControl; + BButton* fAcceptButton; +}; + +#endif // EXTENSION_WINDOW_H diff --git a/src/preferences/filetypes/FileTypes.cpp b/src/preferences/filetypes/FileTypes.cpp index 162869e58b..358b3f186c 100644 --- a/src/preferences/filetypes/FileTypes.cpp +++ b/src/preferences/filetypes/FileTypes.cpp @@ -4,12 +4,11 @@ */ +#include "ApplicationTypesWindow.h" #include "FileTypes.h" #include "FileTypesWindow.h" #include -//#include -//#include #include #include #include @@ -39,21 +38,28 @@ class FileTypes : public BApplication { virtual bool QuitRequested(); private: + void _WindowClosed(); + BFilePanel *fFilePanel; BWindow *fTypesWindow; + BWindow *fApplicationTypesWindow; uint32 fWindowCount; BRect fTypesWindowFrame; + BRect fApplicationTypesWindowFrame; }; FileTypes::FileTypes() : BApplication(kSignature), fTypesWindow(NULL), + fApplicationTypesWindow(NULL), fWindowCount(0) { fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE | B_DIRECTORY_NODE, false); + fTypesWindowFrame = BRect(80.0f, 80.0f, 600.0f, 480.0f); + fApplicationTypesWindowFrame = BRect(100.0f, 100.0f, 540.0f, 480.0f); // TODO: read from settings } @@ -141,6 +147,14 @@ FileTypes::ArgvReceived(int32 argc, char **argv) } +void +FileTypes::_WindowClosed() +{ + if (--fWindowCount == 0 && !fFilePanel->IsShowing()) + PostMessage(B_QUIT_REQUESTED); +} + + void FileTypes::MessageReceived(BMessage *message) { @@ -153,15 +167,30 @@ FileTypes::MessageReceived(BMessage *message) } else fTypesWindow->Activate(true); break; - case kMsgTypesWindowClosed: fTypesWindow = NULL; - // supposed to fall through - case kMsgWindowClosed: - if (--fWindowCount == 0 && !fFilePanel->IsShowing()) - PostMessage(B_QUIT_REQUESTED); + _WindowClosed(); break; + case kMsgOpenApplicationTypesWindow: + if (fApplicationTypesWindow == NULL) { + fApplicationTypesWindow = new ApplicationTypesWindow( + fApplicationTypesWindowFrame); + fApplicationTypesWindow->Show(); + fWindowCount++; + } else + fApplicationTypesWindow->Activate(true); + break; + case kMsgApplicationTypesWindowClosed: + fApplicationTypesWindow = NULL; + _WindowClosed(); + break; + + case kMsgWindowClosed: + _WindowClosed(); + break; + + case kMsgOpenFilePanel: // the open file panel sends us a message when it's done fFilePanel->Window()->SetTitle("FileTypes: Open File"); diff --git a/src/preferences/filetypes/FileTypes.h b/src/preferences/filetypes/FileTypes.h index 263654131a..e944111ccd 100644 --- a/src/preferences/filetypes/FileTypes.h +++ b/src/preferences/filetypes/FileTypes.h @@ -17,6 +17,10 @@ static const uint32 kMsgOpenSameAsPanel = 'opAp'; static const uint32 kMsgOpenTypesWindow = 'opTw'; static const uint32 kMsgTypesWindowClosed = 'clTw'; + +static const uint32 kMsgOpenApplicationTypesWindow = 'opAw'; +static const uint32 kMsgApplicationTypesWindowClosed = 'clAw'; + static const uint32 kMsgWindowClosed = 'WiCl'; #endif // FILE_TYPES_H diff --git a/src/preferences/filetypes/FileTypesWindow.cpp b/src/preferences/filetypes/FileTypesWindow.cpp index bf9895f235..eb81bce8b9 100644 --- a/src/preferences/filetypes/FileTypesWindow.cpp +++ b/src/preferences/filetypes/FileTypesWindow.cpp @@ -4,10 +4,12 @@ */ +#include "ExtensionWindow.h" #include "FileTypes.h" #include "FileTypesWindow.h" #include "MimeTypeListView.h" #include "NewFileTypeWindow.h" +#include "StringView.h" #include #include @@ -498,9 +500,7 @@ AttributeListView::Draw(BRect updateRect) FileTypesWindow::FileTypesWindow(BRect frame) : BWindow(frame, "FileTypes", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS), - fNewTypeWindow(NULL), - fExtensionWindow(NULL), - fAttributeWindow(NULL) + fNewTypeWindow(NULL) { // add the menu @@ -517,7 +517,7 @@ FileTypesWindow::FileTypesWindow(BRect frame) item->SetShortcut('O', B_COMMAND_KEY); menu->AddItem(item); menu->AddItem(new BMenuItem("Application Types" B_UTF8_ELLIPSIS, - new BMessage)); + new BMessage(kMsgOpenApplicationTypesWindow))); menu->AddSeparatorItem(); menu->AddItem(new BMenuItem("About FileTypes" B_UTF8_ELLIPSIS, @@ -558,7 +558,7 @@ FileTypesWindow::FileTypesWindow(BRect frame) if (rect.right < 180) rect.right = 180; - fTypeListView = new MimeTypeListView(rect, "listview", + fTypeListView = new MimeTypeListView(rect, "listview", NULL, false, false, B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM); fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); @@ -644,23 +644,24 @@ FileTypesWindow::FileTypesWindow(BRect frame) innerRect = box->Bounds().InsetByCopy(8.0f, 6.0f); innerRect.top += ceilf(fontHeight.ascent); innerRect.bottom = innerRect.top + button->Bounds().Height(); - fInternalNameControl = new BTextControl(innerRect, "internal", "Internal Name:", "", - NULL, B_FOLLOW_LEFT_RIGHT); - labelWidth = fInternalNameControl->StringWidth(fInternalNameControl->Label()) + 2.0f; - fInternalNameControl->SetDivider(labelWidth); - fInternalNameControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); - fInternalNameControl->SetEnabled(false); - box->ResizeBy(0, fInternalNameControl->Bounds().Height() * 3.0f); - box->AddChild(fInternalNameControl); + fInternalNameView = new StringView(innerRect, "internal", "Internal Name:", "", + B_FOLLOW_LEFT_RIGHT); + labelWidth = fInternalNameView->StringWidth(fInternalNameView->Label()) + 2.0f; + fInternalNameView->SetDivider(labelWidth); + fInternalNameView->SetEnabled(false); + fInternalNameView->ResizeToPreferred(); + box->AddChild(fInternalNameView); - innerRect.OffsetBy(0, fInternalNameControl->Bounds().Height() + 5.0f); + innerRect.OffsetBy(0, fInternalNameView->Bounds().Height() + 5.0f); fTypeNameControl = new BTextControl(innerRect, "type", "Type Name:", "", new BMessage(kMsgTypeEntered), B_FOLLOW_LEFT_RIGHT); fTypeNameControl->SetDivider(labelWidth); fTypeNameControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); + box->ResizeBy(0, fInternalNameView->Bounds().Height() + + fTypeNameControl->Bounds().Height() * 2.0f); box->AddChild(fTypeNameControl); - innerRect.OffsetBy(0, fInternalNameControl->Bounds().Height() + 5.0f); + innerRect.OffsetBy(0, fTypeNameControl->Bounds().Height() + 5.0f); fDescriptionControl = new BTextControl(innerRect, "description", "Description:", "", new BMessage(kMsgDescriptionEntered), B_FOLLOW_LEFT_RIGHT); fDescriptionControl->SetDivider(labelWidth); @@ -1060,7 +1061,7 @@ FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) if (&fCurrentType != type) fCurrentType.SetTo(type->Type()); - fInternalNameControl->SetText(type->Type()); + fInternalNameView->SetText(type->Type()); char description[B_MIME_TYPE_LENGTH]; @@ -1077,7 +1078,7 @@ FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) } } else { fCurrentType.Unset(); - fInternalNameControl->SetText(NULL); + fInternalNameView->SetText(NULL); fTypeNameControl->SetText(NULL); fDescriptionControl->SetText(NULL); } @@ -1098,6 +1099,7 @@ FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) fIconView->SetEnabled(enabled); + fInternalNameView->SetEnabled(enabled); fTypeNameControl->SetEnabled(enabled); fDescriptionControl->SetEnabled(enabled); fPreferredField->SetEnabled(enabled); @@ -1205,12 +1207,31 @@ FileTypesWindow::MessageReceived(BMessage* message) } case kMsgExtensionInvoked: - puts("ext"); + { + if (fCurrentType.Type() == NULL) + break; + + int32 index; + if (message->FindInt32("index", &index) == B_OK) { + BStringItem* item = (BStringItem*)fExtensionListView->ItemAt(index); + if (item == NULL) + break; + + BWindow* window = new ExtensionWindow(this, fCurrentType, item->Text()); + window->Show(); + } break; + } case kMsgAddExtension: - puts("add ext"); + { + if (fCurrentType.Type() == NULL) + break; + + BWindow* window = new ExtensionWindow(this, fCurrentType, NULL); + window->Show(); break; + } case kMsgRemoveExtension: { diff --git a/src/preferences/filetypes/FileTypesWindow.h b/src/preferences/filetypes/FileTypesWindow.h index 2748e19ae6..d339cdc3da 100644 --- a/src/preferences/filetypes/FileTypesWindow.h +++ b/src/preferences/filetypes/FileTypesWindow.h @@ -20,6 +20,7 @@ class BTextControl; class AttributeListView; class IconView; class MimeTypeListView; +class StringView; class FileTypesWindow : public BWindow { @@ -53,7 +54,7 @@ class FileTypesWindow : public BWindow { BButton* fAddExtensionButton; BButton* fRemoveExtensionButton; - BTextControl* fInternalNameControl; + StringView* fInternalNameView; BTextControl* fTypeNameControl; BTextControl* fDescriptionControl; @@ -66,8 +67,6 @@ class FileTypesWindow : public BWindow { BButton* fRemoveAttributeButton; BWindow* fNewTypeWindow; - BWindow* fExtensionWindow; - BWindow* fAttributeWindow; }; static const uint32 kMsgPreferredAppOpened = 'paOp'; diff --git a/src/preferences/filetypes/Jamfile b/src/preferences/filetypes/Jamfile index 2a167588da..bbd62dedc4 100644 --- a/src/preferences/filetypes/Jamfile +++ b/src/preferences/filetypes/Jamfile @@ -8,8 +8,11 @@ SubDirSysHdrs $(HAIKU_TOP) src kits tracker ; Preference FileTypes : FileTypes.cpp FileTypesWindow.cpp + ApplicationTypesWindow.cpp MimeTypeListView.cpp NewFileTypeWindow.cpp + ExtensionWindow.cpp + StringView.cpp : be tracker : FileTypes.rdef FileTypes.icons.rdef ; diff --git a/src/preferences/filetypes/MimeTypeListView.cpp b/src/preferences/filetypes/MimeTypeListView.cpp index ac8b38b5e4..69b233b7be 100644 --- a/src/preferences/filetypes/MimeTypeListView.cpp +++ b/src/preferences/filetypes/MimeTypeListView.cpp @@ -6,7 +6,7 @@ #include "MimeTypeListView.h" -#include +#include // TODO: lazy type collecting (super types only at startup) @@ -28,17 +28,21 @@ mimetype_is_application_signature(BMimeType& type) // #pragma mark - -MimeTypeItem::MimeTypeItem(BMimeType& type, bool flat) +MimeTypeItem::MimeTypeItem(BMimeType& type, bool showIcon, bool flat) : BStringItem(type.Type(), !flat && !type.IsSupertypeOnly() ? 1 : 0, false), - fType(type.Type()) + fType(type.Type()), + fFlat(flat), + fShowIcon(showIcon) { _SetTo(type); } -MimeTypeItem::MimeTypeItem(const char* type, bool flat) +MimeTypeItem::MimeTypeItem(const char* type, bool showIcon, bool flat) : BStringItem(type, !flat && strchr(type, '/') != NULL ? 1 : 0, false), - fType(type) + fType(type), + fFlat(flat), + fShowIcon(showIcon) { BMimeType mimeType(type); _SetTo(mimeType); @@ -51,7 +55,7 @@ MimeTypeItem::~MimeTypeItem() void -MimeTypeItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything) +MimeTypeItem::DrawItem(BView* owner, BRect frame, bool complete) { BFont font; @@ -62,13 +66,70 @@ MimeTypeItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything) owner->SetFont(&boldFont); } - BStringItem::DrawItem(owner, itemRect, drawEverything); + BRect rect = frame; + if (fFlat) { + // This is where the latch would be - yet can freely consider this + // as an ugly hack + rect.left -= 11.0f; + } + + if (fShowIcon) { + rgb_color highColor = owner->HighColor(); + rgb_color lowColor = owner->LowColor(); + + if (IsSelected() || complete) { + if (IsSelected()) + owner->SetLowColor(tint_color(lowColor, B_DARKEN_2_TINT)); + + owner->FillRect(rect, B_SOLID_LOW); + } + + BBitmap bitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_CMAP8); + BMimeType mimeType(fType.String()); + if (mimeType.GetIcon(&bitmap, B_MINI_ICON) == B_OK) { + BPoint point(rect.left + 2.0f, + rect.top + (rect.Height() - B_MINI_ICON) / 2.0f); + + owner->SetDrawingMode(B_OP_ALPHA); + owner->DrawBitmap(&bitmap, point); + } + + owner->SetDrawingMode(B_OP_COPY); + + owner->MovePenTo(rect.left + B_MINI_ICON + 8.0f, frame.top + fBaselineOffset); + owner->SetHighColor(0, 0, 0); + owner->DrawString(Text()); + + owner->SetHighColor(highColor); + owner->SetLowColor(lowColor); + } else + BStringItem::DrawItem(owner, rect, complete); if (IsSupertypeOnly()) owner->SetFont(&font); } +void +MimeTypeItem::Update(BView* owner, const BFont* font) +{ + BStringItem::Update(owner, font); + + if (fShowIcon) { + SetWidth(Width() + B_MINI_ICON + 2.0f); + + if (Height() < B_MINI_ICON + 4.0f) + SetHeight(B_MINI_ICON + 4.0f); + + font_height fontHeight; + font->GetHeight(&fontHeight); + + fBaselineOffset = fontHeight.ascent + + (Height() - ceilf(fontHeight.ascent + fontHeight.descent)) / 2.0f; + } +} + + void MimeTypeItem::_SetTo(BMimeType& type) { @@ -86,12 +147,12 @@ MimeTypeItem::_SetTo(BMimeType& type) fSubtype.SetTo(subType + 1); // omit the slash - Update(); + UpdateText(); } void -MimeTypeItem::Update() +MimeTypeItem::UpdateText() { if (IsSupertypeOnly()) return; @@ -171,19 +232,43 @@ MimeTypeItem::CompareLabels(const BListItem* a, const BListItem* b) MimeTypeListView::MimeTypeListView(BRect rect, const char* name, + const char* supertype, bool showIcons, bool applicationMode, uint32 resizingMode) - : BOutlineListView(rect, name, B_SINGLE_SELECTION_LIST, resizingMode) + : BOutlineListView(rect, name, B_SINGLE_SELECTION_LIST, resizingMode), + fSupertype(supertype), + fShowIcons(showIcons), + fApplicationMode(applicationMode) { - _CollectTypes(); } MimeTypeListView::~MimeTypeListView() { - // free items, stupid BOutlineListView doesn't do this itself +} - for (int32 i = FullListCountItems(); i-- > 0;) { - delete FullListItemAt(i); + +void +MimeTypeListView::_CollectSubtypes(const char* supertype, MimeTypeItem* supertypeItem) +{ + BMessage types; + if (BMimeType::GetInstalledTypes(supertype, &types) != B_OK) + return; + + const char* type; + int32 index = 0; + while (types.FindString("types", index++, &type) == B_OK) { + BMimeType mimeType(type); + + bool isApp = mimetype_is_application_signature(mimeType); + if (fApplicationMode ^ isApp) + continue; + + BStringItem* typeItem = new MimeTypeItem(mimeType, fShowIcons, + supertypeItem == NULL); + if (supertypeItem != NULL) + AddUnder(typeItem, supertypeItem); + else + AddItem(typeItem); } } @@ -191,30 +276,21 @@ MimeTypeListView::~MimeTypeListView() void MimeTypeListView::_CollectTypes() { - BMessage superTypes; - if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK) - return; + if (fSupertype.Type() != NULL) { + // only show MIME types that belong to this supertype + _CollectSubtypes(fSupertype.Type(), NULL); + } else { + BMessage superTypes; + if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK) + return; - const char* superType; - int32 index = 0; - while (superTypes.FindString("super_types", index++, &superType) == B_OK) { - BStringItem* superTypeItem = new MimeTypeItem(superType); - AddItem(superTypeItem); + const char* supertype; + int32 index = 0; + while (superTypes.FindString("super_types", index++, &supertype) == B_OK) { + MimeTypeItem* supertypeItem = new MimeTypeItem(supertype); + AddItem(supertypeItem); - BMessage types; - if (BMimeType::GetInstalledTypes(superType, &types) != B_OK) - continue; - - const char* type; - int32 typeIndex = 0; - while (types.FindString("types", typeIndex++, &type) == B_OK) { - BMimeType mimeType(type); - - if (mimetype_is_application_signature(mimeType)) - continue; - - BStringItem* typeItem = new MimeTypeItem(mimeType); - AddUnder(typeItem, superTypeItem); + _CollectSubtypes(supertype, supertypeItem); } } @@ -225,7 +301,10 @@ MimeTypeListView::_CollectTypes() void MimeTypeListView::_MakeTypesUnique(MimeTypeItem* underItem) { - SortItemsUnder(underItem, underItem != NULL, &MimeTypeItem::Compare); +#ifndef __HAIKU__ + if (fSupertype.Type() == NULL) +#endif + SortItemsUnder(underItem, underItem != NULL, &MimeTypeItem::Compare); bool lastItemSame = false; MimeTypeItem* last = NULL; @@ -282,6 +361,7 @@ MimeTypeListView::AttachedToWindow() BOutlineListView::AttachedToWindow(); BMimeType::StartWatching(this); + _CollectTypes(); } @@ -289,8 +369,13 @@ void MimeTypeListView::DetachedFromWindow() { BOutlineListView::DetachedFromWindow(); - BMimeType::StopWatching(this); + + // free all items, they will be retrieved again in AttachedToWindow() + + for (int32 i = FullListCountItems(); i-- > 0;) { + delete FullListItemAt(i); + } } @@ -442,7 +527,7 @@ MimeTypeListView::UpdateItem(MimeTypeItem* item) if (IndexOf(item) == CurrentSelection()) selected = CurrentSelection(); - item->Update(); + item->UpdateText(); _MakeTypesUnique(dynamic_cast(Superitem(item))); if (selected != -1) { diff --git a/src/preferences/filetypes/MimeTypeListView.h b/src/preferences/filetypes/MimeTypeListView.h index fdb0cc4812..e2a458a087 100644 --- a/src/preferences/filetypes/MimeTypeListView.h +++ b/src/preferences/filetypes/MimeTypeListView.h @@ -6,20 +6,20 @@ #define MIME_TYPE_LIST_VIEW_H +#include #include #include -class BMimeType; - class MimeTypeItem : public BStringItem { public: - MimeTypeItem(BMimeType& type, bool flat = false); - MimeTypeItem(const char* type, bool flat = false); + MimeTypeItem(BMimeType& type, bool showIcon = false, bool flat = false); + MimeTypeItem(const char* type, bool showIcon = false, bool flat = false); virtual ~MimeTypeItem(); virtual void DrawItem(BView* owner, BRect itemRect, bool drawEverything = false); + virtual void Update(BView* owner, const BFont* font); const char* Type() const { return fType.String(); } const char* Subtype() const { return fSubtype.String(); } @@ -27,7 +27,7 @@ class MimeTypeItem : public BStringItem { const char* Description() const { return fDescription.String(); } bool IsSupertypeOnly() const { return fIsSupertype; } - void Update(); + void UpdateText(); void AddSubtype(); static int Compare(const BListItem* a, const BListItem* b); @@ -40,12 +40,17 @@ class MimeTypeItem : public BStringItem { BString fSubtype; BString fType; BString fDescription; + float fBaselineOffset; bool fIsSupertype; + bool fFlat; + bool fShowIcon; }; class MimeTypeListView : public BOutlineListView { public: MimeTypeListView(BRect rect, const char* name, + const char* supertype = NULL, bool showIcons = false, + bool applicationMode = false, uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP); virtual ~MimeTypeListView(); @@ -64,10 +69,14 @@ class MimeTypeListView : public BOutlineListView { virtual void MessageReceived(BMessage* message); private: + void _CollectSubtypes(const char* supertype, MimeTypeItem* supertypeItem); void _CollectTypes(); void _MakeTypesUnique(MimeTypeItem* underItem = NULL); - BString fSelectNewType; + BMimeType fSupertype; + BString fSelectNewType; + bool fShowIcons; + bool fApplicationMode; }; bool mimetype_is_application_signature(BMimeType& type); diff --git a/src/preferences/filetypes/NewFileTypeWindow.cpp b/src/preferences/filetypes/NewFileTypeWindow.cpp index 8b6c1c97c6..e084cfbc79 100644 --- a/src/preferences/filetypes/NewFileTypeWindow.cpp +++ b/src/preferences/filetypes/NewFileTypeWindow.cpp @@ -121,7 +121,7 @@ NewFileTypeWindow::~NewFileTypeWindow() { } -#include + void NewFileTypeWindow::MessageReceived(BMessage* message) { diff --git a/src/preferences/filetypes/NewFileTypeWindow.h b/src/preferences/filetypes/NewFileTypeWindow.h index c2384642ef..37d52c77f3 100644 --- a/src/preferences/filetypes/NewFileTypeWindow.h +++ b/src/preferences/filetypes/NewFileTypeWindow.h @@ -13,6 +13,8 @@ class BButton; class BMenu; class BTextControl; +class FileTypesWindow; + class NewFileTypeWindow : public BWindow { public: diff --git a/src/preferences/filetypes/StringView.cpp b/src/preferences/filetypes/StringView.cpp new file mode 100644 index 0000000000..0afc75fbc7 --- /dev/null +++ b/src/preferences/filetypes/StringView.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "StringView.h" + +//#include + + +StringView::StringView(BRect frame, const char* name, const char* label, + const char* text, uint32 resizeMask, uint32 flags) + : BView(frame, name, resizeMask, flags), + fLabel(label), + fText(text), + fLabelAlignment(B_ALIGN_RIGHT), + fTextAlignment(B_ALIGN_LEFT) +{ + fDivider = StringWidth(label) + 4.0f; +} + + +StringView::~StringView() +{ +} + + +void +StringView::SetAlignment(alignment labelAlignment, alignment textAlignment) +{ + if (labelAlignment == fLabelAlignment && textAlignment == fTextAlignment) + return; + + fLabelAlignment = labelAlignment; + fTextAlignment = textAlignment; + + Invalidate(); +} + + +void +StringView::GetAlignment(alignment* _label, alignment* _text) const +{ + if (_label) + *_label = fLabelAlignment; + if (_text) + *_text = fTextAlignment; +} + + +void +StringView::SetDivider(float divider) +{ + fDivider = divider; + _UpdateText(); + + Invalidate(); +} + + +void +StringView::AttachedToWindow() +{ + if (Parent() != NULL) + SetViewColor(Parent()->ViewColor()); + else + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + SetLowColor(ViewColor()); + _UpdateText(); +} + + +void +StringView::Draw(BRect updateRect) +{ + BRect rect = Bounds(); + + font_height fontHeight; + GetFontHeight(&fontHeight); + + float y = ceilf(fontHeight.ascent) + 1.0f; + float x; + + SetHighColor(IsEnabled() ? ui_color(B_CONTROL_TEXT_COLOR) + : tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DISABLED_LABEL_TINT)); + + if (Label()) { + switch (fLabelAlignment) { + case B_ALIGN_RIGHT: + x = Divider() - StringWidth(Label()) - 3.0f; + break; + + case B_ALIGN_CENTER: + x = Divider() - StringWidth(Label()) / 2.0f; + break; + + default: + x = 1.0f; + break; + } + + DrawString(Label(), BPoint(x, y)); + } + + if (fTruncatedText.String() != NULL) { + switch (fTextAlignment) { + case B_ALIGN_RIGHT: + x = rect.Width() - StringWidth(fTruncatedText.String()); + break; + + case B_ALIGN_CENTER: + x = Divider() + (rect.Width() - Divider() - StringWidth(Label())) / 2.0f; + break; + + default: + x = Divider() + 3.0f; + break; + } + + DrawString(fTruncatedText.String(), BPoint(x, y)); + } +} + + +void +StringView::FrameResized(float width, float height) +{ + BString oldTruncated = fTruncatedText; + _UpdateText(); + + if (oldTruncated != fTruncatedText) { + // invalidate text portion only + BRect rect = Bounds(); + rect.left = Divider(); + Invalidate(rect); + } +} + + +void +StringView::ResizeToPreferred() +{ + float width, height; + GetPreferredSize(&width, &height); + + // Resize the width only for B_ALIGN_LEFT (if its large enough already, that is) + if (Bounds().Width() > width + && (fLabelAlignment != B_ALIGN_LEFT || fTextAlignment != B_ALIGN_LEFT)) + width = Bounds().Width(); + + BView::ResizeTo(width, height); +} + + +void +StringView::GetPreferredSize(float* _width, float* _height) +{ + if (!Text() && !Label()) { + BView::GetPreferredSize(_width, _height); + return; + } + + if (_width) + *_width = 7.0f + ceilf(StringWidth(Label()) + StringWidth(Text())); + + if (_height) { + font_height fontHeight; + GetFontHeight(&fontHeight); + *_height = ceil(fontHeight.ascent + fontHeight.descent + fontHeight.leading) + 2.0f; + } +} + + +void +StringView::SetEnabled(bool enabled) +{ + if (IsEnabled() == enabled) + return; + + fEnabled = enabled; + Invalidate(); +} + + +void +StringView::SetLabel(const char* label) +{ + fLabel = label; + + // invalidate label portion only + BRect rect = Bounds(); + rect.right = Divider(); + Invalidate(rect); +} + + +void +StringView::SetText(const char* text) +{ + fText = text; + + _UpdateText(); + + // invalidate text portion only + BRect rect = Bounds(); + rect.left = Divider(); + Invalidate(rect); +} + + +void +StringView::_UpdateText() +{ + fTruncatedText = fText; + TruncateString(&fTruncatedText, B_TRUNCATE_MIDDLE, Bounds().Width() - Divider()); +} diff --git a/src/preferences/filetypes/StringView.h b/src/preferences/filetypes/StringView.h new file mode 100644 index 0000000000..f1f2399b63 --- /dev/null +++ b/src/preferences/filetypes/StringView.h @@ -0,0 +1,55 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef STRING_VIEW_H +#define STRING_VIEW_H + + +#include +#include + + +class StringView : public BView { + public: + StringView(BRect frame, const char* name, const char* label, + const char* text, uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP, + uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS); + virtual ~StringView(); + + virtual void Draw(BRect updateRect); + virtual void AttachedToWindow(); + + virtual void FrameResized(float width, float height); + + virtual void GetPreferredSize(float* _width, float* _height); + virtual void ResizeToPreferred(); + + void SetEnabled(bool enabled); + bool IsEnabled() const { return fEnabled; } + + void SetLabel(const char* label); + const char* Label() const { return fLabel.String(); } + + void SetText(const char* text); + const char* Text() const { return fText.String(); } + + void SetDivider(float divider); + float Divider() const { return fDivider; } + + void SetAlignment(alignment labelAlignment, alignment textAlignment); + void GetAlignment(alignment* _label, alignment* _text) const; + + private: + void _UpdateText(); + + BString fLabel; + BString fText; + BString fTruncatedText; + float fDivider; + alignment fLabelAlignment; + alignment fTextAlignment; + bool fEnabled; +}; + +#endif // STRING_VIEW_H