* 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 <ControlLook.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 <LayoutBuilder.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"
#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();
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;
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(const char* name, int32 index, DataEditor& editor);
virtual ~TypeView();
private:
BView* fTypeEditorView;
};
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';
}
}
IconView::IconView(const entry_ref* ref, bool isDevice)
: BView(NULL, B_WILL_DRAW),
fRef(*ref),
fIsDevice(isDevice),
fBitmap(NULL)
{
UpdateIcon();
if (fBitmap != NULL)
SetExplicitSize(fBitmap->Bounds().Size());
}
IconView::~IconView()
{
delete fBitmap;
}
void
IconView::AttachedToWindow()
{
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) {
fBitmap = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON)),
B_RGBA32);
}
if (fBitmap != NULL) {
status_t status = B_ERROR;
if (fIsDevice) {
BPath path(&fRef);
status = get_device_icon(path.Path(), fBitmap, B_LARGE_ICON);
} else {
status = BNodeInfo::GetTrackerIcon(&fRef, fBitmap,
(icon_size)(fBitmap->Bounds().IntegerWidth() + 1));
}
if (status != B_OK) {
BMimeType type(B_FILE_MIME_TYPE);
status = type.GetIcon(fBitmap, B_LARGE_ICON);
}
if (status != B_OK) {
delete fBitmap;
fBitmap = NULL;
}
Invalidate();
}
}
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();
rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
UseFillColor(true, &color);
}
PositionSlider::~PositionSlider()
{
}
void
PositionSlider::Reset()
{
SetKeyIncrementValue(int32(1.0 * kMaxSliderLimit / ((fSize - 1) / fBlockSize) + 0.5));
SetEnabled(fSize > fBlockSize);
}
off_t
PositionSlider::Position() const
{
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);
}
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(ceilf(plainFont.Size() * 0.83));
plainFont.SetSize(ceilf(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);
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);
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:
{
if (Looper()->MessageQueue()->FindMessage(kMsgSliderUpdate, 0)
!= NULL)
break;
if (fPosition == fPositionSlider->Position())
break;
fLastPosition = fPosition;
fPosition = fPositionSlider->Position();
UpdatePositionViews();
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;
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;
if (fPosition < 0)
fPosition = 0;
else if (fPosition > ((fFileSize - 1) / fBlockSize) * fBlockSize)
fPosition = ((fFileSize - 1) / fBlockSize) * fBlockSize;
UpdatePositionViews();
fPositionSlider->SetPosition(fPosition);
NotifyTarget();
break;
}
case kMsgLastPosition:
{
fPosition = fLastPosition;
fLastPosition = fPositionSlider->Position();
UpdatePositionViews();
fPositionSlider->SetPosition(fPosition);
NotifyTarget();
break;
}
case kMsgBaseType:
{
int32 type;
if (message->FindInt32("base", &type) != B_OK)
break;
SetBase((base_type)type);
break;
}
default:
BView::MessageReceived(message);
}
}
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()
{
BMenuItem::DrawContent();
font_height fontHeight;
Menu()->GetFontHeight(&fontHeight);
BPoint point = ContentLocation();
point.x = Frame().right - 4 - Menu()->StringWidth(fType.String());
point.y += fontHeight.ascent;
Menu()->DrawString(fType.String(), point);
}
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:
{
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);
}
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);
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) {
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;
}
TypeView::TypeView(const char* name, int32 index, DataEditor& editor)
: BView(name, B_FRAME_EVENTS)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fTypeEditorView = GetTypeEditorAt(index, editor);
if (fTypeEditorView == NULL) {
fTypeEditorView = new BStringView(B_TRANSLATE("Type editor"),
B_TRANSLATE("Type editor not supported"));
}
BLayoutBuilder::Group<>(this, B_VERTICAL)
.Add(fTypeEditorView);
}
TypeView::~TypeView()
{
}
ProbeView::ProbeView(entry_ref* ref, const char* attribute,
const BMessage* settings)
: BView("probeView", B_WILL_DRAW),
fPrintSettings(NULL),
fTypeView(NULL),
fLastSearch(NULL)
{
fEditor.SetTo(*ref, attribute);
int32 baseType = kHexBase;
float fontSize = be_plain_font->Size();
if (settings != NULL) {
settings->FindInt32("base_type", &baseType);
settings->FindFloat("font_size", &fontSize);
}
fHeaderView = new HeaderView(&fEditor.Ref(), fEditor);
fHeaderView->SetBase((base_type)baseType);
fDataView = new DataView(fEditor);
fDataView->SetBase((base_type)baseType);
fDataView->SetFontSize(fontSize);
fScrollView = new BScrollView("scroller", fDataView, B_WILL_DRAW, true,
true);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(-1, -1, -1, -1)
.Add(fHeaderView)
.AddGroup(B_VERTICAL, 0)
.SetInsets(-1, 0, -1, -1)
.Add(fScrollView)
.End();
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)
{
for (int32 i = menu->CountItems(); i-- > 0;) {
delete menu->RemoveItem(i);
}
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, "]");
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) {
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);
}
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);
BMenuBar* bar = Window()->KeyMenuBar();
if (bar == NULL) {
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);
}
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);
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);
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);
}
menu = new BMenu(B_TRANSLATE_COMMENT("View",
"This is the last menubar item 'File Edit Block View'"));
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));
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, 4096};
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) {
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();
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);
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;
}
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));
char buffer[128];
if (fDataView->Base() == kHexBase) {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: 0x%0*Lx"),
size * 2, (long long int)position);
} else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %lld (0x%0*Lx)"),
(long long int)position, size * 2, (long long int)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, (long long int)position);
} else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %lld (0x%0*Lx)"),
(long long int)position, size * 2, (long long int)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"), (long long unsigned)block);
else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"),
(long long int)block, (long long unsigned)block);
}
item->SetLabel(buffer);
}
}
void
ProbeView::_AddBookmark(off_t position)
{
int32 count = fBookmarkMenu->CountItems();
if (count == 1) {
fBookmarkMenu->AddSeparatorItem();
count++;
}
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;
}
}
if (block == bookmark)
return;
char buffer[128];
if (fDataView->Base() == kHexBase)
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), (long long unsigned)block);
else {
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"),
(long long int)block, (long long unsigned)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) {
if (IsHidden())
Show();
_RemoveTypeEditor();
} else {
if (!IsHidden())
Hide();
_RemoveTypeEditor();
fTypeView = new TypeView("type shell", index, fEditor);
if (Parent() != NULL)
Parent()->GetLayout()->AddView(fTypeView);
else
Window()->GetLayout()->AddView(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) {
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);
int32 start, end;
fDataView->GetSelection(start, end);
_UpdateSelectionMenuItems(start, end);
_UpdateBookmarkMenuItems();
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);
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();
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) {
BMessage* itemMessage = fFindAgainMenuItem->Message();
if (itemMessage == NULL || itemMessage->FindData("data",
B_RAW_TYPE, (const void**)&data, &size) != B_OK) {
beep();
break;
}
} else {
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());
}
}
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);
}
}