/* * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ #include "DiskProbe.h" #include "DataEditor.h" #include "DataView.h" #include "FileWindow.h" #include "AttributeWindow.h" #include "OpenWindow.h" #include "FindWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "DiskProbe" const char *kSignature = "application/x-vnd.Haiku-DiskProbe"; static const uint32 kMsgDiskProbeSettings = 'DPst'; static const uint32 kCascadeOffset = 20; struct disk_probe_settings { BRect window_frame; int32 base_type; int32 font_size; int32 flags; }; enum disk_probe_flags { kCaseSensitive = 0x01, // this flag alone is R5 DiskProbe settings compatible kHexFindMode = 0x02, }; class Settings { public: Settings(); ~Settings(); const BMessage &Message() const { return fMessage; } void UpdateFrom(BMessage *message); private: status_t Open(BFile *file, int32 mode); BMessage fMessage; bool fUpdated; }; class DiskProbe : public BApplication { public: DiskProbe(); virtual ~DiskProbe(); virtual void ReadyToRun(); virtual void RefsReceived(BMessage *message); virtual void ArgvReceived(int32 argc, char **argv); virtual void MessageReceived(BMessage *message); virtual bool QuitRequested(); private: status_t Probe(BEntry &entry, const char *attribute = NULL); Settings fSettings; BFilePanel *fFilePanel; BWindow *fOpenWindow; FindWindow *fFindWindow; uint32 fWindowCount; BRect fWindowFrame; BMessenger fFindTarget; }; //----------------- Settings::Settings() : fMessage(kMsgDiskProbeSettings), fUpdated(false) { BScreen screen; fMessage.AddRect("window_frame", BRect(50, 50, screen.Frame().Width() - 50, screen.Frame().Height() - 50)); fMessage.AddInt32("base_type", kHexBase); fMessage.AddFloat("font_size", 12.0f); fMessage.AddBool("case_sensitive", true); fMessage.AddInt8("find_mode", kAsciiMode); BFile file; if (Open(&file, B_READ_ONLY) != B_OK) return; // ToDo: load/save settings as flattened BMessage - but not yet, // since that will break compatibility with R5's DiskProbe disk_probe_settings settings; if (file.Read(&settings, sizeof(settings)) == sizeof(settings)) { #if B_HOST_IS_BENDIAN // settings are saved in little endian settings.window_frame.left = B_LENDIAN_TO_HOST_FLOAT( settings.window_frame.left); settings.window_frame.top = B_LENDIAN_TO_HOST_FLOAT( settings.window_frame.top); settings.window_frame.right = B_LENDIAN_TO_HOST_FLOAT( settings.window_frame.right); settings.window_frame.bottom = B_LENDIAN_TO_HOST_FLOAT( settings.window_frame.bottom); #endif // check if the window frame is on screen at all BScreen screen; if (screen.Frame().Contains(settings.window_frame.LeftTop()) && settings.window_frame.Width() < screen.Frame().Width() && settings.window_frame.Height() < screen.Frame().Height()) fMessage.ReplaceRect("window_frame", settings.window_frame); if (settings.base_type == kHexBase || settings.base_type == kDecimalBase) fMessage.ReplaceInt32("base_type", B_LENDIAN_TO_HOST_INT32(settings.base_type)); if (settings.font_size >= 0 && settings.font_size <= 72) fMessage.ReplaceFloat("font_size", float(B_LENDIAN_TO_HOST_INT32(settings.font_size))); fMessage.ReplaceBool("case_sensitive", settings.flags & kCaseSensitive); fMessage.ReplaceInt8("find_mode", settings.flags & kHexFindMode ? kHexMode : kAsciiMode); } } Settings::~Settings() { // only save the settings if something has changed if (!fUpdated) return; BFile file; if (Open(&file, B_CREATE_FILE | B_WRITE_ONLY) != B_OK) return; disk_probe_settings settings; settings.window_frame = fMessage.FindRect("window_frame"); #if B_HOST_IS_BENDIAN // settings are saved in little endian settings.window_frame.left = B_HOST_TO_LENDIAN_FLOAT( settings.window_frame.left); settings.window_frame.top = B_HOST_TO_LENDIAN_FLOAT( settings.window_frame.top); settings.window_frame.right = B_HOST_TO_LENDIAN_FLOAT( settings.window_frame.right); settings.window_frame.bottom = B_HOST_TO_LENDIAN_FLOAT( settings.window_frame.bottom); #endif settings.base_type = B_HOST_TO_LENDIAN_INT32( fMessage.FindInt32("base_type")); settings.font_size = B_HOST_TO_LENDIAN_INT32( int32(fMessage.FindFloat("font_size") + 0.5f)); settings.flags = B_HOST_TO_LENDIAN_INT32( (fMessage.FindBool("case_sensitive") ? kCaseSensitive : 0) | (fMessage.FindInt8("find_mode") == kHexMode ? kHexFindMode : 0)); file.Write(&settings, sizeof(settings)); } 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("DiskProbe_data"); return file->SetTo(path.Path(), mode); } void Settings::UpdateFrom(BMessage *message) { BRect frame; if (message->FindRect("window_frame", &frame) == B_OK) fMessage.ReplaceRect("window_frame", frame); int32 baseType; if (message->FindInt32("base_type", &baseType) == B_OK) fMessage.ReplaceInt32("base_type", baseType); float fontSize; if (message->FindFloat("font_size", &fontSize) == B_OK) fMessage.ReplaceFloat("font_size", fontSize); bool caseSensitive; if (message->FindBool("case_sensitive", &caseSensitive) == B_OK) fMessage.ReplaceBool("case_sensitive", caseSensitive); int8 findMode; if (message->FindInt8("find_mode", &findMode) == B_OK) fMessage.ReplaceInt8("find_mode", findMode); fUpdated = true; } // #pragma mark - DiskProbe::DiskProbe() : BApplication(kSignature), fOpenWindow(NULL), fFindWindow(NULL), fWindowCount(0) { fFilePanel = new BFilePanel(); fWindowFrame = fSettings.Message().FindRect("window_frame"); } DiskProbe::~DiskProbe() { delete fFilePanel; } void DiskProbe::ReadyToRun() { // are there already windows open? if (CountWindows() != 1) return; // if not, ask the user to open a file PostMessage(kMsgOpenOpenWindow); } /** Opens a window containing the file pointed to by the entry_ref. * This function will fail if that file doesn't exist or could not * be opened. * It will check if there already is a window that probes the * file in question and will activate it in that case. * This function must be called with the application looper locked. */ status_t DiskProbe::Probe(BEntry &entry, const char *attribute) { entry_ref ref; status_t status = entry.GetRef(&ref); if (status < B_OK) return status; ProbeWindow *lastWindow(NULL); // Do we already have that window open? for (int32 i = CountWindows(); i-- > 0; ) { ProbeWindow *window = dynamic_cast(WindowAt(i)); if (window == NULL) continue; if (window->Contains(ref, attribute)) { window->Activate(true); return B_OK; } if (lastWindow == NULL) lastWindow = window; } // Does the file really exist? if (!entry.Exists()) return B_ENTRY_NOT_FOUND; entry.GetRef(&ref); // cascade window BRect rect; if (lastWindow != NULL) rect = lastWindow->Frame(); else rect = fWindowFrame; rect.OffsetBy(kCascadeOffset, kCascadeOffset); BWindow *window; if (attribute != NULL) window = new AttributeWindow(rect, &ref, attribute, &fSettings.Message()); else window = new FileWindow(rect, &ref, &fSettings.Message()); window->Show(); /* adjust the cascading... we can only do this after the window was created * to adjust to the real size */ rect.right = window->Frame().right; rect.bottom = window->Frame().bottom; BScreen screen; BRect screenBorder = screen.Frame(); float left = rect.left; if (left + rect.Width() > screenBorder.right) left = 7; float top = rect.top; if (top + rect.Height() > screenBorder.bottom) top = 26; rect.OffsetTo(BPoint(left, top)); window->MoveTo(BPoint(left, top)); fWindowCount++; return B_OK; } void DiskProbe::RefsReceived(BMessage *message) { bool traverseLinks = (modifiers() & B_SHIFT_KEY) == 0; int32 index = 0; entry_ref ref; while (message->FindRef("refs", index++, &ref) == B_OK) { const char *attribute = NULL; if (message->FindString("attributes", index - 1, &attribute) == B_OK) traverseLinks = false; BEntry entry; status_t status = entry.SetTo(&ref, traverseLinks); if (status == B_OK) status = Probe(entry, attribute); if (status != B_OK) { char buffer[1024]; snprintf(buffer, sizeof(buffer), B_TRANSLATE_COMMENT("Could not open \"%s\":\n" "%s", "Opening of entry reference buffer for a DiskProbe " "request Alert message. The name of entry reference and " "error message is shown."), ref.name, strerror(status)); BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), buffer, B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); } } } void DiskProbe::ArgvReceived(int32 argc, char **argv) { BMessage *message = CurrentMessage(); BDirectory currentDirectory; if (message) 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, B_TRANSLATE("Could not open file \"%s\": %s\n"), path.Path(), strerror(status)); continue; } refs.AddRef("refs", &ref); } RefsReceived(&refs); } void DiskProbe::MessageReceived(BMessage *message) { switch (message->what) { case kMsgOpenOpenWindow: if (fOpenWindow == NULL) { fOpenWindow = new OpenWindow(); fOpenWindow->Show(); fWindowCount++; } else fOpenWindow->Activate(true); break; case kMsgOpenWindowClosed: fOpenWindow = NULL; // supposed to fall through case kMsgWindowClosed: if (--fWindowCount == 0 && !fFilePanel->IsShowing()) PostMessage(B_QUIT_REQUESTED); break; case kMsgSettingsChanged: fSettings.UpdateFrom(message); break; case kMsgFindWindowClosed: fFindWindow = NULL; break; case kMsgFindTarget: { BMessenger target; if (message->FindMessenger("target", &target) != B_OK) break; if (fFindWindow != NULL && fFindWindow->Lock()) { fFindWindow->SetTarget(target); fFindWindow->Unlock(); } break; } case kMsgOpenFindWindow: { BMessenger target; if (message->FindMessenger("target", &target) != B_OK) break; if (fFindWindow == NULL) { // open it! fFindWindow = new FindWindow(fWindowFrame.OffsetByCopy(80, 80), *message, target, &fSettings.Message()); fFindWindow->Show(); } else fFindWindow->Activate(); break; } case kMsgOpenFilePanel: fFilePanel->Show(); break; case B_CANCEL: if (fWindowCount == 0) PostMessage(B_QUIT_REQUESTED); break; default: BApplication::MessageReceived(message); break; } } bool DiskProbe::QuitRequested() { return true; } // #pragma mark - int main(int argc, char **argv) { DiskProbe probe; probe.Run(); return 0; }