/* * Copyright 2006-2007, Axel Dörfler, axeld@pinc-software.de. * All rights reserved. Distributed under the terms of the MIT License. */ #include "ApplicationTypesWindow.h" #include "ApplicationTypeWindow.h" #include "FileTypes.h" #include "FileTypesWindow.h" #include "FileTypeWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "FileTypes" const char* kSignature = "application/x-vnd.Haiku-FileTypes"; static const uint32 kMsgFileTypesSettings = 'FTst'; static const uint32 kCascadeOffset = 20; class Settings { public: Settings(); ~Settings(); const BMessage& Message() const { return fMessage; } void UpdateFrom(BMessage* message); private: void _SetDefaults(); status_t _Open(BFile* file, int32 mode); BMessage fMessage; bool fUpdated; }; class FileTypes : public BApplication { public: FileTypes(); virtual ~FileTypes(); virtual void ReadyToRun(); virtual void RefsReceived(BMessage* message); virtual void ArgvReceived(int32 argc, char** argv); virtual void MessageReceived(BMessage* message); virtual bool QuitRequested(); private: void _WindowClosed(); // Add one degree of offset to the starting position of the next ApplicationTypeWindow void _AppTypeCascade(BRect lastFrame); private: Settings fSettings; BFilePanel* fFilePanel; BMessenger fFilePanelTarget; FileTypesWindow* fTypesWindow; BWindow* fApplicationTypesWindow; uint32 fWindowCount; uint32 fTypeWindowCount; BString fArgvType; }; Settings::Settings() : fMessage(kMsgFileTypesSettings), fUpdated(false) { _SetDefaults(); BFile file; if (_Open(&file, B_READ_ONLY) != B_OK) return; BMessage settings; if (settings.Unflatten(&file) == B_OK) { // We don't unflatten into our default message to make sure // nothing is lost (because of old or corrupted on disk settings) UpdateFrom(&settings); fUpdated = false; } } Settings::~Settings() { // only save the settings if something has changed if (!fUpdated) return; BFile file; if (_Open(&file, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY) != B_OK) return; fMessage.Flatten(&file); } void Settings::UpdateFrom(BMessage* message) { BRect frame; if (message->FindRect("file_types_frame", &frame) == B_OK) fMessage.ReplaceRect("file_types_frame", frame); if (message->FindRect("app_types_frame", &frame) == B_OK) fMessage.ReplaceRect("app_types_frame", frame); // "app_type_initial_frame" is omitted because it is not meant to be updated if (message->FindRect("app_type_next_frame", &frame) == B_OK) fMessage.ReplaceRect("app_type_next_frame", frame); bool showIcons; if (message->FindBool("show_icons", &showIcons) == B_OK) fMessage.ReplaceBool("show_icons", showIcons); bool showRule; if (message->FindBool("show_rule", &showRule) == B_OK) fMessage.ReplaceBool("show_rule", showRule); float splitWeight; if (message->FindFloat("left_split_weight", &splitWeight) == B_OK) fMessage.ReplaceFloat("left_split_weight", splitWeight); if (message->FindFloat("right_split_weight", &splitWeight) == B_OK) fMessage.ReplaceFloat("right_split_weight", splitWeight); fUpdated = true; } void Settings::_SetDefaults() { fMessage.AddRect("file_types_frame", BRect(80.0f, 80.0f, 600.0f, 480.0f)); fMessage.AddRect("app_types_frame", BRect(100.0f, 100.0f, 540.0f, 480.0f)); fMessage.AddRect("app_type_initial_frame", BRect(100.0f, 110.0f, 250.0f, 340.0f)); fMessage.AddRect("app_type_next_frame", BRect(100.0f, 110.0f, 250.0f, 340.0f)); fMessage.AddBool("show_icons", true); fMessage.AddBool("show_rule", false); fMessage.AddFloat("left_split_weight", 0.2); fMessage.AddFloat("right_split_weight", 0.8); } status_t Settings::_Open(BFile* file, int32 mode) { BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) return B_ERROR; path.Append("FileTypes settings"); return file->SetTo(path.Path(), mode); } // #pragma mark - FileTypes::FileTypes() : BApplication(kSignature), fTypesWindow(NULL), fApplicationTypesWindow(NULL), fWindowCount(0), fTypeWindowCount(0) { fFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false); } FileTypes::~FileTypes() { delete fFilePanel; } void FileTypes::ReadyToRun() { // are there already windows open? if (CountWindows() != 1) return; // if not, open the FileTypes window PostMessage(kMsgOpenTypesWindow); } void 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) { BEntry entry; BFile file; status_t status = entry.SetTo(&ref, traverseLinks); if (status == B_OK) status = file.SetTo(&entry, B_READ_ONLY); if (status != B_OK) { // file cannot be opened char buffer[1024]; snprintf(buffer, sizeof(buffer), B_TRANSLATE("Could not open \"%s\":\n" "%s"), ref.name, strerror(status)); BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), buffer, B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); message->RemoveData("refs", --index); continue; } if (!is_application(file) && !is_resource(file)) { entry_ref target; if (entry.GetRef(&target) == B_OK && target != ref) message->ReplaceRef("refs", index - 1, &ref); continue; } // remove application from list message->RemoveData("refs", --index); // There are some refs left that want to be handled by the type window BWindow* window = new ApplicationTypeWindow(fSettings.Message(), entry); _AppTypeCascade(window->Frame()); // For accurate height and width, get the frame that results after layouting, // instead of the initial frame that's stored in fSettings. window->Show(); fTypeWindowCount++; fWindowCount++; } 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 + kCascadeOffset * fTypeWindowCount, 110.0f + kCascadeOffset * fTypeWindowCount); BWindow* window = new FileTypeWindow(point, *message); window->Show(); fTypeWindowCount++; fWindowCount++; } void FileTypes::ArgvReceived(int32 argc, char** argv) { if (argc == 3 && strcmp(argv[1], "-type") == 0) { fArgvType = argv[2]; return; } BMessage* message = CurrentMessage(); BDirectory currentDirectory; if (message != NULL) currentDirectory.SetTo(message->FindString("cwd")); BMessage refs; for (int i = 1 ; i < argc ; i++) { BPath path; if (argv[i][0] == '/') path.SetTo(argv[i]); else path.SetTo(¤tDirectory, argv[i]); status_t status; entry_ref ref; BEntry entry; if ((status = entry.SetTo(path.Path(), false)) != B_OK || (status = entry.GetRef(&ref)) != B_OK) { fprintf(stderr, "Could not open file \"%s\": %s\n", path.Path(), strerror(status)); continue; } refs.AddRef("refs", &ref); } RefsReceived(&refs); } void FileTypes::MessageReceived(BMessage* message) { switch (message->what) { case kMsgSettingsChanged: fSettings.UpdateFrom(message); break; case kMsgOpenTypesWindow: if (fTypesWindow == NULL) { fTypesWindow = new FileTypesWindow(fSettings.Message()); if (fArgvType.Length() > 0) { // Set the window to the type that was requested on the // command line (-type), we do this only once, if we // ever opened more than one FileTypesWindow. fTypesWindow->SelectType(fArgvType.String()); fArgvType = ""; } fTypesWindow->Show(); fWindowCount++; } else fTypesWindow->Activate(true); break; case kMsgTypesWindowClosed: fTypesWindow = NULL; _WindowClosed(); break; case kMsgOpenApplicationTypesWindow: if (fApplicationTypesWindow == NULL) { fApplicationTypesWindow = new ApplicationTypesWindow( fSettings.Message()); fApplicationTypesWindow->Show(); fWindowCount++; } else fApplicationTypesWindow->Activate(true); break; case kMsgApplicationTypesWindowClosed: fApplicationTypesWindow = NULL; _WindowClosed(); break; case kMsgTypeWindowClosed: fTypeWindowCount--; // supposed to fall through case kMsgWindowClosed: _WindowClosed(); break; case kMsgOpenFilePanel: { // the open file panel sends us a message when it's done const char* subTitle; if (message->FindString("title", &subTitle) != B_OK) subTitle = B_TRANSLATE("Open file"); int32 what; if (message->FindInt32("message", &what) != B_OK) what = B_REFS_RECEIVED; BMessenger target; if (message->FindMessenger("target", &target) != B_OK) target = be_app_messenger; BString title = B_TRANSLATE("FileTypes"); if (subTitle != NULL && subTitle[0]) { title.Append(": "); title.Append(subTitle); } uint32 flavors = B_FILE_NODE; if (message->FindBool("allowDirs")) flavors |= B_DIRECTORY_NODE; fFilePanel->SetNodeFlavors(flavors); fFilePanel->SetMessage(new BMessage(what)); fFilePanel->Window()->SetTitle(title.String()); fFilePanel->SetTarget(target); if (!fFilePanel->IsShowing()) fFilePanel->Show(); break; } case B_SILENT_RELAUNCH: // In case we were launched via the add-on, there is no types // window yet. if (fTypesWindow == NULL) PostMessage(kMsgOpenTypesWindow); break; case B_CANCEL: if (fWindowCount == 0) PostMessage(B_QUIT_REQUESTED); break; case B_SIMPLE_DATA: RefsReceived(message); break; default: BApplication::MessageReceived(message); break; } } bool FileTypes::QuitRequested() { return true; } void FileTypes::_WindowClosed() { if (--fWindowCount == 0 && !fFilePanel->IsShowing()) PostMessage(B_QUIT_REQUESTED); } void FileTypes::_AppTypeCascade(BRect lastFrame) { BScreen screen; BRect screenBorder = screen.Frame(); BRect initFrame; float left = lastFrame.left + kCascadeOffset; if (left + lastFrame.Width() > screenBorder.right) { // If about to cascade off the right edge of the screen, revert the horizontal // position to that of the first window. if (fSettings.Message().FindRect("app_type_initial_frame", &initFrame) == B_OK) left = initFrame.LeftTop().x; } float top = lastFrame.top + kCascadeOffset; if (top + lastFrame.Height() > screenBorder.bottom) { if (fSettings.Message().FindRect("app_type_initial_frame", &initFrame) == B_OK) top = initFrame.LeftTop().y; } lastFrame.OffsetTo(BPoint(left, top)); BMessage update(kMsgSettingsChanged); update.AddRect("app_type_next_frame", lastFrame); fSettings.UpdateFrom(&update); } // #pragma mark - bool is_application(BFile& file) { BAppFileInfo appInfo(&file); if (appInfo.InitCheck() != B_OK) return false; char type[B_MIME_TYPE_LENGTH]; if (appInfo.GetType(type) != B_OK || strcasecmp(type, B_APP_MIME_TYPE)) return false; return true; } bool is_resource(BFile& file) { BResources resources(&file); if (resources.InitCheck() != B_OK) return false; BNodeInfo nodeInfo(&file); char type[B_MIME_TYPE_LENGTH]; if (nodeInfo.GetType(type) != B_OK || strcasecmp(type, B_RESOURCE_MIME_TYPE)) return false; return true; } void error_alert(const char* message, status_t status, alert_type type) { char warning[512]; if (status != B_OK) { snprintf(warning, sizeof(warning), "%s:\n\t%s\n", message, strerror(status)); } BAlert* alert = new BAlert(B_TRANSLATE("FileTypes request"), status == B_OK ? message : warning, B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, type); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); } int main(int argc, char** argv) { FileTypes probe; probe.Run(); return 0; }