/*
* Copyright 2004-2018, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "ProbeView.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <Alert.h>
#include <Application.h>
#include <Autolock.h>
#include <Beep.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <Clipboard.h>
#include <Directory.h>
#include <Entry.h>
#include <ExpressionParser.h>
#include <fs_attr.h>
#include <GridView.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <GroupView.h>
#include <Locale.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <MessageQueue.h>
#include <NodeInfo.h>
#include <Node.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PrintJob.h>
#include <ScrollView.h>
#include <StringView.h>
#include <Slider.h>
#include <String.h>
#include <TextControl.h>
#include <Volume.h>
#include <Window.h>
#include "DataView.h"
#include "DiskProbe.h"
#include "TypeEditors.h"
#ifndef __HAIKU__
# define DRAW_SLIDER_BAR
// if this is defined, the standard slider bar is replaced with
// one that looks exactly like the one in the original DiskProbe
// (even in Dano/Zeta)
#endif
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ProbeView"
static const uint32 kMsgSliderUpdate = 'slup';
static const uint32 kMsgPositionUpdate = 'poup';
static const uint32 kMsgLastPosition = 'lpos';
static const uint32 kMsgFontSize = 'fnts';
static const uint32 kMsgBlockSize = 'blks';
static const uint32 kMsgAddBookmark = 'bmrk';
static const uint32 kMsgPrint = 'prnt';
static const uint32 kMsgPageSetup = 'pgsp';
static const uint32 kMsgViewAs = 'vwas';
static const uint32 kMsgStopFind = 'sfnd';
class IconView : public BView {
public:
IconView(const entry_ref* ref, bool isDevice);
virtual ~IconView();
virtual void AttachedToWindow();
virtual void Draw(BRect updateRect);
void UpdateIcon();
private:
entry_ref fRef;
bool fIsDevice;
BBitmap* fBitmap;
};
class PositionSlider : public BSlider {
public:
PositionSlider(const char* name,
BMessage* message, off_t size,
uint32 blockSize);
virtual ~PositionSlider();
#ifdef DRAW_SLIDER_BAR
virtual void DrawBar();
#endif
off_t Position() const;
off_t Size() const { return fSize; }
uint32 BlockSize() const { return fBlockSize; }
virtual void SetPosition(float position);
void SetPosition(off_t position);
void SetSize(off_t size);
void SetBlockSize(uint32 blockSize);
private:
void Reset();
private:
static const int32 kMaxSliderLimit = 0x7fffff80;
// this is the maximum value that BSlider seem to work with fine
off_t fSize;
uint32 fBlockSize;
};
class HeaderView : public BGridView, public BInvoker {
public:
HeaderView(const entry_ref* ref,
DataEditor& editor);
virtual ~HeaderView();
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void MessageReceived(BMessage* message);
base_type Base() const { return fBase; }
void SetBase(base_type);
off_t CursorOffset() const
{ return fPosition % fBlockSize; }
off_t Position() const { return fPosition; }
uint32 BlockSize() const { return fBlockSize; }
void SetTo(off_t position, uint32 blockSize);
void UpdateIcon();
private:
void FormatValue(char* buffer, size_t bufferSize,
off_t value);
void UpdatePositionViews(bool all = true);
void UpdateOffsetViews(bool all = true);
void UpdateFileSizeView();
void NotifyTarget();
private:
const char* fAttribute;
off_t fFileSize;
uint32 fBlockSize;
base_type fBase;
off_t fPosition;
off_t fLastPosition;
BTextControl* fTypeControl;
BTextControl* fPositionControl;
BStringView* fPathView;
BStringView* fSizeView;
BTextControl* fOffsetControl;
BTextControl* fFileOffsetControl;
PositionSlider* fPositionSlider;
IconView* fIconView;
BButton* fStopButton;
};
class TypeMenuItem : public BMenuItem {
public:
TypeMenuItem(const char* name, const char* type,
BMessage* message);
virtual void GetContentSize(float* _width, float* _height);
virtual void DrawContent();
private:
BString fType;
};
class EditorLooper : public BLooper {
public:
EditorLooper(const char* name,
DataEditor& editor, BMessenger messenger);
virtual ~EditorLooper();
virtual void MessageReceived(BMessage* message);
bool FindIsRunning() const { return !fQuitFind; }
void Find(off_t startAt, const uint8* data,
size_t dataSize, bool caseInsensitive,
BMessenger progressMonitor);
void QuitFind();
private:
DataEditor& fEditor;
BMessenger fMessenger;
volatile bool fQuitFind;
};
class TypeView : public BView {
public:
TypeView(BRect rect, const char* name,
int32 index, DataEditor& editor,
int32 resizingMode);
virtual ~TypeView();
virtual void FrameResized(float width, float height);
private:
BView* fTypeEditorView;
};
// #pragma mark - utility functions
static void
get_type_string(char* buffer, size_t bufferSize, type_code type)
{
for (int32 i = 0; i < 4; i++) {
buffer[i] = type >> (24 - 8 * i);
if (buffer[i] < ' ' || buffer[i] == 0x7f) {
snprintf(buffer, bufferSize, "0x%04" B_PRIx32, type);
break;
} else if (i == 3)
buffer[4] = '\0';
}
}
// #pragma mark - IconView
IconView::IconView(const entry_ref* ref, bool isDevice)
: BView(NULL, B_WILL_DRAW),
fRef(*ref),
fIsDevice(isDevice),
fBitmap(NULL)
{
UpdateIcon();
SetExplicitSize(BSize(32, 32));
}
IconView::~IconView()
{
delete fBitmap;
}
void
IconView::AttachedToWindow()
{
if (Parent() != NULL)
SetViewColor(Parent()->ViewColor());
else
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}
void
IconView::Draw(BRect updateRect)
{
if (fBitmap == NULL)
return;
SetDrawingMode(B_OP_ALPHA);
DrawBitmap(fBitmap, updateRect, updateRect);
SetDrawingMode(B_OP_COPY);
}
void
IconView::UpdateIcon()
{
if (fBitmap == NULL)
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
fBitmap = new BBitmap(BRect(0, 0, 31, 31), B_RGBA32);
#else
fBitmap = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
#endif
if (fBitmap != NULL) {
status_t status = B_ERROR;
if (fIsDevice) {
BPath path(&fRef);
#ifdef __HAIKU__
status = get_device_icon(path.Path(), fBitmap, B_LARGE_ICON);
#else
status = get_device_icon(path.Path(), fBitmap->Bits(), B_LARGE_ICON);
#endif
} else
status = BNodeInfo::GetTrackerIcon(&fRef, fBitmap);
if (status != B_OK) {
// Try to get generic icon
BMimeType type(B_FILE_MIME_TYPE);
status = type.GetIcon(fBitmap, B_LARGE_ICON);
}
if (status != B_OK) {
delete fBitmap;
fBitmap = NULL;
}
Invalidate();
}
}
// #pragma mark - PositionSlider
PositionSlider::PositionSlider(const char* name, BMessage* message,
off_t size, uint32 blockSize)
:
BSlider(name, NULL, message, 0, kMaxSliderLimit, B_HORIZONTAL,
B_TRIANGLE_THUMB),
fSize(size),
fBlockSize(blockSize)
{
Reset();
#ifndef DRAW_SLIDER_BAR
rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
UseFillColor(true, &color);
#endif
}
PositionSlider::~PositionSlider()
{
}
#ifdef DRAW_SLIDER_BAR
void
PositionSlider::DrawBar()
{
BView* view = OffscreenView();
BRect barFrame = BarFrame();
BRect frame = barFrame.InsetByCopy(1, 1);
frame.top++;
frame.left++;
frame.right = ThumbFrame().left + ThumbFrame().Width() / 2;
#ifdef HAIKU_TARGET_PLATFORM_BEOS
if (IsEnabled())
view->SetHighColor(102, 152, 203);
else
view->SetHighColor(92, 102, 160);
#else
view->SetHighColor(IsEnabled() ? ui_color(B_CONTROL_HIGHLIGHT_COLOR)
: tint_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR), B_DARKEN_1_TINT));
#endif
view->FillRect(frame);
frame.left = frame.right + 1;
frame.right = barFrame.right - 1;
view->SetHighColor(tint_color(ViewColor(), IsEnabled() ? B_DARKEN_1_TINT : B_LIGHTEN_1_TINT));
view->FillRect(frame);
rgb_color cornerColor = tint_color(ViewColor(), B_DARKEN_1_TINT);
rgb_color darkColor = tint_color(ViewColor(), B_DARKEN_3_TINT);
#ifdef HAIKU_TARGET_PLATFORM_BEOS
rgb_color shineColor = {255, 255, 255};
rgb_color shadowColor = {0, 0, 0};
#else
rgb_color shineColor = ui_color(B_SHINE_COLOR);
rgb_color shadowColor = ui_color(B_SHADOW_COLOR);
#endif
if (!IsEnabled()) {
darkColor = tint_color(ViewColor(), B_DARKEN_1_TINT);
shineColor = tint_color(shineColor, B_DARKEN_2_TINT);
shadowColor = tint_color(shadowColor, B_LIGHTEN_2_TINT);
}
view->BeginLineArray(9);
// the corners
view->AddLine(barFrame.LeftTop(), barFrame.LeftTop(), cornerColor);
view->AddLine(barFrame.LeftBottom(), barFrame.LeftBottom(), cornerColor);
view->AddLine(barFrame.RightTop(), barFrame.RightTop(), cornerColor);
// the edges
view->AddLine(BPoint(barFrame.left, barFrame.top + 1),
BPoint(barFrame.left, barFrame.bottom - 1), darkColor);
view->AddLine(BPoint(barFrame.right, barFrame.top + 1),
BPoint(barFrame.right, barFrame.bottom), shineColor);
barFrame.left++;
barFrame.right--;
view->AddLine(barFrame.LeftTop(), barFrame.RightTop(), darkColor);
view->AddLine(barFrame.LeftBottom(), barFrame.RightBottom(), shineColor);
// the inner edges
barFrame.top++;
view->AddLine(barFrame.LeftTop(), barFrame.RightTop(), shadowColor);
view->AddLine(BPoint(barFrame.left, barFrame.top + 1),
BPoint(barFrame.left, barFrame.bottom - 1), shadowColor);
view->EndLineArray();
}
#endif // DRAW_SLIDER_BAR
void
PositionSlider::Reset()
{
SetKeyIncrementValue(int32(1.0 * kMaxSliderLimit / ((fSize - 1) / fBlockSize) + 0.5));
SetEnabled(fSize > fBlockSize);
}
off_t
PositionSlider::Position() const
{
// ToDo:
// Note: this code is far from being perfect: depending on the file size, it has
// a maxium granularity that might be less than the actual block size demands...
// The only way to work around this that I can think of, is to replace the slider
// class completely with one that understands off_t values.
// For example, with a block size of 512 bytes, it should be good enough for about
// 1024 GB - and that's not really that far away these days.
return (off_t(1.0 * (fSize - 1) * Value() / kMaxSliderLimit + 0.5) / fBlockSize) * fBlockSize;
}
void
PositionSlider::SetPosition(float position)
{
BSlider::SetPosition(position);
}
void
PositionSlider::SetPosition(off_t position)
{
position /= fBlockSize;
SetValue(int32(1.0 * kMaxSliderLimit * position / ((fSize - 1) / fBlockSize) + 0.5));
}
void
PositionSlider::SetSize(off_t size)
{
if (size == fSize)
return;
off_t position = Position();
if (position >= size)
position = size - 1;
fSize = size;
Reset();
SetPosition(position);
}
void
PositionSlider::SetBlockSize(uint32 blockSize)
{
if (blockSize == fBlockSize)
return;
off_t position = Position();
fBlockSize = blockSize;
Reset();
SetPosition(position);
}
// #pragma mark - HeaderView
HeaderView::HeaderView(const entry_ref* ref, DataEditor& editor)
: BGridView("probeHeader", B_USE_SMALL_SPACING, B_USE_SMALL_SPACING),
fAttribute(editor.Attribute()),
fFileSize(editor.FileSize()),
fBlockSize(editor.BlockSize()),
fBase(kHexBase),
fPosition(0),
fLastPosition(0)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
GridLayout()->SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
fIconView = new IconView(ref, editor.IsDevice());
GridLayout()->AddView(fIconView, 0, 0, 1, 2);
BGroupView* line = new BGroupView(B_HORIZONTAL);
GridLayout()->AddView(line, 1, 0);
BFont boldFont = *be_bold_font;
BFont plainFont = *be_plain_font;
boldFont.SetSize(plainFont.Size() * 0.83);
plainFont.SetSize(plainFont.Size() * 0.83);
BStringView* stringView = new BStringView(
B_EMPTY_STRING, editor.IsAttribute()
? B_TRANSLATE("Attribute: ") : editor.IsDevice()
? B_TRANSLATE("Device: ") : B_TRANSLATE("File: "));
stringView->SetFont(&boldFont);
line->AddChild(stringView);
BPath path(ref);
BString string = path.Path();
if (fAttribute != NULL) {
string.Prepend(" (");
string.Prepend(fAttribute);
string.Append(")");
}
fPathView = new BStringView(B_EMPTY_STRING, string.String());
fPathView->SetFont(&plainFont);
line->AddChild(fPathView);
if (editor.IsAttribute()) {
stringView = new BStringView(B_EMPTY_STRING,
B_TRANSLATE("Attribute type: "));
stringView->SetFont(&boldFont);
line->AddChild(stringView);
char buffer[16];
get_type_string(buffer, sizeof(buffer), editor.Type());
fTypeControl = new BTextControl(B_EMPTY_STRING, NULL, buffer,
new BMessage(kMsgPositionUpdate));
fTypeControl->SetFont(&plainFont);
fTypeControl->TextView()->SetFontAndColor(&plainFont);
fTypeControl->SetEnabled(false);
// ToDo: for now
line->AddChild(fTypeControl);
} else
fTypeControl = NULL;
fStopButton = new BButton(B_EMPTY_STRING,
B_TRANSLATE("Stop"), new BMessage(kMsgStopFind));
fStopButton->SetFont(&plainFont);
fStopButton->Hide();
line->AddChild(fStopButton);
BGroupLayoutBuilder(line).AddGlue();
line = new BGroupView(B_HORIZONTAL, B_USE_SMALL_SPACING);
GridLayout()->AddView(line, 1, 1);
stringView = new BStringView(B_EMPTY_STRING, B_TRANSLATE("Block: "));
stringView->SetFont(&boldFont);
line->AddChild(stringView);
BMessage* msg = new BMessage(kMsgPositionUpdate);
msg->AddBool("fPositionControl", true);
// BTextControl oddities
fPositionControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg);
fPositionControl->SetDivider(0.0);
fPositionControl->SetFont(&plainFont);
fPositionControl->TextView()->SetFontAndColor(&plainFont);
fPositionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT);
line->AddChild(fPositionControl);
fSizeView = new BStringView(B_EMPTY_STRING, B_TRANSLATE_COMMENT("of "
"0x0", "This is a part of 'Block 0xXXXX of 0x0026' message. In "
"languages without 'of' structure it can be replaced simply "
"with '/'."));
fSizeView->SetFont(&plainFont);
line->AddChild(fSizeView);
UpdateFileSizeView();
stringView = new BStringView(B_EMPTY_STRING, B_TRANSLATE("Offset: "));
stringView->SetFont(&boldFont);
line->AddChild(stringView);
msg = new BMessage(kMsgPositionUpdate);
msg->AddBool("fOffsetControl", false);
fOffsetControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg);
fOffsetControl->SetDivider(0.0);
fOffsetControl->SetFont(&plainFont);
fOffsetControl->TextView()->SetFontAndColor(&plainFont);
fOffsetControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT);
line->AddChild(fOffsetControl);
UpdateOffsetViews(false);
stringView = new BStringView(B_EMPTY_STRING, editor.IsAttribute()
? B_TRANSLATE("Attribute offset: ") : editor.IsDevice()
? B_TRANSLATE("Device offset: ") : B_TRANSLATE("File offset: "));
stringView->SetFont(&boldFont);
line->AddChild(stringView);
msg = new BMessage(kMsgPositionUpdate);
msg->AddBool("fFileOffsetControl", false);
fFileOffsetControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg);
fFileOffsetControl->SetDivider(0.0);
fFileOffsetControl->SetFont(&plainFont);
fFileOffsetControl->TextView()->SetFontAndColor(&plainFont);
fFileOffsetControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT);
line->AddChild(fFileOffsetControl);
BGroupLayoutBuilder(line).AddGlue();
fPositionSlider = new PositionSlider("slider",
new BMessage(kMsgSliderUpdate), editor.FileSize(), editor.BlockSize());
fPositionSlider->SetModificationMessage(new BMessage(kMsgSliderUpdate));
fPositionSlider->SetBarThickness(8);
GridLayout()->AddView(fPositionSlider, 0, 2, 2, 1);
}
HeaderView::~HeaderView()
{
}
void
HeaderView::AttachedToWindow()
{
SetTarget(Window());
fStopButton->SetTarget(Parent());
fPositionControl->SetTarget(this);
fOffsetControl->SetTarget(this);
fFileOffsetControl->SetTarget(this);
fPositionSlider->SetTarget(this);
BMessage* message;
Window()->AddShortcut(B_HOME, B_COMMAND_KEY,
message = new BMessage(kMsgPositionUpdate), this);
message->AddInt64("block", 0);
Window()->AddShortcut(B_END, B_COMMAND_KEY,
message = new BMessage(kMsgPositionUpdate), this);
message->AddInt64("block", -1);
Window()->AddShortcut(B_PAGE_UP, B_COMMAND_KEY,
message = new BMessage(kMsgPositionUpdate), this);
message->AddInt32("delta", -1);
Window()->AddShortcut(B_PAGE_DOWN, B_COMMAND_KEY,
message = new BMessage(kMsgPositionUpdate), this);
message->AddInt32("delta", 1);
}
void
HeaderView::DetachedFromWindow()
{
Window()->RemoveShortcut(B_HOME, B_COMMAND_KEY);
Window()->RemoveShortcut(B_END, B_COMMAND_KEY);
Window()->RemoveShortcut(B_PAGE_UP, B_COMMAND_KEY);
Window()->RemoveShortcut(B_PAGE_DOWN, B_COMMAND_KEY);
}
void
HeaderView::UpdateIcon()
{
fIconView->UpdateIcon();
}
void
HeaderView::FormatValue(char* buffer, size_t bufferSize, off_t value)
{
snprintf(buffer, bufferSize, fBase == kHexBase ? "0x%" B_PRIxOFF : "%"
B_PRIdOFF, value);
}
void
HeaderView::UpdatePositionViews(bool all)
{
char buffer[64];
FormatValue(buffer, sizeof(buffer), fPosition / fBlockSize);
fPositionControl->SetText(buffer);
if (all) {
FormatValue(buffer, sizeof(buffer), fPosition);
fFileOffsetControl->SetText(buffer);
}
}
void
HeaderView::UpdateOffsetViews(bool all)
{
char buffer[64];
FormatValue(buffer, sizeof(buffer), fPosition % fBlockSize);
fOffsetControl->SetText(buffer);
if (all) {
FormatValue(buffer, sizeof(buffer), fPosition);
fFileOffsetControl->SetText(buffer);
}
}
void
HeaderView::UpdateFileSizeView()
{
BString string(B_TRANSLATE("of "));
char buffer[64];
FormatValue(buffer, sizeof(buffer),
(fFileSize + fBlockSize - 1) / fBlockSize);
string << buffer;
fSizeView->SetText(string.String());
}
void
HeaderView::SetBase(base_type type)
{
if (fBase == type)
return;
fBase = type;
UpdatePositionViews();
UpdateOffsetViews(false);
UpdateFileSizeView();
}
void
HeaderView::SetTo(off_t position, uint32 blockSize)
{
fPosition = position;
fLastPosition = (fLastPosition / fBlockSize) * blockSize;
fBlockSize = blockSize;
fPositionSlider->SetBlockSize(blockSize);
UpdatePositionViews();
UpdateOffsetViews(false);
UpdateFileSizeView();
}
void
HeaderView::NotifyTarget()
{
BMessage update(kMsgPositionUpdate);
update.AddInt64("position", fPosition);
Messenger().SendMessage(&update);
}
void
HeaderView::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_OBSERVER_NOTICE_CHANGE: {
int32 what;
if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
break;
switch (what) {
case kDataViewCursorPosition:
off_t offset;
if (message->FindInt64("position", &offset) == B_OK) {
fPosition = (fPosition / fBlockSize) * fBlockSize
+ offset;
UpdateOffsetViews();
}
break;
}
break;
}
case kMsgSliderUpdate:
{
// First, make sure we're only considering the most
// up-to-date message in the queue (which might not
// be this one).
// If there is another message of this type in the
// queue, we're just ignoring the current message.
if (Looper()->MessageQueue()->FindMessage(kMsgSliderUpdate, 0)
!= NULL)
break;
// if nothing has changed, we can ignore this message as well
if (fPosition == fPositionSlider->Position())
break;
fLastPosition = fPosition;
fPosition = fPositionSlider->Position();
// update position text control
UpdatePositionViews();
// notify our target
NotifyTarget();
break;
}
case kMsgDataEditorFindProgress:
{
bool state;
if (message->FindBool("running", &state) == B_OK
&& fFileSize > fBlockSize) {
fPositionSlider->SetEnabled(!state);
if (state) {
fStopButton->Show();
} else {
fStopButton->Hide();
}
}
off_t position;
if (message->FindInt64("position", &position) != B_OK)
break;
fPosition = (position / fBlockSize) * fBlockSize;
// round to block size
// update views
UpdatePositionViews(false);
fPositionSlider->SetPosition(fPosition);
break;
}
case kMsgPositionUpdate:
{
off_t lastPosition = fPosition;
off_t position;
int32 delta;
bool round = true;
if (message->FindInt64("position", &position) == B_OK)
fPosition = position;
else if (message->FindInt64("block", &position) == B_OK) {
if (position < 0)
position += (fFileSize - 1) / fBlockSize + 1;
fPosition = position * fBlockSize;
} else if (message->FindInt32("delta", &delta) == B_OK) {
fPosition += delta * off_t(fBlockSize);
} else {
try {
ExpressionParser parser;
parser.SetSupportHexInput(true);
if (message->FindBool("fPositionControl", &round)
== B_OK) {
fPosition = parser.EvaluateToInt64(
fPositionControl->Text()) * fBlockSize;
} else if (message->FindBool("fOffsetControl", &round)
== B_OK) {
fPosition = (fPosition / fBlockSize) * fBlockSize +
parser.EvaluateToInt64(fOffsetControl->Text());
} else if (message->FindBool("fFileOffsetControl", &round)
== B_OK) {
fPosition = parser.EvaluateToInt64(
fFileOffsetControl->Text());
}
} catch (...) {
beep();
break;
}
}
fLastPosition = lastPosition;
if (round)
fPosition = (fPosition / fBlockSize) * fBlockSize;
// round to block size
if (fPosition < 0)
fPosition = 0;
else if (fPosition > ((fFileSize - 1) / fBlockSize) * fBlockSize)
fPosition = ((fFileSize - 1) / fBlockSize) * fBlockSize;
// update views
UpdatePositionViews();
fPositionSlider->SetPosition(fPosition);
// notify our target
NotifyTarget();
break;
}
case kMsgLastPosition:
{
fPosition = fLastPosition;
fLastPosition = fPositionSlider->Position();
// update views
UpdatePositionViews();
fPositionSlider->SetPosition(fPosition);
// notify our target
NotifyTarget();
break;
}
case kMsgBaseType:
{
int32 type;
if (message->FindInt32("base", &type) != B_OK)
break;
SetBase((base_type)type);
break;
}
default:
BView::MessageReceived(message);
}
}
// #pragma mark - TypeMenuItem
/*! The TypeMenuItem is a BMenuItem that displays a type string at its
right border.
It is used to display the attribute and type in the attributes menu.
It does not mix nicely with short cuts.
*/
TypeMenuItem::TypeMenuItem(const char* name, const char* type,
BMessage* message)
:
BMenuItem(name, message),
fType(type)
{
}
void
TypeMenuItem::GetContentSize(float* _width, float* _height)
{
BMenuItem::GetContentSize(_width, _height);
if (_width)
*_width += Menu()->StringWidth(fType.String());
}
void
TypeMenuItem::DrawContent()
{
// draw the label
BMenuItem::DrawContent();
font_height fontHeight;
Menu()->GetFontHeight(&fontHeight);
// draw the type
BPoint point = ContentLocation();
point.x = Frame().right - 4 - Menu()->StringWidth(fType.String());
point.y += fontHeight.ascent;
#ifdef HAIKU_TARGET_PLATFORM_BEOS
Menu()->SetDrawingMode(B_OP_ALPHA);
#endif
Menu()->DrawString(fType.String(), point);
}
// #pragma mark - EditorLooper
/*! The purpose of this looper is to off-load the editor data loading from
the main window looper.
It will listen to the offset changes of the editor, let him update its
data, and will then synchronously notify the target.
That way, simple offset changes will not stop the main looper from
operating. Therefore, all offset updates for the editor will go through
this looper.
Also, it will run the find action in the editor.
*/
EditorLooper::EditorLooper(const char* name, DataEditor& editor,
BMessenger target)
: BLooper(name),
fEditor(editor),
fMessenger(target),
fQuitFind(true)
{
fEditor.StartWatching(this);
}
EditorLooper::~EditorLooper()
{
fEditor.StopWatching(this);
}
void
EditorLooper::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgPositionUpdate:
{
// First, make sure we're only considering the most
// up-to-date message in the queue (which might not
// be this one).
// If there is another message of this type in the
// queue, we're just ignoring the current message.
if (Looper()->MessageQueue()->FindMessage(kMsgPositionUpdate, 0) != NULL)
break;
off_t position;
if (message->FindInt64("position", &position) == B_OK) {
BAutolock locker(fEditor);
fEditor.SetViewOffset(position);
BMessage message(kMsgSetSelection);
message.AddInt64("start", position - fEditor.ViewOffset());
message.AddInt64("end", position - fEditor.ViewOffset());
fMessenger.SendMessage(&message);
}
break;
}
case kMsgDataEditorParameterChange:
{
bool updated = false;
if (fEditor.Lock()) {
fEditor.UpdateIfNeeded(&updated);
fEditor.Unlock();
}
if (updated) {
BMessage reply;
fMessenger.SendMessage(kMsgUpdateData, &reply);
// We are doing a synchronously transfer, to prevent
// that we're already locking the editor again when
// our target wants to get the editor data.
}
break;
}
case kMsgFind:
{
BMessenger progressMonitor;
message->FindMessenger("progress_monitor", &progressMonitor);
off_t startAt = 0;
message->FindInt64("start", &startAt);
bool caseInsensitive = !message->FindBool("case_sensitive");
ssize_t dataSize;
const uint8* data;
if (message->FindData("data", B_RAW_TYPE, (const void**)&data,
&dataSize) == B_OK)
Find(startAt, data, dataSize, caseInsensitive, progressMonitor);
}
default:
BLooper::MessageReceived(message);
break;
}
}
void
EditorLooper::Find(off_t startAt, const uint8* data, size_t dataSize,
bool caseInsensitive, BMessenger progressMonitor)
{
fQuitFind = false;
BAutolock locker(fEditor);
bigtime_t startTime = system_time();
off_t foundAt = fEditor.Find(startAt, data, dataSize, caseInsensitive,
true, progressMonitor, &fQuitFind);
if (foundAt >= B_OK) {
fEditor.SetViewOffset(foundAt);
// select the part in our target
BMessage message(kMsgSetSelection);
message.AddInt64("start", foundAt - fEditor.ViewOffset());
message.AddInt64("end", foundAt + dataSize - 1 - fEditor.ViewOffset());
fMessenger.SendMessage(&message);
} else if (foundAt == B_ENTRY_NOT_FOUND) {
if (system_time() > startTime + 8000000LL) {
// If the user had to wait more than 8 seconds for the result,
// we are trying to please him with a requester...
BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
B_TRANSLATE("Could not find search string."),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
} else
beep();
}
}
void
EditorLooper::QuitFind()
{
fQuitFind = true;
// this will cleanly stop the find process
}
// #pragma mark - TypeView
TypeView::TypeView(BRect rect, const char* name, int32 index,
DataEditor& editor, int32 resizingMode)
: BView(rect, name, resizingMode, B_FRAME_EVENTS)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fTypeEditorView = GetTypeEditorAt(index, Frame(), editor);
if (fTypeEditorView == NULL) {
AddChild(new BStringView(Bounds(), B_TRANSLATE("Type editor"),
B_TRANSLATE("Type editor not supported"), B_FOLLOW_NONE));
} else
AddChild(fTypeEditorView);
if ((fTypeEditorView->ResizingMode() & (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM))
!= 0) {
BRect rect = Bounds();
BRect frame = fTypeEditorView->Frame();
rect.left = frame.left;
rect.top = frame.top;
if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
rect.right = frame.right;
if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
rect.bottom = frame.bottom;
fTypeEditorView->ResizeTo(rect.Width(), rect.Height());
}
}
TypeView::~TypeView()
{
}
void
TypeView::FrameResized(float width, float height)
{
BRect rect = Bounds();
BPoint point = fTypeEditorView->Frame().LeftTop();
if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
point.x = (rect.Width() - fTypeEditorView->Bounds().Width()) / 2;
if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
point.y = (rect.Height() - fTypeEditorView->Bounds().Height()) / 2;
fTypeEditorView->MoveTo(point);
}
// #pragma mark - ProbeView
ProbeView::ProbeView(entry_ref* ref, const char* attribute,
const BMessage* settings)
: BView("probeView", B_WILL_DRAW),
fPrintSettings(NULL),
fTypeView(NULL),
fLastSearch(NULL)
{
BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0);
SetLayout(layout);
layout->SetInsets(-1, -1, -1, -1);
fEditor.SetTo(*ref, attribute);
int32 baseType = kHexBase;
float fontSize = 12.0f;
if (settings != NULL) {
settings->FindInt32("base_type", &baseType);
settings->FindFloat("font_size", &fontSize);
}
fHeaderView = new HeaderView(&fEditor.Ref(), fEditor);
fHeaderView->SetBase((base_type)baseType);
AddChild(fHeaderView);
fDataView = new DataView(fEditor);
fDataView->SetBase((base_type)baseType);
fDataView->SetFontSize(fontSize);
fScrollView = new BScrollView("scroller", fDataView, B_WILL_DRAW, true,
true);
AddChild(fScrollView);
fDataView->UpdateScroller();
}
ProbeView::~ProbeView()
{
}
void
ProbeView::DetachedFromWindow()
{
fEditorLooper->QuitFind();
if (fEditorLooper->Lock())
fEditorLooper->Quit();
fEditorLooper = NULL;
fEditor.StopWatching(this);
fDataView->StopWatching(fHeaderView, kDataViewCursorPosition);
fDataView->StopWatching(this, kDataViewSelection);
fDataView->StopWatching(this, kDataViewPreferredSize);
be_clipboard->StopWatching(this);
}
void
ProbeView::_UpdateAttributesMenu(BMenu* menu)
{
// remove old contents
for (int32 i = menu->CountItems(); i-- > 0;) {
delete menu->RemoveItem(i);
}
// add new items (sorted)
BNode node(&fEditor.AttributeRef());
if (node.InitCheck() == B_OK) {
char attribute[B_ATTR_NAME_LENGTH];
node.RewindAttrs();
while (node.GetNextAttrName(attribute) == B_OK) {
attr_info info;
if (node.GetAttrInfo(attribute, &info) != B_OK)
continue;
char type[16];
type[0] = '[';
get_type_string(type + 1, sizeof(type) - 2, info.type);
strcat(type, "]");
// find where to insert
int32 i;
for (i = 0; i < menu->CountItems(); i++) {
if (strcasecmp(menu->ItemAt(i)->Label(), attribute) > 0)
break;
}
BMessage* message = new BMessage(B_REFS_RECEIVED);
message->AddRef("refs", &fEditor.AttributeRef());
message->AddString("attributes", attribute);
menu->AddItem(new TypeMenuItem(attribute, type, message), i);
}
}
if (menu->CountItems() == 0) {
// if there are no attributes, add an item to the menu
// that says so
BMenuItem* item = new BMenuItem(B_TRANSLATE_COMMENT("none",
"No attributes"), NULL);
item->SetEnabled(false);
menu->AddItem(item);
}
menu->SetTargetForItems(be_app);
}
void
ProbeView::AddSaveMenuItems(BMenu* menu, int32 index)
{
menu->AddItem(fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
new BMessage(B_SAVE_REQUESTED), 'S'), index);
fSaveMenuItem->SetTarget(this);
fSaveMenuItem->SetEnabled(false);
//menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, NULL), index);
}
void
ProbeView::AddPrintMenuItems(BMenu* menu, int32 index)
{
BMenuItem* item;
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
new BMessage(kMsgPageSetup)), index++);
item->SetTarget(this);
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
new BMessage(kMsgPrint), 'P'), index++);
item->SetTarget(this);
}
void
ProbeView::AddViewAsMenuItems()
{
#if 0
BMenuBar* bar = Window()->KeyMenuBar();
if (bar == NULL)
return;
BMenuItem* item = bar->FindItem(B_TRANSLATE("View"));
BMenu* menu = NULL;
if (item != NULL)
menu = item->Submenu();
else
menu = bar->SubmenuAt(bar->CountItems() - 1);
if (menu == NULL)
return;
menu->AddSeparatorItem();
BMenu* subMenu = new BMenu(B_TRANSLATE("View As"));
subMenu->SetRadioMode(true);
BMessage* message = new BMessage(kMsgViewAs);
subMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Raw"), message));
item->SetMarked(true);
const char* name;
for (int32 i = 0; GetNthTypeEditor(i, &name) == B_OK; i++) {
message = new BMessage(kMsgViewAs);
message->AddInt32("editor index", i);
subMenu->AddItem(new BMenuItem(name, message));
}
subMenu->SetTargetForItems(this);
menu->AddItem(new BMenuItem(subMenu));
#endif
}
void
ProbeView::AttachedToWindow()
{
BView::AttachedToWindow();
fEditorLooper = new EditorLooper(fEditor.Ref().name, fEditor,
BMessenger(fDataView));
fEditorLooper->Run();
fEditor.StartWatching(this);
fDataView->StartWatching(fHeaderView, kDataViewCursorPosition);
fDataView->StartWatching(this, kDataViewSelection);
fDataView->StartWatching(this, kDataViewPreferredSize);
be_clipboard->StartWatching(this);
// Add menu to window
BMenuBar* bar = Window()->KeyMenuBar();
if (bar == NULL) {
// there is none? Well, but we really want to have one
bar = new BMenuBar("");
Window()->AddChild(bar);
BMenu* menu = new BMenu(fEditor.IsAttribute()
? B_TRANSLATE("Attribute") : fEditor.IsDevice()
? B_TRANSLATE("Device") : B_TRANSLATE("File"));
AddSaveMenuItems(menu, 0);
menu->AddSeparatorItem();
AddPrintMenuItems(menu, menu->CountItems());
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
new BMessage(B_CLOSE_REQUESTED), 'W'));
bar->AddItem(menu);
}
// "Edit" menu
BMenu* menu = new BMenu(B_TRANSLATE("Edit"));
BMenuItem* item;
menu->AddItem(fUndoMenuItem = new BMenuItem(B_TRANSLATE("Undo"),
new BMessage(B_UNDO), 'Z'));
fUndoMenuItem->SetEnabled(fEditor.CanUndo());
fUndoMenuItem->SetTarget(fDataView);
menu->AddItem(fRedoMenuItem = new BMenuItem(B_TRANSLATE("Redo"),
new BMessage(B_REDO), 'Z', B_SHIFT_KEY));
fRedoMenuItem->SetEnabled(fEditor.CanRedo());
fRedoMenuItem->SetTarget(fDataView);
menu->AddSeparatorItem();
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy"),
new BMessage(B_COPY), 'C'));
item->SetTarget(NULL, Window());
menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"),
new BMessage(B_PASTE), 'V'));
fPasteMenuItem->SetTarget(NULL, Window());
_CheckClipboard();
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(B_SELECT_ALL), 'A'));
item->SetTarget(NULL, Window());
menu->AddSeparatorItem();
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
new BMessage(kMsgOpenFindWindow), 'F'));
item->SetTarget(this);
menu->AddItem(fFindAgainMenuItem = new BMenuItem(B_TRANSLATE("Find again"),
new BMessage(kMsgFind), 'G'));
fFindAgainMenuItem->SetEnabled(false);
fFindAgainMenuItem->SetTarget(this);
bar->AddItem(menu);
// "Block" menu
menu = new BMenu(B_TRANSLATE("Block"));
BMessage* message = new BMessage(kMsgPositionUpdate);
message->AddInt32("delta", 1);
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Next"), message,
B_RIGHT_ARROW));
item->SetTarget(fHeaderView);
message = new BMessage(kMsgPositionUpdate);
message->AddInt32("delta", -1);
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Previous"), message,
B_LEFT_ARROW));
item->SetTarget(fHeaderView);
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Back"),
new BMessage(kMsgLastPosition), 'J'));
item->SetTarget(fHeaderView);
BMenu* subMenu = new BMenu(B_TRANSLATE("Selection"));
message = new BMessage(kMsgPositionUpdate);
message->AddInt64("block", 0);
subMenu->AddItem(fNativeMenuItem = new BMenuItem("", message, 'K'));
fNativeMenuItem->SetTarget(fHeaderView);
message = new BMessage(*message);
subMenu->AddItem(fSwappedMenuItem = new BMenuItem("", message, 'L'));
fSwappedMenuItem->SetTarget(fHeaderView);
menu->AddItem(new BMenuItem(subMenu));
_UpdateSelectionMenuItems(0, 0);
menu->AddSeparatorItem();
fBookmarkMenu = new BMenu(B_TRANSLATE("Bookmarks"));
fBookmarkMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Add"),
new BMessage(kMsgAddBookmark), 'B'));
item->SetTarget(this);
menu->AddItem(new BMenuItem(fBookmarkMenu));
bar->AddItem(menu);
// "Attributes" menu (it's only visible if the underlying
// file system actually supports attributes)
BDirectory directory;
BVolume volume;
if (directory.SetTo(&fEditor.AttributeRef()) == B_OK
&& directory.IsRootDirectory())
directory.GetVolume(&volume);
else
fEditor.File().GetVolume(&volume);
if (!fEditor.IsAttribute() && volume.InitCheck() == B_OK
&& (volume.KnowsMime() || volume.KnowsAttr())) {
bar->AddItem(menu = new BMenu(B_TRANSLATE("Attributes")));
_UpdateAttributesMenu(menu);
}
// "View" menu
menu = new BMenu(B_TRANSLATE_COMMENT("View",
"This is the last menubar item 'File Edit Block View'"));
// Number Base (hex/decimal)
subMenu = new BMenu(B_TRANSLATE_COMMENT("Base", "A menu item, the number "
"that is basis for a system of calculation. The base 10 system is a "
"decimal system. This is in the same menu window than 'Font size' "
"and 'BlockSize'"));
message = new BMessage(kMsgBaseType);
message->AddInt32("base_type", kDecimalBase);
subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Decimal",
"A menu item, as short as possible, noun is recommended if it is "
"shorter than adjective."), message, 'D'));
item->SetTarget(this);
if (fHeaderView->Base() == kDecimalBase)
item->SetMarked(true);
message = new BMessage(kMsgBaseType);
message->AddInt32("base_type", kHexBase);
subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Hex",
"A menu item, as short as possible, noun is recommended if it is "
"shorter than adjective."), message, 'H'));
item->SetTarget(this);
if (fHeaderView->Base() == kHexBase)
item->SetMarked(true);
subMenu->SetRadioMode(true);
menu->AddItem(new BMenuItem(subMenu));
// Block Size
subMenu = new BMenu(B_TRANSLATE_COMMENT("Block size", "Menu item. "
"This is in the same menu window than 'Base' and 'Font size'"));
subMenu->SetRadioMode(true);
const uint32 blockSizes[] = {512, 1024, 2048};
for (uint32 i = 0; i < sizeof(blockSizes) / sizeof(blockSizes[0]); i++) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%" B_PRId32 "%s", blockSizes[i],
fEditor.IsDevice() && fEditor.BlockSize() == blockSizes[i]
? B_TRANSLATE(" (native)") : "");
subMenu->AddItem(item = new BMenuItem(buffer,
message = new BMessage(kMsgBlockSize)));
message->AddInt32("block_size", blockSizes[i]);
if (fEditor.BlockSize() == blockSizes[i])
item->SetMarked(true);
}
if (subMenu->FindMarked() == NULL) {
// if the device has some weird block size, we'll add it here, too
char buffer[32];
snprintf(buffer, sizeof(buffer), B_TRANSLATE("%ld (native)"),
fEditor.BlockSize());
subMenu->AddItem(item = new BMenuItem(buffer,
message = new BMessage(kMsgBlockSize)));
message->AddInt32("block_size", fEditor.BlockSize());
item->SetMarked(true);
}
subMenu->SetTargetForItems(this);
menu->AddItem(new BMenuItem(subMenu));
menu->AddSeparatorItem();
// Font Size
subMenu = new BMenu(B_TRANSLATE("Font size"));
subMenu->SetRadioMode(true);
const int32 fontSizes[] = {9, 10, 11, 12, 13, 14, 18, 24, 36, 48};
int32 fontSize = int32(fDataView->FontSize() + 0.5);
if (fDataView->FontSizeFitsBounds())
fontSize = 0;
for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%" B_PRId32, fontSizes[i]);
subMenu->AddItem(item = new BMenuItem(buffer,
message = new BMessage(kMsgFontSize)));
message->AddFloat("font_size", fontSizes[i]);
if (fontSizes[i] == fontSize)
item->SetMarked(true);
}
subMenu->AddSeparatorItem();
subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Fit",
"Size of fonts, fits to available room"),
message = new BMessage(kMsgFontSize)));
message->AddFloat("font_size", 0.0f);
if (fontSize == 0)
item->SetMarked(true);
subMenu->SetTargetForItems(this);
menu->AddItem(new BMenuItem(subMenu));
bar->AddItem(menu);
}
void
ProbeView::AllAttached()
{
fHeaderView->SetTarget(fEditorLooper);
}
void
ProbeView::WindowActivated(bool active)
{
if (!active)
return;
fDataView->MakeFocus(true);
// set this view as the current find panel's target
BMessage target(kMsgFindTarget);
target.AddMessenger("target", this);
be_app_messenger.SendMessage(&target);
}
void
ProbeView::_UpdateSelectionMenuItems(int64 start, int64 end)
{
int64 position = 0;
const uint8* data = fDataView->DataAt(start);
if (data == NULL) {
fNativeMenuItem->SetEnabled(false);
fSwappedMenuItem->SetEnabled(false);
return;
}
// retrieve native endian position
int size;
if (end < start + 8)
size = end + 1 - start;
else
size = 8;
int64 bigEndianPosition = 0;
memcpy(&bigEndianPosition, data, size);
position = B_BENDIAN_TO_HOST_INT64(bigEndianPosition) >> (8 * (8 - size));
// update menu items
char buffer[128];
if (fDataView->Base() == kHexBase) {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: 0x%0*Lx"),
size * 2, position);
} else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %Ld (0x%0*Lx)"),
position, size * 2, position);
}
fNativeMenuItem->SetLabel(buffer);
fNativeMenuItem->SetEnabled(position >= 0
&& (off_t)(position * fEditor.BlockSize()) < fEditor.FileSize());
fNativeMenuItem->Message()->ReplaceInt64("block", position);
position = B_SWAP_INT64(position) >> (8 * (8 - size));
if (fDataView->Base() == kHexBase) {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: 0x%0*Lx"),
size * 2, position);
} else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %Ld (0x%0*Lx)"),
position, size * 2, position);
}
fSwappedMenuItem->SetLabel(buffer);
fSwappedMenuItem->SetEnabled(position >= 0 && (off_t)(position * fEditor.BlockSize()) < fEditor.FileSize());
fSwappedMenuItem->Message()->ReplaceInt64("block", position);
}
void
ProbeView::_UpdateBookmarkMenuItems()
{
for (int32 i = 2; i < fBookmarkMenu->CountItems(); i++) {
BMenuItem* item = fBookmarkMenu->ItemAt(i);
if (item == NULL)
break;
BMessage* message = item->Message();
if (message == NULL)
break;
off_t block = message->FindInt64("block");
char buffer[128];
if (fDataView->Base() == kHexBase)
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
else
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
item->SetLabel(buffer);
}
}
void
ProbeView::_AddBookmark(off_t position)
{
int32 count = fBookmarkMenu->CountItems();
if (count == 1) {
fBookmarkMenu->AddSeparatorItem();
count++;
}
// insert current position as bookmark
off_t block = position / fEditor.BlockSize();
off_t bookmark = -1;
BMenuItem* item;
int32 i;
for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) {
BMessage* message = item->Message();
if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) {
if (block <= bookmark)
break;
}
}
// the bookmark already exists
if (block == bookmark)
return;
char buffer[128];
if (fDataView->Base() == kHexBase)
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
else
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
BMessage* message;
item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate));
item->SetTarget(fHeaderView);
if (count < 12)
item->SetShortcut('0' + count - 2, B_COMMAND_KEY);
message->AddInt64("block", block);
fBookmarkMenu->AddItem(item, i);
}
void
ProbeView::_RemoveTypeEditor()
{
if (fTypeView == NULL)
return;
if (Parent() != NULL)
Parent()->RemoveChild(fTypeView);
else
Window()->RemoveChild(fTypeView);
delete fTypeView;
fTypeView = NULL;
}
void
ProbeView::_SetTypeEditor(int32 index)
{
if (index == -1) {
// remove type editor, show raw editor
if (IsHidden())
Show();
_RemoveTypeEditor();
} else {
// hide raw editor, create and show type editor
if (!IsHidden())
Hide();
_RemoveTypeEditor();
fTypeView = new TypeView(Frame(), "type shell", index, fEditor,
B_FOLLOW_ALL);
if (Parent() != NULL)
Parent()->AddChild(fTypeView);
else
Window()->AddChild(fTypeView);
}
}
void
ProbeView::_CheckClipboard()
{
if (!be_clipboard->Lock())
return;
bool hasData = false;
BMessage* clip;
if ((clip = be_clipboard->Data()) != NULL) {
const void* data;
ssize_t size;
if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK
|| clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK)
hasData = true;
}
be_clipboard->Unlock();
fPasteMenuItem->SetEnabled(hasData);
}
status_t
ProbeView::_PageSetup()
{
BPrintJob printJob(Window()->Title());
if (fPrintSettings != NULL)
printJob.SetSettings(new BMessage(*fPrintSettings));
status_t status = printJob.ConfigPage();
if (status == B_OK) {
// replace the print settings on success
delete fPrintSettings;
fPrintSettings = printJob.Settings();
}
return status;
}
void
ProbeView::_Print()
{
if (fPrintSettings == NULL && _PageSetup() != B_OK)
return;
BPrintJob printJob(Window()->Title());
printJob.SetSettings(new BMessage(*fPrintSettings));
if (printJob.ConfigJob() == B_OK) {
BRect rect = printJob.PrintableRect();
float width, height;
fDataView->GetPreferredSize(&width, &height);
printJob.BeginJob();
fDataView->SetScale(rect.Width() / width);
printJob.DrawView(fDataView, rect, rect.LeftTop());
fDataView->SetScale(1.0);
printJob.SpoolPage();
printJob.CommitJob();
}
}
status_t
ProbeView::_Save()
{
status_t status = fEditor.Save();
if (status == B_OK)
return B_OK;
char buffer[1024];
snprintf(buffer, sizeof(buffer),
B_TRANSLATE("Writing to the file failed:\n"
"%s\n\n"
"All changes will be lost when you quit."),
strerror(status));
BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
buffer, B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
return status;
}
bool
ProbeView::QuitRequested()
{
fEditorLooper->QuitFind();
if (!fEditor.IsModified())
return true;
BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"),
B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WIDTH_AS_USUAL,
B_OFFSET_SPACING, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
alert->SetShortcut(1, 'd');
alert->SetShortcut(2, 's');
int32 chosen = alert->Go();
if (chosen == 0)
return false;
if (chosen == 1)
return true;
return _Save() == B_OK;
}
void
ProbeView::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SAVE_REQUESTED:
_Save();
break;
case B_OBSERVER_NOTICE_CHANGE: {
int32 what;
if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
break;
switch (what) {
case kDataViewSelection:
{
int64 start, end;
if (message->FindInt64("start", &start) == B_OK
&& message->FindInt64("end", &end) == B_OK)
_UpdateSelectionMenuItems(start, end);
break;
}
}
break;
}
case kMsgBaseType:
{
int32 type;
if (message->FindInt32("base_type", &type) != B_OK)
break;
fHeaderView->SetBase((base_type)type);
fDataView->SetBase((base_type)type);
// The selection menu items depend on the base type as well
int32 start, end;
fDataView->GetSelection(start, end);
_UpdateSelectionMenuItems(start, end);
_UpdateBookmarkMenuItems();
// update the application's settings
BMessage update(*message);
update.what = kMsgSettingsChanged;
be_app_messenger.SendMessage(&update);
break;
}
case kMsgFontSize:
{
float size;
if (message->FindFloat("font_size", &size) != B_OK)
break;
fDataView->SetFontSize(size);
// update the application's settings
BMessage update(*message);
update.what = kMsgSettingsChanged;
be_app_messenger.SendMessage(&update);
break;
}
case kMsgBlockSize:
{
int32 blockSize;
if (message->FindInt32("block_size", &blockSize) != B_OK)
break;
BAutolock locker(fEditor);
if (fEditor.SetViewSize(blockSize) == B_OK
&& fEditor.SetBlockSize(blockSize) == B_OK)
fHeaderView->SetTo(fEditor.ViewOffset(), blockSize);
break;
}
case kMsgViewAs:
{
int32 index;
if (message->FindInt32("editor index", &index) != B_OK)
index = -1;
_SetTypeEditor(index);
break;
}
case kMsgAddBookmark:
_AddBookmark(fHeaderView->Position());
break;
case kMsgPrint:
_Print();
break;
case kMsgPageSetup:
_PageSetup();
break;
case kMsgOpenFindWindow:
{
fEditorLooper->QuitFind();
// set this view as the current find panel's target
BMessage find(*fFindAgainMenuItem->Message());
find.what = kMsgOpenFindWindow;
find.AddMessenger("target", this);
be_app_messenger.SendMessage(&find);
break;
}
case kMsgFind:
{
const uint8* data;
ssize_t size;
if (message->FindData("data", B_RAW_TYPE, (const void**)&data,
&size) != B_OK) {
// search again for last pattern
BMessage* itemMessage = fFindAgainMenuItem->Message();
if (itemMessage == NULL || itemMessage->FindData("data",
B_RAW_TYPE, (const void**)&data, &size) != B_OK) {
// this shouldn't ever happen, but well...
beep();
break;
}
} else {
// remember the search pattern
fFindAgainMenuItem->SetMessage(new BMessage(*message));
fFindAgainMenuItem->SetEnabled(true);
}
int32 start, end;
fDataView->GetSelection(start, end);
BMessage find(*message);
find.AddInt64("start", fHeaderView->Position() + start + 1);
find.AddMessenger("progress_monitor", BMessenger(fHeaderView));
fEditorLooper->PostMessage(&find);
break;
}
case kMsgStopFind:
fEditorLooper->QuitFind();
break;
case B_NODE_MONITOR:
{
switch (message->FindInt32("opcode")) {
case B_STAT_CHANGED:
fEditor.ForceUpdate();
break;
case B_ATTR_CHANGED:
{
const char* name;
if (message->FindString("attr", &name) != B_OK)
break;
if (fEditor.IsAttribute()) {
if (!strcmp(name, fEditor.Attribute()))
fEditor.ForceUpdate();
} else {
BMenuBar* bar = Window()->KeyMenuBar();
if (bar != NULL) {
BMenuItem* item = bar->FindItem("Attributes");
if (item != NULL && item->Submenu() != NULL)
_UpdateAttributesMenu(item->Submenu());
}
}
// There might be a new icon
if (!strcmp(name, "BEOS:TYPE")
|| !strcmp(name, "BEOS:M:STD_ICON")
|| !strcmp(name, "BEOS:L:STD_ICON")
|| !strcmp(name, "BEOS:ICON"))
fHeaderView->UpdateIcon();
break;
}
}
break;
}
case B_CLIPBOARD_CHANGED:
_CheckClipboard();
break;
case kMsgDataEditorStateChange:
{
bool enabled;
if (message->FindBool("can_undo", &enabled) == B_OK)
fUndoMenuItem->SetEnabled(enabled);
if (message->FindBool("can_redo", &enabled) == B_OK)
fRedoMenuItem->SetEnabled(enabled);
if (message->FindBool("modified", &enabled) == B_OK)
fSaveMenuItem->SetEnabled(enabled);
break;
}
default:
BView::MessageReceived(message);
}
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.