diff --git a/src/preferences/filetypes/FileTypeWindow.cpp b/src/preferences/filetypes/FileTypeWindow.cpp new file mode 100644 index 0000000000..319dcb2dbf --- /dev/null +++ b/src/preferences/filetypes/FileTypeWindow.cpp @@ -0,0 +1,381 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "FileTypes.h" +#include "FileTypeWindow.h" +#include "PreferredAppMenu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +const uint32 kMsgTypeEntered = 'type'; +const uint32 kMsgSelectType = 'sltp'; +const uint32 kMsgSameTypeAs = 'stpa'; + +const uint32 kMsgPreferredAppChosen = 'papc'; +const uint32 kMsgSelectPreferredApp = 'slpa'; +const uint32 kMsgSamePreferredAppAs = 'spaa'; + + +FileTypeWindow::FileTypeWindow(BPoint position, const BMessage& refs) + : BWindow(BRect(0.0f, 0.0f, 200.0f, 200.0f).OffsetBySelf(position), + "File Type", B_TITLED_WINDOW, + B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) +{ + // "Icon" group + +#if 0 + BFont font(be_bold_font); + float labelWidth = font.StringWidth("Icon"); + font_height fontHeight; + font.GetHeight(&fontHeight); + + BRect innerRect; + fIconView = new IconView(innerRect, "icon box", NULL); + fIconView->ResizeToPreferred(); + + rect.left = rect.right + 12.0f + B_V_SCROLL_BAR_WIDTH; + rect.right = rect.left + max_c(fIconView->Bounds().Width(), labelWidth) + 16.0f; + rect.bottom = rect.top + ceilf(fontHeight.ascent) + + max_c(fIconView->Bounds().Height(), + button->Bounds().Height() * 2.0f + 4.0f) + 12.0f; + rect.top -= 2.0f; + BBox* box = new BBox(rect); + box->SetLabel("Icon"); + topView->AddChild(box); + + innerRect.left = 8.0f; + innerRect.top = fontHeight.ascent / 2.0f + + (rect.Height() - fontHeight.ascent / 2.0f - fIconView->Bounds().Height()) / 2.0f + + 3.0f + fontHeight.ascent; + if (innerRect.top + fIconView->Bounds().Height() > box->Bounds().Height() - 6.0f) + innerRect.top = box->Bounds().Height() - 6.0f - fIconView->Bounds().Height(); + fIconView->MoveTo(innerRect.LeftTop()); + box->AddChild(fIconView); +#endif + 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); + + // "File Type" group + + BFont font(be_bold_font); +// float labelWidth = font.StringWidth("Icon"); + font_height fontHeight; + font.GetHeight(&fontHeight); + + rect.InsetBy(8.0f, 8.0f); + BBox* box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); + box->SetLabel("File Type"); + topView->AddChild(box); + + rect = box->Bounds(); + rect.InsetBy(8.0f, 4.0f + fontHeight.ascent + fontHeight.descent); + fTypeControl = new BTextControl(rect, "type", NULL, NULL, + new BMessage(kMsgTypeEntered), B_FOLLOW_LEFT_RIGHT); + fTypeControl->SetDivider(0.0f); + float width, height; + fTypeControl->GetPreferredSize(&width, &height); + fTypeControl->ResizeTo(rect.Width(), height); + box->AddChild(fTypeControl); + + // filter out invalid characters that can't be part of a MIME type name + BTextView* textView = fTypeControl->TextView(); + const char* disallowedCharacters = "/<>@,;:\"()[]?="; + for (int32 i = 0; disallowedCharacters[i]; i++) { + textView->DisallowChar(disallowedCharacters[i]); + } + + rect.OffsetBy(0.0f, fTypeControl->Bounds().Height() + 5.0f); + fSelectTypeButton = new BButton(rect, "select type", "Select" B_UTF8_ELLIPSIS, + new BMessage(kMsgSelectType), B_FOLLOW_LEFT | B_FOLLOW_TOP); + fSelectTypeButton->ResizeToPreferred(); + box->AddChild(fSelectTypeButton); + + rect.OffsetBy(fSelectTypeButton->Bounds().Width() + 8.0f, 0.0f); + fSameTypeAsButton = new BButton(rect, "same type as", "Same As" B_UTF8_ELLIPSIS, + new BMessage(kMsgSamePreferredAppAs), B_FOLLOW_LEFT | B_FOLLOW_TOP); + fSameTypeAsButton->ResizeToPreferred(); + box->AddChild(fSameTypeAsButton); + + box->ResizeTo(box->Bounds().Width(), fSelectTypeButton->Frame().bottom + + 8.0f); + + // "Preferred Application" group + + rect.top = box->Frame().bottom + 8.0f; + rect.bottom = rect.top + box->Bounds().Height(); + rect.left = 8.0f; + rect.right = Bounds().Width() - 8.0f; + box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); + box->SetLabel("Preferred Application"); + topView->AddChild(box); + + BMenu* menu = new BPopUpMenu("preferred"); + BMenuItem* item; + menu->AddItem(item = new BMenuItem("Default Application", + new BMessage(kMsgPreferredAppChosen))); + item->SetMarked(true); + + rect = fTypeControl->Frame(); + BView* constrainingView = new BView(rect, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW); + constrainingView->SetViewColor(topView->ViewColor()); + + fPreferredField = new BMenuField(rect.OffsetToCopy(B_ORIGIN), "preferred", + NULL, menu); + fPreferredField->GetPreferredSize(&width, &height); + fPreferredField->ResizeTo(rect.Width(), height); + constrainingView->AddChild(fPreferredField); + // we embed the menu field in another view to make it behave like + // we want so that it can't obscure other elements with larger + // labels + + box->AddChild(constrainingView); + + rect.OffsetBy(0.0f, fTypeControl->Bounds().Height() + 5.0f); + fSelectAppButton = new BButton(rect, "select app", "Select" B_UTF8_ELLIPSIS, + new BMessage(kMsgSelectPreferredApp), B_FOLLOW_LEFT | B_FOLLOW_TOP); + fSelectAppButton->ResizeToPreferred(); + box->AddChild(fSelectAppButton); + + rect.OffsetBy(fSelectAppButton->Bounds().Width() + 8.0f, 0.0f); + fSameAppAsButton = new BButton(rect, "same app as", "Same As" B_UTF8_ELLIPSIS, + new BMessage(kMsgSamePreferredAppAs), B_FOLLOW_LEFT | B_FOLLOW_TOP); + fSameAppAsButton->ResizeToPreferred(); + box->AddChild(fSameAppAsButton); + + ResizeTo(fSameAppAsButton->Frame().right + 100.0f, box->Frame().bottom + 8.0f); + SetSizeLimits(fSameAppAsButton->Frame().right + 24.0f, 32767.0f, Bounds().Height(), + Bounds().Height()); + + fTypeControl->MakeFocus(true); + + BMimeType::StartWatching(this); + _SetTo(refs); +} + + +FileTypeWindow::~FileTypeWindow() +{ + BMimeType::StopWatching(this); +} + + +BString +FileTypeWindow::_Title(const BMessage& refs) +{ + BString title; + entry_ref ref; + if (refs.FindRef("refs", 1, &ref) == B_OK) { + bool same = false; + BEntry entry, parent; + if (entry.SetTo(&ref) == B_OK + && entry.GetParent(&parent) == B_OK) { + same = true; + + // Check if all entries have the same parent directory + for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) { + BEntry directory; + if (entry.SetTo(&ref) == B_OK + && entry.GetParent(&directory) == B_OK) { + if (directory != parent) { + same = false; + break; + } + } + } + } + + char name[B_FILE_NAME_LENGTH]; + if (same && parent.GetName(name) == B_OK) { + title = "Multiple Files from \""; + title.Append(name); + title.Append("\""); + } else + title = "[Multiple Files]"; + } else if (refs.FindRef("refs", 0, &ref) == B_OK) + title = ref.name; + + title.Append(" File Type"); + return title; +} + + +void +FileTypeWindow::_SetTo(const BMessage& refs) +{ + SetTitle(_Title(refs).String()); + + // get common info and icons + + fCommonPreferredApp = ""; + fCommonType = ""; + entry_ref ref; + for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) { + BNode node(&ref); + if (node.InitCheck() != B_OK) + continue; + + BNodeInfo info(&node); + if (info.InitCheck() != B_OK) + continue; + + // TODO: watch entries? + + entry_ref* copiedRef = new entry_ref(ref); + fEntries.AddItem(copiedRef); + + char type[B_MIME_TYPE_LENGTH]; + if (info.GetType(type) != B_OK) + type[0] = '\0'; + + if (i > 0) { + if (fCommonType != type) + fCommonType = ""; + } else + fCommonType = type; + + char preferredApp[B_MIME_TYPE_LENGTH]; + if (info.GetPreferredApp(preferredApp) != B_OK) + preferredApp[0] = '\0'; + + if (i > 0) { + if (fCommonPreferredApp != preferredApp) + fCommonPreferredApp = ""; + } else + fCommonPreferredApp = preferredApp; + } + + fTypeControl->SetText(fCommonType.String()); + _UpdatePreferredApps(); +} + + +void +FileTypeWindow::_AdoptType() +{ + for (int32 i = 0; i < fEntries.CountItems(); i++) { + BNode node(fEntries.ItemAt(i)); + BNodeInfo info(&node); + if (node.InitCheck() != B_OK + || info.InitCheck() != B_OK) + continue; + + info.SetType(fCommonType.String()); + } +} + + +void +FileTypeWindow::_AdoptPreferredApp() +{ + for (int32 i = 0; i < fEntries.CountItems(); i++) { + BNode node(fEntries.ItemAt(i)); + if (fCommonPreferredApp.Length() == 0) { + node.RemoveAttr("BEOS:PREF_APP"); + continue; + } + + BNodeInfo info(&node); + if (node.InitCheck() != B_OK + || info.InitCheck() != B_OK) + continue; + + info.SetPreferredApp(fCommonPreferredApp.String()); + } +} + + +void +FileTypeWindow::_UpdatePreferredApps() +{ + BMimeType type(fCommonType.String()); + update_preferred_app_menu(fPreferredField->Menu(), &type, + kMsgPreferredAppChosen, fCommonPreferredApp.String()); +} + + +void +FileTypeWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + // File Type group + + case kMsgTypeEntered: + fCommonType = fTypeControl->Text(); + _AdoptType(); + break; + + // Preferred Application group + + case kMsgPreferredAppChosen: + { + const char* signature; + if (message->FindString("signature", &signature) == B_OK) + fCommonPreferredApp = signature; + else + fCommonPreferredApp = ""; + + _AdoptPreferredApp(); + break; + } + +#if 0 + case kMsgSelectPreferredApp: + be_app->PostMessage(kMsgOpenSelectPanel); + break; + case kMsgPreferredAppOpened: + _AdoptPreferredApplication(message, false); + break; + + case kMsgSamePreferredAppAs: + be_app->PostMessage(kMsgOpenSameAsPanel); + break; + case kMsgSamePreferredAppAsOpened: + _AdoptPreferredApplication(message, true); + break; +#endif + + 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 (which == B_MIME_TYPE_DELETED +#ifdef __HAIKU__ + || which == B_SUPPORTED_TYPES_CHANGED +#endif + ) + _UpdatePreferredApps(); + break; + + default: + BWindow::MessageReceived(message); + } +} + + +bool +FileTypeWindow::QuitRequested() +{ + be_app->PostMessage(kMsgTypeWindowClosed); + return true; +} + diff --git a/src/preferences/filetypes/FileTypeWindow.h b/src/preferences/filetypes/FileTypeWindow.h new file mode 100644 index 0000000000..186dece4e8 --- /dev/null +++ b/src/preferences/filetypes/FileTypeWindow.h @@ -0,0 +1,54 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef FILE_TYPE_WINDOW_H +#define FILE_TYPE_WINDOW_H + + +#include +#include +#include + +#include + +class BButton; +class BMenuField; +class BTextControl; + +class IconView; +class MimeTypeListView; + + +class FileTypeWindow : public BWindow { + public: + FileTypeWindow(BPoint position, const BMessage& refs); + virtual ~FileTypeWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + private: + BString _Title(const BMessage& refs); + void _SetTo(const BMessage& refs); + void _AdoptType(); + void _AdoptPreferredApp(); + void _UpdatePreferredApps(); + + private: + BObjectList fEntries; + BString fCommonType; + BString fCommonPreferredApp; + + BTextControl* fTypeControl; + BButton* fSelectTypeButton; + BButton* fSameTypeAsButton; + + IconView* fIconView; + + BMenuField* fPreferredField; + BButton* fSelectAppButton; + BButton* fSameAppAsButton; +}; + +#endif // FILE_TYPE_WINDOW_H diff --git a/src/preferences/filetypes/FileTypes.cpp b/src/preferences/filetypes/FileTypes.cpp index 358b3f186c..872ff3b2e3 100644 --- a/src/preferences/filetypes/FileTypes.cpp +++ b/src/preferences/filetypes/FileTypes.cpp @@ -6,8 +6,10 @@ #include "ApplicationTypesWindow.h" #include "FileTypes.h" +#include "FileTypeWindow.h" #include "FileTypesWindow.h" +#include #include #include #include @@ -44,6 +46,7 @@ class FileTypes : public BApplication { BWindow *fTypesWindow; BWindow *fApplicationTypesWindow; uint32 fWindowCount; + uint32 fTypeWindowCount; BRect fTypesWindowFrame; BRect fApplicationTypesWindowFrame; }; @@ -53,7 +56,8 @@ FileTypes::FileTypes() : BApplication(kSignature), fTypesWindow(NULL), fApplicationTypesWindow(NULL), - fWindowCount(0) + fWindowCount(0), + fTypeWindowCount(0) { fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE | B_DIRECTORY_NODE, false); @@ -87,6 +91,7 @@ FileTypes::RefsReceived(BMessage *message) { bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0; + // filter out applications and entries we can't open int32 index = 0; entry_ref ref; while (message->FindRef("refs", index++, &ref) == B_OK) { @@ -96,7 +101,11 @@ FileTypes::RefsReceived(BMessage *message) // TODO: open FileType window } + BFile file; + status = file.SetTo(&ref, B_READ_ONLY); if (status != B_OK) { + // file cannot be opened + char buffer[1024]; snprintf(buffer, sizeof(buffer), "Could not open \"%s\":\n" @@ -106,8 +115,36 @@ FileTypes::RefsReceived(BMessage *message) (new BAlert("FileTypes request", buffer, "Ok", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); + + message->RemoveData("refs", --index); + continue; } + + BAppFileInfo appInfo(&file); + if (appInfo.InitCheck() != B_OK) + continue; + + char signature[B_MIME_TYPE_LENGTH]; + if (appInfo.GetSignature(signature) != B_OK) + continue; + + // remove application from list + message->RemoveData("refs", --index); + + printf("found app: %s\n", ref.name); } + + if (message->FindRef("refs", &ref) != B_OK) + return; + + // There are some refs left that want to be handled by the type window + BPoint point(100.0f + 20.0f * fTypeWindowCount, 110.0f + 20.0f * fTypeWindowCount); + + BWindow* window = new FileTypeWindow(point, *message); + window->Show(); + + fTypeWindowCount++; + fWindowCount++; } @@ -186,6 +223,10 @@ FileTypes::MessageReceived(BMessage *message) _WindowClosed(); break; + case kMsgTypeWindowClosed: + fTypeWindowCount--; + // supposed to fall through + case kMsgWindowClosed: _WindowClosed(); break; @@ -229,6 +270,10 @@ FileTypes::MessageReceived(BMessage *message) PostMessage(B_QUIT_REQUESTED); break; + case B_SIMPLE_DATA: + RefsReceived(message); + break; + default: BApplication::MessageReceived(message); break; diff --git a/src/preferences/filetypes/FileTypes.h b/src/preferences/filetypes/FileTypes.h index e944111ccd..bfec2fbf9a 100644 --- a/src/preferences/filetypes/FileTypes.h +++ b/src/preferences/filetypes/FileTypes.h @@ -21,6 +21,7 @@ static const uint32 kMsgTypesWindowClosed = 'clTw'; static const uint32 kMsgOpenApplicationTypesWindow = 'opAw'; static const uint32 kMsgApplicationTypesWindowClosed = 'clAw'; +static const uint32 kMsgTypeWindowClosed = 'cltw'; static const uint32 kMsgWindowClosed = 'WiCl'; #endif // FILE_TYPES_H diff --git a/src/preferences/filetypes/FileTypesWindow.cpp b/src/preferences/filetypes/FileTypesWindow.cpp index d98aff3e54..b0bd799262 100644 --- a/src/preferences/filetypes/FileTypesWindow.cpp +++ b/src/preferences/filetypes/FileTypesWindow.cpp @@ -11,6 +11,7 @@ #include "FileTypesWindow.h" #include "MimeTypeListView.h" #include "NewFileTypeWindow.h" +#include "PreferredAppMenu.h" #include "StringView.h" #include @@ -83,16 +84,6 @@ class IconView : public BControl { // #pragma mark - -static int -compare_menu_items(const void* _a, const void* _b) -{ - BMenuItem* a = *(BMenuItem**)_a; - BMenuItem* b = *(BMenuItem**)_b; - - return strcasecmp(a->Label(), b->Label()); -} - - static bool is_application_in_message(BMessage& applications, const char* app) { @@ -639,152 +630,10 @@ FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs) } -void -FileTypesWindow::_AddSignature(BMenuItem* item, const char* signature) -{ - const char* subType = strchr(signature, '/'); - if (subType == NULL) - return; - - char label[B_MIME_TYPE_LENGTH]; - snprintf(label, sizeof(label), "%s (%s)", item->Label(), subType + 1); - - item->SetLabel(label); -} - - -BMenuItem* -FileTypesWindow::_CreateApplicationItem(const char* signature) -{ - char name[B_FILE_NAME_LENGTH]; - - BMessage* message = new BMessage(kMsgPreferredAppChosen); - message->AddString("signature", signature); - - BMimeType applicationType(signature); - if (applicationType.GetShortDescription(name) == B_OK) - return new BMenuItem(name, message); - - return new BMenuItem(signature, message); -} - - void FileTypesWindow::_UpdatePreferredApps(BMimeType* type) { - // clear menu - - BMenu* menu = fPreferredField->Menu(); - for (int32 i = menu->CountItems(); i-- > 1;) { - delete menu->RemoveItem(i); - } - - // fill it again - - menu->ItemAt(0)->SetMarked(true); - - BMessage applications; - if (type == NULL || type->GetSupportingApps(&applications) != B_OK) - return; - - char preferred[B_MIME_TYPE_LENGTH]; - if (type->GetPreferredApp(preferred) != B_OK) - preferred[0] = '\0'; - - int32 lastFullSupport; - if (applications.FindInt32("be:sub", &lastFullSupport) != B_OK) - lastFullSupport = -1; - - BList subList; - BList superList; - - const char* signature; - int32 i = 0; - while (applications.FindString("applications", i, &signature) == B_OK) { - BMenuItem* item = _CreateApplicationItem(signature); - - if (i < lastFullSupport) - subList.AddItem(item); - else - superList.AddItem(item); - - i++; - } - - // sort lists - - subList.SortItems(compare_menu_items); - superList.SortItems(compare_menu_items); - - // add lists to the menu - - if (subList.CountItems() != 0 || superList.CountItems() != 0) - menu->AddSeparatorItem(); - - for (int32 i = 0; i < subList.CountItems(); i++) { - menu->AddItem((BMenuItem*)subList.ItemAt(i)); - } - - // Add type separator - if (superList.CountItems() != 0 && subList.CountItems() != 0) - menu->AddSeparatorItem(); - - for (int32 i = 0; i < superList.CountItems(); i++) { - menu->AddItem((BMenuItem*)superList.ItemAt(i)); - } - - // make items unique and select current choice - - bool lastItemSame = false; - const char* lastSignature = NULL; - BMenuItem* last = NULL; - BMenuItem* select = NULL; - - for (int32 index = 0; index < menu->CountItems(); index++) { - BMenuItem* item = menu->ItemAt(index); - if (item == NULL) - continue; - - if (item->Message() == NULL - || item->Message()->FindString("signature", &signature) != B_OK) - continue; - - if (!strcasecmp(signature, preferred)) - select = item; - - if (last == NULL || strcmp(last->Label(), item->Label())) { - if (lastItemSame) - _AddSignature(last, lastSignature); - - lastItemSame = false; - last = item; - lastSignature = signature; - continue; - } - - lastItemSame = true; - _AddSignature(last, lastSignature); - - last = item; - lastSignature = signature; - } - - if (lastItemSame) - _AddSignature(last, lastSignature); - - if (select != NULL) { - // We don't select the item earlier, so that the menu field can - // pick up the signature as well as label. - select->SetMarked(true); - } else if (preferred[0]) { - // The preferred application is not an application that support - // this file type! - BMenuItem* item = _CreateApplicationItem(preferred); - - menu->AddSeparatorItem(); - menu->AddItem(item); - item->SetMarked(item); - } + update_preferred_app_menu(fPreferredField->Menu(), type, kMsgPreferredAppChosen); } @@ -880,6 +729,14 @@ void FileTypesWindow::MessageReceived(BMessage* message) { switch (message->what) { + case B_SIMPLE_DATA: + type_code type; + if (message->GetInfo("refs", &type) == B_OK + && type == B_REF_TYPE) { + be_app->PostMessage(message); + } + break; + case kMsgToggleIcons: { BMenuItem* item; @@ -1131,9 +988,12 @@ FileTypesWindow::MessageReceived(BMessage* message) _SetType(NULL); } else { // this change could still affect our current type - + if (which == B_MIME_TYPE_DELETED || which == B_PREFERRED_APP_CHANGED +#ifdef __HAIKU__ + || which == B_SUPPORTED_TYPES_CHANGED +#endif || which == B_ICON_FOR_TYPE_CHANGED) { _UpdatePreferredApps(&fCurrentType); _UpdateIcon(&fCurrentType); diff --git a/src/preferences/filetypes/FileTypesWindow.h b/src/preferences/filetypes/FileTypesWindow.h index d339cdc3da..7c88d12c9a 100644 --- a/src/preferences/filetypes/FileTypesWindow.h +++ b/src/preferences/filetypes/FileTypesWindow.h @@ -36,8 +36,6 @@ class FileTypesWindow : public BWindow { private: void _UpdateExtensions(BMimeType* type); void _AdoptPreferredApplication(BMessage* message, bool sameAs); - void _AddSignature(BMenuItem* item, const char* signature); - BMenuItem* _CreateApplicationItem(const char* signature); void _UpdatePreferredApps(BMimeType* type); void _UpdateIcon(BMimeType* type); void _SetType(BMimeType* type, int32 forceUpdate = 0); diff --git a/src/preferences/filetypes/Jamfile b/src/preferences/filetypes/Jamfile index b941956677..2f33e359b5 100644 --- a/src/preferences/filetypes/Jamfile +++ b/src/preferences/filetypes/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src preferences filetypes ; SetSubDirSupportedPlatformsBeOSCompatible ; AddSubDirSupportedPlatforms libbe_test ; +UsePrivateHeaders shared ; SubDirSysHdrs $(HAIKU_TOP) src kits tracker ; Preference FileTypes : @@ -14,6 +15,8 @@ Preference FileTypes : NewFileTypeWindow.cpp AttributeWindow.cpp ExtensionWindow.cpp + FileTypeWindow.cpp + PreferredAppMenu.cpp StringView.cpp : be tracker : FileTypes.rdef FileTypes.icons.rdef diff --git a/src/preferences/filetypes/PreferredAppMenu.cpp b/src/preferences/filetypes/PreferredAppMenu.cpp new file mode 100644 index 0000000000..f8273dc011 --- /dev/null +++ b/src/preferences/filetypes/PreferredAppMenu.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "PreferredAppMenu.h" + +#include +#include +#include + +#include +#include + + +static int +compare_menu_items(const void* _a, const void* _b) +{ + BMenuItem* a = *(BMenuItem**)_a; + BMenuItem* b = *(BMenuItem**)_b; + + return strcasecmp(a->Label(), b->Label()); +} + + +static void +add_signature(BMenuItem* item, const char* signature) +{ + const char* subType = strchr(signature, '/'); + if (subType == NULL) + return; + + char label[B_MIME_TYPE_LENGTH]; + snprintf(label, sizeof(label), "%s (%s)", item->Label(), subType + 1); + + item->SetLabel(label); +} + + +static BMenuItem* +create_application_item(const char* signature, uint32 what) +{ + char name[B_FILE_NAME_LENGTH]; + + BMessage* message = new BMessage(what); + message->AddString("signature", signature); + + BMimeType applicationType(signature); + if (applicationType.GetShortDescription(name) == B_OK) + return new BMenuItem(name, message); + + return new BMenuItem(signature, message); +} + + +void +update_preferred_app_menu(BMenu* menu, BMimeType* type, uint32 what, + const char* preferredFrom) +{ + // clear menu (but leave the first entry, ie. "None") + + for (int32 i = menu->CountItems(); i-- > 1;) { + delete menu->RemoveItem(i); + } + + // fill it again + + menu->ItemAt(0)->SetMarked(true); + + BMessage applications; + if (type == NULL || type->GetSupportingApps(&applications) != B_OK) + return; + + char preferred[B_MIME_TYPE_LENGTH]; + if (type->GetPreferredApp(preferred) != B_OK) + preferred[0] = '\0'; + + int32 lastFullSupport; + if (applications.FindInt32("be:sub", &lastFullSupport) != B_OK) + lastFullSupport = -1; + + BList subList; + BList superList; + + const char* signature; + int32 i = 0; + while (applications.FindString("applications", i, &signature) == B_OK) { + BMenuItem* item = create_application_item(signature, what); + + if (i < lastFullSupport) + subList.AddItem(item); + else + superList.AddItem(item); + + i++; + } + + // sort lists + + subList.SortItems(compare_menu_items); + superList.SortItems(compare_menu_items); + + // add lists to the menu + + if (subList.CountItems() != 0 || superList.CountItems() != 0) + menu->AddSeparatorItem(); + + for (int32 i = 0; i < subList.CountItems(); i++) { + menu->AddItem((BMenuItem*)subList.ItemAt(i)); + } + + // Add type separator + if (superList.CountItems() != 0 && subList.CountItems() != 0) + menu->AddSeparatorItem(); + + for (int32 i = 0; i < superList.CountItems(); i++) { + menu->AddItem((BMenuItem*)superList.ItemAt(i)); + } + + // make items unique and select current choice + + bool lastItemSame = false; + const char* lastSignature = NULL; + BMenuItem* last = NULL; + BMenuItem* select = NULL; + + for (int32 index = 0; index < menu->CountItems(); index++) { + BMenuItem* item = menu->ItemAt(index); + if (item == NULL) + continue; + + if (item->Message() == NULL + || item->Message()->FindString("signature", &signature) != B_OK) + continue; + + if (preferredFrom == NULL && !strcasecmp(signature, preferred) + || preferredFrom != NULL && !strcasecmp(signature, preferredFrom)) + select = item; + + if (last == NULL || strcmp(last->Label(), item->Label())) { + if (lastItemSame) + add_signature(last, lastSignature); + + lastItemSame = false; + last = item; + lastSignature = signature; + continue; + } + + lastItemSame = true; + add_signature(last, lastSignature); + + last = item; + lastSignature = signature; + } + + if (lastItemSame) + add_signature(last, lastSignature); + + if (select != NULL) { + // We don't select the item earlier, so that the menu field can + // pick up the signature as well as label. + select->SetMarked(true); + } else if (preferredFrom == NULL && preferred[0] + || preferredFrom && preferredFrom[0]) { + // The preferred application is not an application that support + // this file type! + BMenuItem* item = create_application_item(preferredFrom + ? preferredFrom : preferred, what); + + menu->AddSeparatorItem(); + menu->AddItem(item); + item->SetMarked(item); + } +} + diff --git a/src/preferences/filetypes/PreferredAppMenu.h b/src/preferences/filetypes/PreferredAppMenu.h new file mode 100644 index 0000000000..6eeb33bfef --- /dev/null +++ b/src/preferences/filetypes/PreferredAppMenu.h @@ -0,0 +1,17 @@ +/* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef PREFERRED_APP_MENU_H +#define PREFERRED_APP_MENU_H + + +#include + +class BMenu; +class BMimeType; + +void update_preferred_app_menu(BMenu* menu, BMimeType* type, uint32 what, + const char* preferredFrom = NULL); + +#endif // PREFERRED_APP_MENU_H