haiku/src/kits/tracker/Utilities.cpp
Rene Gollent 4183675895 Move black/white color constant definitions to header.
Previously, there were two instances of the actual variables, named in
ControlLook.cpp and Utilities.cpp. This caused clashes on gcc4 builds, leading
to various crashes during initialization. Instead we now declare them as
static constants in the header itself as is done for various other interface
color constants elsewhere. Fixes ticket #8358.
2012-03-02 20:39:12 -05:00

1664 lines
35 KiB
C++

/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#include "Attributes.h"
#include "MimeTypes.h"
#include "Model.h"
#include "PoseView.h"
#include "Utilities.h"
#include "ContainerWindow.h"
#include <IconUtils.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Debug.h>
#include <Directory.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <MenuItem.h>
#include <OS.h>
#include <PopUpMenu.h>
#include <Region.h>
#include <StorageDefs.h>
#include <TextView.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#ifndef _IMPEXP_BE
# define _IMPEXP_BE
#endif
extern _IMPEXP_BE const uint32 LARGE_ICON_TYPE;
extern _IMPEXP_BE const uint32 MINI_ICON_TYPE;
FILE *logFile = NULL;
static const float kMinSeparatorStubX = 10;
static const float kStubToStringSlotX = 5;
namespace BPrivate {
const float kExactMatchScore = INFINITY;
bool gLocalizedNamePreferred;
uint32
HashString(const char *string, uint32 seed)
{
char ch;
uint32 result = seed;
while((ch = *string++) != 0) {
result = (result << 7) ^ (result >> 24);
result ^= ch;
}
result ^= result << 12;
return result;
}
uint32
AttrHashString(const char *string, uint32 type)
{
char c;
uint32 hash = 0;
while((c = *string++) != 0) {
hash = (hash << 7) ^ (hash >> 24);
hash ^= c;
}
hash ^= hash << 12;
hash &= ~0xff;
hash |= type;
return hash;
}
bool
ValidateStream(BMallocIO *stream, uint32 key, int32 version)
{
uint32 testKey;
int32 testVersion;
if (stream->Read(&testKey, sizeof(uint32)) <= 0
|| stream->Read(&testVersion, sizeof(int32)) <=0)
return false;
return testKey == key && testVersion == version;
}
void
DisallowFilenameKeys(BTextView *textView)
{
textView->DisallowChar('/');
}
void
DisallowMetaKeys(BTextView *textView)
{
textView->DisallowChar(B_TAB);
textView->DisallowChar(B_ESCAPE);
textView->DisallowChar(B_INSERT);
textView->DisallowChar(B_DELETE);
textView->DisallowChar(B_HOME);
textView->DisallowChar(B_END);
textView->DisallowChar(B_PAGE_UP);
textView->DisallowChar(B_PAGE_DOWN);
textView->DisallowChar(B_FUNCTION_KEY);
}
PeriodicUpdatePoses::PeriodicUpdatePoses()
:
fPoseList(20, true)
{
fLock = new Benaphore("PeriodicUpdatePoses");
}
PeriodicUpdatePoses::~PeriodicUpdatePoses()
{
fLock->Lock();
fPoseList.MakeEmpty();
delete fLock;
}
void
PeriodicUpdatePoses::AddPose(BPose *pose, BPoseView *poseView,
PeriodicUpdateCallback callback, void *cookie)
{
periodic_pose *periodic = new periodic_pose;
periodic->pose = pose;
periodic->pose_view = poseView;
periodic->callback = callback;
periodic->cookie = cookie;
fPoseList.AddItem(periodic);
}
bool
PeriodicUpdatePoses::RemovePose(BPose *pose, void **cookie)
{
int32 count = fPoseList.CountItems();
for (int32 index = 0; index < count; index++) {
if (fPoseList.ItemAt(index)->pose == pose) {
if (!fLock->Lock())
return false;
periodic_pose *periodic = fPoseList.RemoveItemAt(index);
if (cookie)
*cookie = periodic->cookie;
delete periodic;
fLock->Unlock();
return true;
}
}
return false;
}
void
PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
{
if (!fLock->Lock())
return;
int32 count = fPoseList.CountItems();
for (int32 index = 0; index < count; index++) {
periodic_pose *periodic = fPoseList.ItemAt(index);
if (periodic->callback(periodic->pose, periodic->cookie)
|| forceRedraw) {
periodic->pose_view->LockLooper();
periodic->pose_view->UpdateIcon(periodic->pose);
periodic->pose_view->UnlockLooper();
}
}
fLock->Unlock();
}
PeriodicUpdatePoses gPeriodicUpdatePoses;
} // namespace BPrivate
void
PoseInfo::EndianSwap(void *castToThis)
{
PoseInfo *self = (PoseInfo *)castToThis;
PRINT(("swapping PoseInfo\n"));
STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
// do a sanity check on the icon position
if (self->fLocation.x < -20000 || self->fLocation.x > 20000
|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
// position out of range, force autoplcemement
PRINT((" rejecting icon position out of range\n"));
self->fInitedDirectory = -1LL;
self->fLocation = BPoint(0, 0);
}
}
void
PoseInfo::PrintToStream()
{
PRINT(("%s, inode:%Lx, location %f %f\n", fInvisible ? "hidden" : "visible",
fInitedDirectory, fLocation.x, fLocation.y));
}
// #pragma mark -
size_t
ExtendedPoseInfo::Size() const
{
return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
}
size_t
ExtendedPoseInfo::Size(int32 count)
{
return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
}
size_t
ExtendedPoseInfo::SizeWithHeadroom() const
{
return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
}
size_t
ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
{
int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
if (count > 0)
count /= sizeof(FrameLocation);
else
count = 0;
return Size(count + 1);
}
bool
ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
{
for (int32 index = 0; index < fNumFrames; index++) {
if (fLocations[index].fFrame == frame)
return true;
}
return false;
}
BPoint
ExtendedPoseInfo::LocationForFrame(BRect frame) const
{
for (int32 index = 0; index < fNumFrames; index++) {
if (fLocations[index].fFrame == frame)
return fLocations[index].fLocation;
}
TRESPASS();
return BPoint(0, 0);
}
bool
ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
{
for (int32 index = 0; index < fNumFrames; index++) {
if (fLocations[index].fFrame == frame) {
if (fLocations[index].fLocation == newLocation)
return false;
fLocations[index].fLocation = newLocation;
return true;
}
}
fLocations[fNumFrames].fFrame = frame;
fLocations[fNumFrames].fLocation = newLocation;
fLocations[fNumFrames].fWorkspaces = 0xffffffff;
fNumFrames++;
return true;
}
void
ExtendedPoseInfo::EndianSwap(void *castToThis)
{
ExtendedPoseInfo *self = (ExtendedPoseInfo *)castToThis;
PRINT(("swapping ExtendedPoseInfo\n"));
self->fWorkspaces = SwapUInt32(self->fWorkspaces);
self->fNumFrames = SwapInt32(self->fNumFrames);
for (int32 index = 0; index < self->fNumFrames; index++) {
swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
sizeof(BPoint), B_SWAP_ALWAYS);
if (self->fLocations[index].fLocation.x < -20000
|| self->fLocations[index].fLocation.x > 20000
|| self->fLocations[index].fLocation.y < -20000
|| self->fLocations[index].fLocation.y > 20000) {
// position out of range, force autoplcemement
PRINT((" rejecting icon position out of range\n"));
self->fLocations[index].fLocation = BPoint(0, 0);
}
swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
sizeof(BRect), B_SWAP_ALWAYS);
}
}
void
ExtendedPoseInfo::PrintToStream()
{
}
// #pragma mark -
OffscreenBitmap::OffscreenBitmap(BRect frame)
:
fBitmap(NULL)
{
NewBitmap(frame);
}
OffscreenBitmap::OffscreenBitmap()
:
fBitmap(NULL)
{
}
OffscreenBitmap::~OffscreenBitmap()
{
delete fBitmap;
}
void
OffscreenBitmap::NewBitmap(BRect bounds)
{
delete fBitmap;
fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true);
if (fBitmap && fBitmap->Lock()) {
BView *view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
fBitmap->AddChild(view);
BRect clipRect = view->Bounds();
BRegion newClip;
newClip.Set(clipRect);
view->ConstrainClippingRegion(&newClip);
fBitmap->Unlock();
} else {
delete fBitmap;
fBitmap = NULL;
}
}
BView *
OffscreenBitmap::BeginUsing(BRect frame)
{
if (!fBitmap || fBitmap->Bounds() != frame)
NewBitmap(frame);
fBitmap->Lock();
return View();
}
void
OffscreenBitmap::DoneUsing()
{
fBitmap->Unlock();
}
BBitmap *
OffscreenBitmap::Bitmap() const
{
ASSERT(fBitmap);
ASSERT(fBitmap->IsLocked());
return fBitmap;
}
BView *
OffscreenBitmap::View() const
{
ASSERT(fBitmap);
return fBitmap->ChildAt(0);
}
// #pragma mark -
namespace BPrivate {
/*! Changes the alpha value of the given bitmap to create a nice
horizontal fade out in the specified region.
"from" is always transparent, "to" opaque.
*/
void
FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from,
int32 to)
{
// check parameters
if (width < 0 || height < 0 || from < 0 || to < 0)
return;
float change = 1.f / (to - from);
if (from > to) {
int32 temp = from;
from = to;
to = temp;
}
for (int32 y = 0; y < height; y++) {
float alpha = change > 0 ? 0.0f : 1.0f;
for (int32 x = from; x <= to; x++) {
if (bits[x] & 0xff000000) {
uint32 a = uint32((bits[x] >> 24) * alpha);
bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
}
alpha += change;
}
bits += width;
}
}
/*! Changes the alpha value of the given bitmap to create a nice
vertical fade out in the specified region.
"from" is always transparent, "to" opaque.
*/
void
FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from,
int32 to)
{
// check parameters
if (width < 0 || height < 0 || from < 0 || to < 0)
return;
if (from > to)
bits += width * (height - (from - to));
float change = 1.f / (to - from);
if (from > to) {
int32 temp = from;
from = to;
to = temp;
}
float alpha = change > 0 ? 0.0f : 1.0f;
for (int32 y = from; y <= to; y++) {
for (int32 x = 0; x < width; x++) {
if (bits[x] & 0xff000000) {
uint32 a = uint32((bits[x] >> 24) * alpha);
bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
}
}
alpha += change;
bits += width;
}
}
} // namespace BPrivate
// #pragma mark -
DraggableIcon::DraggableIcon(BRect rect, const char *name, const char *mimeType,
icon_size size, const BMessage *message, BMessenger target,
uint32 resizeMask, uint32 flags)
:
BView(rect, name, resizeMask, flags),
fMessage(*message),
fTarget(target)
{
fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
BMimeType mime(mimeType);
status_t error = mime.GetIcon(fBitmap, size);
ASSERT(mime.IsValid());
if (error != B_OK) {
PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
BMimeType mime(B_FILE_MIMETYPE);
ASSERT(mime.IsInstalled());
mime.GetIcon(fBitmap, size);
}
}
DraggableIcon::~DraggableIcon()
{
delete fBitmap;
}
void
DraggableIcon::SetTarget(BMessenger target)
{
fTarget = target;
}
BRect
DraggableIcon::PreferredRect(BPoint offset, icon_size size)
{
BRect result(0, 0, size - 1, size - 1);
result.OffsetTo(offset);
return result;
}
void
DraggableIcon::AttachedToWindow()
{
BView *parent = Parent();
if (parent != NULL) {
SetViewColor(parent->ViewColor());
SetLowColor(parent->LowColor());
}
}
void
DraggableIcon::MouseDown(BPoint point)
{
if (!DragStarted(&fMessage))
return;
BRect rect(Bounds());
BBitmap *dragBitmap = new BBitmap(rect, B_RGBA32, true);
dragBitmap->Lock();
BView *view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
dragBitmap->AddChild(view);
view->SetOrigin(0, 0);
BRect clipRect(view->Bounds());
BRegion newClip;
newClip.Set(clipRect);
view->ConstrainClippingRegion(&newClip);
// Transparent draw magic
view->SetHighColor(0, 0, 0, 0);
view->FillRect(view->Bounds());
view->SetDrawingMode(B_OP_ALPHA);
view->SetHighColor(0, 0, 0, 128);
// set the level of transparency by value
view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
view->DrawBitmap(fBitmap);
view->Sync();
dragBitmap->Unlock();
DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
}
bool
DraggableIcon::DragStarted(BMessage *)
{
return true;
}
void
DraggableIcon::Draw(BRect)
{
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
DrawBitmap(fBitmap);
}
// #pragma mark -
FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
const char *text, uint32 resizeFlags, uint32 flags)
:
BStringView(bounds, name, text, resizeFlags, flags),
fBitmap(NULL),
fOrigBitmap(NULL)
{
}
FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
const char *text, BBitmap *inBitmap, uint32 resizeFlags, uint32 flags)
:
BStringView(bounds, name, text, resizeFlags, flags),
fBitmap(NULL),
fOrigBitmap(inBitmap)
{
}
FlickerFreeStringView::~FlickerFreeStringView()
{
delete fBitmap;
}
void
FlickerFreeStringView::Draw(BRect)
{
BRect bounds(Bounds());
if (!fBitmap)
fBitmap = new OffscreenBitmap(Bounds());
BView *offscreen = fBitmap->BeginUsing(bounds);
if (Parent()) {
fViewColor = Parent()->ViewColor();
fLowColor = Parent()->ViewColor();
}
offscreen->SetViewColor(fViewColor);
offscreen->SetHighColor(HighColor());
offscreen->SetLowColor(fLowColor);
BFont font;
GetFont(&font);
offscreen->SetFont(&font);
offscreen->Sync();
if (fOrigBitmap)
offscreen->DrawBitmap(fOrigBitmap, Frame(), bounds);
else
offscreen->FillRect(bounds, B_SOLID_LOW);
if (Text()) {
BPoint loc;
font_height height;
GetFontHeight(&height);
edge_info eInfo;
switch (Alignment()) {
case B_ALIGN_LEFT:
case B_ALIGN_HORIZONTAL_UNSET:
case B_ALIGN_USE_FULL_WIDTH:
{
// If the first char has a negative left edge give it
// some more room by shifting that much more to the right.
font.GetEdges(Text(), 1, &eInfo);
loc.x = bounds.left + (2 - eInfo.left);
break;
}
case B_ALIGN_CENTER:
{
float width = StringWidth(Text());
float center = (bounds.right - bounds.left) / 2;
loc.x = center - (width/2);
break;
}
case B_ALIGN_RIGHT:
{
float width = StringWidth(Text());
loc.x = bounds.right - width - 2;
break;
}
}
loc.y = bounds.bottom - (1 + height.descent);
offscreen->DrawString(Text(), loc);
}
offscreen->Sync();
SetDrawingMode(B_OP_COPY);
DrawBitmap(fBitmap->Bitmap());
fBitmap->DoneUsing();
}
void
FlickerFreeStringView::AttachedToWindow()
{
_inherited::AttachedToWindow();
if (Parent()) {
fViewColor = Parent()->ViewColor();
fLowColor = Parent()->ViewColor();
}
SetViewColor(B_TRANSPARENT_32_BIT);
SetLowColor(B_TRANSPARENT_32_BIT);
}
void
FlickerFreeStringView::SetViewColor(rgb_color color)
{
if (fViewColor != color) {
fViewColor = color;
Invalidate();
}
_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
}
void
FlickerFreeStringView::SetLowColor(rgb_color color)
{
if (fLowColor != color) {
fLowColor = color;
Invalidate();
}
_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
}
// #pragma mark -
TitledSeparatorItem::TitledSeparatorItem(const char *label)
:
BMenuItem(label, 0)
{
_inherited::SetEnabled(false);
}
TitledSeparatorItem::~TitledSeparatorItem()
{
}
void
TitledSeparatorItem::SetEnabled(bool)
{
// leave disabled
}
void
TitledSeparatorItem::GetContentSize(float *width, float *height)
{
_inherited::GetContentSize(width, height);
}
inline rgb_color
ShiftMenuBackgroundColor(float by)
{
return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
}
void
TitledSeparatorItem::Draw()
{
BRect frame(Frame());
BMenu *parent = Menu();
ASSERT(parent);
menu_info minfo;
get_menu_info(&minfo);
if (minfo.separator > 0) {
frame.left += 10;
frame.right -= 10;
} else {
frame.left += 1;
frame.right -= 1;
}
float startX = frame.left;
float endX = frame.right;
float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
+ 2 * kStubToStringSlotX);
// ToDo:
// handle case where maxStringWidth turns out negative here
BString truncatedLabel(Label());
parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
maxStringWidth = parent->StringWidth(truncatedLabel.String());
// first calculate the length of the stub part of the
// divider line, so we can use it for secondStartX
float firstEndX = ((endX - startX) - maxStringWidth) / 2
- kStubToStringSlotX;
if (firstEndX < 0)
firstEndX = 0;
float secondStartX = endX - firstEndX;
// now finish calculating firstEndX
firstEndX += startX;
parent->PushState();
int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
if (minfo.separator == 2) {
y++;
frame.left++;
frame.right--;
parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
}
y++;
if (minfo.separator == 2) {
frame.left++;
frame.right--;
}
parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
parent->EndLineArray();
font_height finfo;
parent->GetFontHeight(&finfo);
parent->SetLowColor(parent->ViewColor());
BPoint loc(firstEndX + kStubToStringSlotX,
ContentLocation().y + finfo.ascent);
parent->MovePenTo(loc + BPoint(1, 1));
parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
parent->DrawString(truncatedLabel.String());
parent->MovePenTo(loc);
parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
parent->DrawString(truncatedLabel.String());
parent->PopState();
}
// #pragma mark -
ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
uint32 shortcutWhat, BHandler *target)
:
BMessageFilter(B_KEY_DOWN),
fShortcutKey(shortcutKey),
fShortcutModifier(shortcutModifier),
fShortcutWhat(shortcutWhat),
fTarget(target)
{
}
filter_result
ShortcutFilter::Filter(BMessage *message, BHandler **)
{
if (message->what == B_KEY_DOWN) {
uint32 modifiers;
uint32 rawKeyChar = 0;
uint8 byte = 0;
int32 key = 0;
if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
|| message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
|| message->FindInt8("byte", (int8 *)&byte) != B_OK
|| message->FindInt32("key", &key) != B_OK)
return B_DISPATCH_MESSAGE;
modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
| B_OPTION_KEY | B_MENU_KEY;
// strip caps lock, etc.
if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
return B_SKIP_MESSAGE;
}
}
// let others deal with this
return B_DISPATCH_MESSAGE;
}
// #pragma mark -
namespace BPrivate {
void
EmbedUniqueVolumeInfo(BMessage *message, const BVolume *volume)
{
BDirectory rootDirectory;
time_t created;
fs_info info;
if (volume->GetRootDirectory(&rootDirectory) == B_OK
&& rootDirectory.GetCreationTime(&created) == B_OK
&& fs_stat_dev(volume->Device(), &info) == 0) {
message->AddInt32("creationDate", created);
message->AddInt64("capacity", volume->Capacity());
message->AddString("deviceName", info.device_name);
message->AddString("volumeName", info.volume_name);
message->AddString("fshName", info.fsh_name);
}
}
status_t
MatchArchivedVolume(BVolume *result, const BMessage *message, int32 index)
{
time_t created;
off_t capacity;
if (message->FindInt32("creationDate", index, &created) != B_OK
|| message->FindInt64("capacity", index, &capacity) != B_OK)
return B_ERROR;
BVolumeRoster roster;
BVolume volume;
BString deviceName, volumeName, fshName;
if (message->FindString("deviceName", &deviceName) == B_OK
&& message->FindString("volumeName", &volumeName) == B_OK
&& message->FindString("fshName", &fshName) == B_OK) {
// New style volume identifiers: We have a couple of characteristics,
// and compute a score from them. The volume with the greatest score
// (if over a certain threshold) is the one we're looking for. We
// pick the first volume, in case there is more than one with the
// same score.
dev_t foundDevice = -1;
int foundScore = -1;
roster.Rewind();
while (roster.GetNextVolume(&volume) == B_OK) {
if (volume.IsPersistent() && volume.KnowsQuery()) {
// get creation time and fs_info
BDirectory root;
volume.GetRootDirectory(&root);
time_t cmpCreated;
fs_info info;
if (root.GetCreationTime(&cmpCreated) == B_OK
&& fs_stat_dev(volume.Device(), &info) == 0) {
// compute the score
int score = 0;
// creation time
if (created == cmpCreated)
score += 5;
// capacity
if (capacity == volume.Capacity())
score += 4;
// device name
if (deviceName == info.device_name)
score += 3;
// volume name
if (volumeName == info.volume_name)
score += 2;
// fsh name
if (fshName == info.fsh_name)
score += 1;
// check score
if (score >= 9 && score > foundScore) {
foundDevice = volume.Device();
foundScore = score;
}
}
}
}
if (foundDevice >= 0)
return result->SetTo(foundDevice);
} else {
// Old style volume identifiers: We have only creation time and
// capacity. Both must match.
roster.Rewind();
while (roster.GetNextVolume(&volume) == B_OK)
if (volume.IsPersistent() && volume.KnowsQuery()) {
BDirectory root;
volume.GetRootDirectory(&root);
time_t cmpCreated;
root.GetCreationTime(&cmpCreated);
if (created == cmpCreated && capacity == volume.Capacity()) {
*result = volume;
return B_OK;
}
}
}
return B_DEV_BAD_DRIVE_NUM;
}
void
StringFromStream(BString *string, BMallocIO *stream, bool endianSwap)
{
int32 length;
stream->Read(&length, sizeof(length));
if (endianSwap)
length = SwapInt32(length);
if (length < 0 || length > 10000) {
// TODO: should fail here
PRINT(("problems instatiating a string, length probably wrong %"
B_PRId32 "\n", length));
return;
}
char *buffer = string->LockBuffer(length + 1);
stream->Read(buffer, (size_t)length + 1);
string->UnlockBuffer(length);
}
void
StringToStream(const BString *string, BMallocIO *stream)
{
int32 length = string->Length();
stream->Write(&length, sizeof(int32));
stream->Write(string->String(), (size_t)string->Length() + 1);
}
int32
ArchiveSize(const BString *string)
{
return string->Length() + 1 + (ssize_t)sizeof(int32);
}
int32
CountRefs(const BMessage *message)
{
uint32 type;
int32 count;
message->GetInfo("refs", &type, &count);
return count;
}
static entry_ref *
EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
void *passThru, int32 maxCount)
{
uint32 type;
int32 count;
message->GetInfo("refs", &type, &count);
if (maxCount >= 0 && count > maxCount)
count = maxCount;
for (int32 index = 0; index < count; index++) {
entry_ref ref;
message->FindRef("refs", index, &ref);
entry_ref *result = (func)(&ref, passThru);
if (result)
return result;
}
return NULL;
}
bool
ContainsEntryRef(const BMessage *message, const entry_ref *ref)
{
entry_ref match;
for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
index++) {
if (*ref == match)
return true;
}
return false;
}
entry_ref *
EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
void *passThru)
{
return EachEntryRefCommon(message, func, passThru, -1);
}
typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
const entry_ref *
EachEntryRef(const BMessage *message,
const entry_ref *(*func)(const entry_ref *, void *), void *passThru)
{
return EachEntryRefCommon(const_cast<BMessage *>(message),
(EachEntryIteratee)func, passThru, -1);
}
entry_ref *
EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
void *passThru, int32 maxCount)
{
return EachEntryRefCommon(message, func, passThru, maxCount);
}
const entry_ref *
EachEntryRef(const BMessage *message,
const entry_ref *(*func)(const entry_ref *, void *), void *passThru,
int32 maxCount)
{
return EachEntryRefCommon(const_cast<BMessage *>(message),
(EachEntryIteratee)func, passThru, maxCount);
}
void
TruncateLeaf(BString *string)
{
for (int32 index = string->Length(); index >= 0; index--) {
if ((*string)[index] == '/') {
string->Truncate(index + 1);
return;
}
}
}
int64
StringToScalar(const char *text)
{
char *end;
int64 val;
char *buffer = new char [strlen(text) + 1];
strcpy(buffer, text);
if (strstr(buffer, "k") || strstr(buffer, "K")) {
val = strtoll(buffer, &end, 10);
val *= kKBSize;
} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
val = strtoll(buffer, &end, 10);
val *= kMBSize;
} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
val = strtoll(buffer, &end, 10);
val *= kGBSize;
} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
val = strtoll(buffer, &end, 10);
val *= kGBSize;
} else {
// no suffix, try plain byte conversion
val = strtoll(buffer, &end, 10);
}
delete [] buffer;
return val;
}
static BRect
LineBounds(BPoint where, float length, bool vertical)
{
BRect result;
result.SetLeftTop(where);
result.SetRightBottom(where + BPoint(2, 2));
if (vertical)
result.bottom = result.top + length;
else
result.right = result.left + length;
return result;
}
SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
const char *name)
:
BView(LineBounds(where, length, vertical), name,
B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
void
SeparatorLine::Draw(BRect)
{
BRect bounds(Bounds());
rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
bool vertical = (bounds.left > bounds.right - 3);
BeginLineArray(2);
if (vertical) {
AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
AddLine(bounds.LeftTop() + BPoint(1, 0),
bounds.LeftBottom() + BPoint(1, 0), kWhite);
} else {
AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
AddLine(bounds.LeftTop() + BPoint(0, 1),
bounds.RightTop() + BPoint(0, 1), kWhite);
}
EndLineArray();
}
void
HexDump(const void *buf, int32 length)
{
const int32 kBytesPerLine = 16;
int32 offset;
unsigned char *buffer = (unsigned char *)buf;
for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
int32 remain = length;
int32 index;
printf( "0x%06x: ", (int)offset);
for (index = 0; index < kBytesPerLine; index++) {
if (remain-- > 0)
printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
else
printf(" ");
}
remain = length;
printf(" \'");
for (index = 0; index < kBytesPerLine; index++) {
if (remain-- > 0)
printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
else
printf(" ");
}
printf("\'\n");
length -= kBytesPerLine;
if (length <= 0)
break;
}
fflush(stdout);
}
void
EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on)
{
BMenuItem *item = menu->FindItem(itemName);
if (item)
item->SetEnabled(on);
}
void
MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on)
{
BMenuItem *item = menu->FindItem(itemName);
if (item)
item->SetMarked(on);
}
void
EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
{
BMenuItem *item = menu->FindItem(commandName);
if (item)
item->SetEnabled(on);
}
void
MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
{
BMenuItem *item = menu->FindItem(commandName);
if (item)
item->SetMarked(on);
}
void
DeleteSubmenu(BMenuItem *submenuItem)
{
if (!submenuItem)
return;
BMenu *menu = submenuItem->Submenu();
if (!menu)
return;
for (;;) {
BMenuItem *item = menu->RemoveItem((int32)0);
if (!item)
return;
delete item;
}
}
status_t
GetAppSignatureFromAttr(BFile *file, char *result)
{
// This call is a performance improvement that
// avoids using the BAppFileInfo API when retrieving the
// app signature -- the call is expensive because by default
// the resource fork is scanned to read the attribute
#ifdef B_APP_FILE_INFO_IS_FAST
BAppFileInfo appFileInfo(file);
return appFileInfo.GetSignature(result);
#else
ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
0, result, B_MIME_TYPE_LENGTH);
if (readResult <= 0)
return (status_t)readResult;
return B_OK;
#endif // B_APP_FILE_INFO_IS_FAST
}
status_t
GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size)
{
// This call is a performance improvement that
// avoids using the BAppFileInfo API when retrieving the
// app icons -- the call is expensive because by default
// the resource fork is scanned to read the icons
//#ifdef B_APP_FILE_INFO_IS_FAST
BAppFileInfo appFileInfo(file);
return appFileInfo.GetIcon(result, size);
//#else
//
// const char *attrName = kAttrIcon;
// uint32 type = B_VECTOR_ICON_TYPE;
//
// // try vector icon
// attr_info ainfo;
// status_t ret = file->GetAttrInfo(attrName, &ainfo);
//
// if (ret == B_OK) {
// uint8 buffer[ainfo.size];
// ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
// ainfo.size);
// if (readResult == ainfo.size) {
// if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK)
// return B_OK;
// }
// }
//
// // try again with R5 icons
// attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
// type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
//
// ret = file->GetAttrInfo(attrName, &ainfo);
// if (ret < B_OK)
// return ret;
//
// uint8 buffer[ainfo.size];
//
// ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
// if (readResult <= 0)
// return (status_t)readResult;
//
// if (result->ColorSpace() != B_CMAP8) {
// ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result);
// } else {
// result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8);
// }
//
// return ret;
//#endif // B_APP_FILE_INFO_IS_FAST
}
status_t
GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size)
{
BNodeInfo fileInfo(file);
return fileInfo.GetIcon(result, size);
}
void
PrintToStream(rgb_color color)
{
printf("r:%x, g:%x, b:%x, a:%x\n",
color.red, color.green, color.blue, color.alpha);
}
extern BMenuItem *
EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *))
{
int32 count = menu->CountItems();
for (int32 index = 0; index < count; index++) {
BMenuItem *item = menu->ItemAt(index);
BMenuItem *result = (func)(item);
if (result)
return result;
if (recursive) {
BMenu *submenu = menu->SubmenuAt(index);
if (submenu)
return EachMenuItem(submenu, true, func);
}
}
return NULL;
}
extern const BMenuItem *
EachMenuItem(const BMenu *menu, bool recursive,
BMenuItem *(*func)(const BMenuItem *))
{
int32 count = menu->CountItems();
for (int32 index = 0; index < count; index++) {
BMenuItem *item = menu->ItemAt(index);
BMenuItem *result = (func)(item);
if (result)
return result;
if (recursive) {
BMenu *submenu = menu->SubmenuAt(index);
if (submenu)
return EachMenuItem(submenu, true, func);
}
}
return NULL;
}
PositionPassingMenuItem::PositionPassingMenuItem(const char *title,
BMessage *message, char shortcut, uint32 modifiers)
:
BMenuItem(title, message, shortcut, modifiers)
{
}
PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu, BMessage *message)
:
BMenuItem(menu, message)
{
}
status_t
PositionPassingMenuItem::Invoke(BMessage *message)
{
if (!Menu())
return B_ERROR;
if (!IsEnabled())
return B_ERROR;
if (!message)
message = Message();
if (!message)
return B_BAD_VALUE;
BMessage clone(*message);
clone.AddInt32("index", Menu()->IndexOf(this));
clone.AddInt64("when", system_time());
clone.AddPointer("source", this);
// embed the invoke location of the menu so that we can create
// a new folder, etc. on the spot
BMenu *menu = Menu();
for (;;) {
if (!menu->Supermenu())
break;
menu = menu->Supermenu();
}
// use the window position only, if the item was invoked from the menu
// menu->Window() points to the window the item was invoked from
if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) {
LooperAutoLocker lock(menu);
if (lock.IsLocked()) {
BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
clone.AddPoint("be:invoke_origin", invokeOrigin);
}
}
return BInvoker::Invoke(&clone);
}
bool
BootedInSafeMode()
{
const char *safeMode = getenv("SAFEMODE");
return (safeMode && strcmp(safeMode, "yes") == 0);
}
float
ComputeTypeAheadScore(const char *text, const char *match, bool wordMode)
{
// highest score: exact match
const char* found = strcasestr(text, match);
if (found != NULL) {
if (found == text)
return kExactMatchScore;
return 1.f / (found - text);
}
// there was no exact match
// second best: all characters at word beginnings
if (wordMode) {
float score = 0;
for (int32 j = 0, k = 0; match[j]; j++) {
while (text[k]
&& tolower(text[k]) != tolower(match[j])) {
k++;
}
if (text[k] == '\0') {
score = 0;
break;
}
bool wordStart = k == 0 || isspace(text[k - 1]);
if (wordStart)
score++;
if (j > 0) {
bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
if (wordEnd)
score += 0.3;
if (match[j - 1] == text[k - 1])
score += 0.7;
}
score += 1.f / (k + 1);
k++;
}
return score;
}
return -1;
}
void
_ThrowOnError(status_t error, const char *DEBUG_ONLY(file),
int32 DEBUG_ONLY(line))
{
if (error != B_OK) {
PRINT(("failing %s at %s:%d\n", strerror(error), file, (int)line));
throw error;
}
}
void
_ThrowIfNotSize(ssize_t size, const char *DEBUG_ONLY(file),
int32 DEBUG_ONLY(line))
{
if (size < B_OK) {
PRINT(("failing %s at %s:%d\n", strerror(size), file, (int)line));
throw (status_t)size;
}
}
void
_ThrowOnError(status_t error, const char *DEBUG_ONLY(debugString),
const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
{
if (error != B_OK) {
PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file,
(int)line));
throw error;
}
}
} // namespace BPrivate