/*
* Copyright 2004-2018, Axel Dörfler, axeld@pinc-software.de.
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "DiskProbe.h"
#include <stdio.h>
#include <string.h>
#include <AboutWindow.h>
#include <Alert.h>
#include <Application.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Directory.h>
#include <Entry.h>
#include <FilePanel.h>
#include <FindDirectory.h>
#include <LayoutUtils.h>
#include <Locale.h>
#include <Path.h>
#include <Screen.h>
#include <TextView.h>
#include "DataEditor.h"
#include "DataView.h"
#include "FileWindow.h"
#include "AttributeWindow.h"
#include "OpenWindow.h"
#include "FindWindow.h"
#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);
private:
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);
private:
Settings fSettings;
BFilePanel* fFilePanel;
BWindow* fOpenWindow;
FindWindow* fFindWindow;
uint32 fWindowCount;
BRect fWindowFrame;
BMessenger fFindTarget;
};
//-----------------
Settings::Settings()
:
fMessage(kMsgDiskProbeSettings),
fUpdated(false)
{
float fontSize = be_plain_font->Size();
int32 windowWidth = DataView::WidthForFontSize(fontSize) + 20;
// TODO: make scrollbar width variable
BScreen screen;
fMessage.AddRect("window_frame", BLayoutUtils::AlignInFrame(screen.Frame(),
BSize(windowWidth, windowWidth),
BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)));
fMessage.AddInt32("base_type", kHexBase);
fMessage.AddFloat("font_size", fontSize);
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<ProbeWindow*>(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;
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.