426 lines
8.7 KiB
C++
426 lines
8.7 KiB
C++
/*
|
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
|
* Distributed under the terms of the MIT/X11 license.
|
|
*
|
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
|
* The software comes with no warranty, etc.
|
|
*/
|
|
|
|
|
|
#include "ControlsView.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <Bitmap.h>
|
|
#include <Box.h>
|
|
#include <TabView.h>
|
|
#include <NodeMonitor.h>
|
|
#include <Path.h>
|
|
#include <PopUpMenu.h>
|
|
#include <String.h>
|
|
#include <SupportDefs.h>
|
|
#include <View.h>
|
|
#include <Volume.h>
|
|
#include <VolumeRoster.h>
|
|
#include <Window.h>
|
|
|
|
#include <LayoutBuilder.h>
|
|
|
|
#include "DiskUsage.h"
|
|
#include "VolumeView.h"
|
|
|
|
|
|
class VolumeTab: public BTab {
|
|
public:
|
|
VolumeTab(BVolume* volume);
|
|
virtual ~VolumeTab();
|
|
|
|
BVolume* Volume() const
|
|
{ return fVolume; }
|
|
float IconWidth() const;
|
|
|
|
virtual void DrawLabel(BView* owner, BRect frame);
|
|
virtual void DrawFocusMark(BView* owner, BRect frame);
|
|
|
|
private:
|
|
BBitmap* fIcon;
|
|
BVolume* fVolume;
|
|
};
|
|
|
|
|
|
VolumeTab::VolumeTab(BVolume* volume)
|
|
:
|
|
BTab(),
|
|
fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
|
|
fVolume(volume)
|
|
{
|
|
if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
|
|
delete fIcon;
|
|
fIcon = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
float
|
|
VolumeTab::IconWidth() const
|
|
{
|
|
if (fIcon != NULL)
|
|
// add a small margin
|
|
return fIcon->Bounds().Width() + kSmallHMargin;
|
|
else
|
|
return 0.0f;
|
|
}
|
|
|
|
|
|
void
|
|
VolumeTab::DrawLabel(BView* owner, BRect frame)
|
|
{
|
|
owner->SetDrawingMode(B_OP_OVER);
|
|
if (fIcon != NULL) {
|
|
owner->MovePenTo(frame.left + kSmallHMargin,
|
|
(frame.top + frame.bottom - fIcon->Bounds().Height()) / 2.0);
|
|
owner->DrawBitmap(fIcon);
|
|
}
|
|
font_height fh;
|
|
owner->GetFontHeight(&fh);
|
|
|
|
BString label = Label();
|
|
|
|
owner->TruncateString(&label, B_TRUNCATE_END,
|
|
frame.Width() - IconWidth() - kSmallHMargin);
|
|
|
|
owner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
|
|
owner->DrawString(label,
|
|
BPoint(frame.left + IconWidth() + kSmallHMargin,
|
|
(frame.top + frame.bottom - fh.ascent - fh.descent) / 2.0
|
|
+ fh.ascent));
|
|
}
|
|
|
|
|
|
void
|
|
VolumeTab::DrawFocusMark(BView* owner, BRect frame)
|
|
{
|
|
frame.left += IconWidth();
|
|
BTab::DrawFocusMark(owner, frame);
|
|
}
|
|
|
|
|
|
VolumeTab::~VolumeTab()
|
|
{
|
|
delete fIcon;
|
|
delete fVolume;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
class ControlsView::VolumeTabView: public BTabView {
|
|
public:
|
|
VolumeTabView();
|
|
virtual ~VolumeTabView();
|
|
|
|
virtual void AttachedToWindow();
|
|
virtual void MessageReceived(BMessage* message);
|
|
virtual BRect TabFrame(int32 index) const;
|
|
|
|
BVolume* FindDeviceFor(dev_t device,
|
|
bool invoke = false);
|
|
|
|
private:
|
|
void _AddVolume(dev_t device);
|
|
void _RemoveVolume(dev_t device);
|
|
|
|
BVolumeRoster* fVolumeRoster;
|
|
};
|
|
|
|
|
|
ControlsView::VolumeTabView::VolumeTabView()
|
|
:
|
|
BTabView("volume_tabs", B_WIDTH_FROM_LABEL)
|
|
{
|
|
SetBorder(B_NO_BORDER);
|
|
}
|
|
|
|
|
|
ControlsView::VolumeTabView::~VolumeTabView()
|
|
{
|
|
fVolumeRoster->StopWatching();
|
|
delete fVolumeRoster;
|
|
}
|
|
|
|
|
|
BRect
|
|
ControlsView::VolumeTabView::TabFrame(int32 index) const
|
|
{
|
|
float height = BTabView::TabFrame(index).Height();
|
|
float x = 0.0f;
|
|
float width = 0.0f;
|
|
float minStringWidth = StringWidth("Haiku");
|
|
int32 countTabs = CountTabs();
|
|
|
|
// calculate the total width if no truncation is made at all
|
|
float averageWidth = Frame().Width() / countTabs;
|
|
|
|
// margins are the deltas with the average widths
|
|
float* margins = new float[countTabs];
|
|
for (int32 i = 0; i < countTabs; i++) {
|
|
float tabLabelWidth = StringWidth(TabAt(i)->Label());
|
|
if (tabLabelWidth < minStringWidth)
|
|
tabLabelWidth = minStringWidth;
|
|
|
|
float tabWidth = tabLabelWidth + 3.0f * kSmallHMargin
|
|
+ ((VolumeTab*)TabAt(i))->IconWidth();
|
|
margins[i] = tabWidth - averageWidth;
|
|
width += tabWidth;
|
|
}
|
|
|
|
// determine how much we should shave to show all tabs (truncating)
|
|
float toShave = width - Frame().Width();
|
|
|
|
if (toShave > 0.0f) {
|
|
// the thinest a tab can be to hold the minimum string
|
|
float minimumMargin = minStringWidth + 3.0f * kSmallHMargin
|
|
- averageWidth;
|
|
|
|
float averageToShave;
|
|
float oldToShave;
|
|
/*
|
|
we might have to do multiple passes because of the minimum
|
|
tab width we are imposing.
|
|
we could also fail to totally fit all tabs.
|
|
TODO: allow paging.
|
|
*/
|
|
|
|
do {
|
|
averageToShave = toShave / countTabs;
|
|
oldToShave = toShave;
|
|
for (int32 i = 0; i < countTabs; i++) {
|
|
float iconWidth = ((VolumeTab*)TabAt(i))->IconWidth();
|
|
float newMargin = max_c(margins[i] - averageToShave,
|
|
minimumMargin + iconWidth);
|
|
toShave -= margins[i] - newMargin;
|
|
margins[i] = newMargin;
|
|
}
|
|
} while (toShave > 0 && fabs(oldToShave - toShave) >= 1.0f);
|
|
}
|
|
|
|
for (int i = 0; i < index; i++)
|
|
x += averageWidth + margins[i];
|
|
|
|
float margin = margins[index];
|
|
delete[] margins;
|
|
|
|
return BRect(x, 0.0f, x + averageWidth + margin, height);
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::VolumeTabView::AttachedToWindow()
|
|
{
|
|
// Populate the menu with the persistent volumes.
|
|
fVolumeRoster = new BVolumeRoster();
|
|
|
|
BVolume tempVolume;
|
|
while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
|
|
if (!tempVolume.IsPersistent())
|
|
continue;
|
|
|
|
char name[B_PATH_NAME_LENGTH];
|
|
if (tempVolume.GetName(name) != B_OK)
|
|
continue;
|
|
|
|
if (strcmp(name, "system") == 0
|
|
|| strcmp(name, "config") == 0) {
|
|
// Don't include virtual volumes.
|
|
continue;
|
|
}
|
|
|
|
BVolume* volume = new BVolume(tempVolume);
|
|
VolumeView* volumeView = new VolumeView(name, volume);
|
|
VolumeTab* volumeTab = new VolumeTab(volume);
|
|
AddTab(volumeView, volumeTab);
|
|
}
|
|
|
|
// Begin watching mount and unmount events.
|
|
fVolumeRoster->StartWatching(BMessenger(this));
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::VolumeTabView::MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case B_NODE_MONITOR:
|
|
switch (message->FindInt32("opcode")) {
|
|
case B_DEVICE_MOUNTED:
|
|
_AddVolume(message->FindInt32("new device"));
|
|
break;
|
|
|
|
case B_DEVICE_UNMOUNTED:
|
|
_RemoveVolume(message->FindInt32("device"));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case kBtnCancel:
|
|
case kBtnRescan:
|
|
ViewForTab(Selection())->MessageReceived(message);
|
|
break;
|
|
|
|
case B_SIMPLE_DATA:
|
|
case B_REFS_RECEIVED:
|
|
{
|
|
entry_ref ref;
|
|
|
|
for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
|
|
BEntry entry(&ref, true);
|
|
BPath path;
|
|
entry.GetPath(&path);
|
|
dev_t device = dev_for_path(path.Path());
|
|
|
|
for (int j = 0; VolumeTab* item = (VolumeTab*)TabAt(j); j++) {
|
|
if (item->Volume()->Device() == device) {
|
|
Select(j);
|
|
((VolumeView*)(item->View()))->SetPath(path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BTabView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
BVolume*
|
|
ControlsView::VolumeTabView::FindDeviceFor(dev_t device, bool invoke)
|
|
{
|
|
BVolume* volume = NULL;
|
|
|
|
// Iterate through items looking for a BVolume representing this device.
|
|
for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
|
|
if (item->Volume()->Device() == device) {
|
|
volume = item->Volume();
|
|
if (invoke)
|
|
Select(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return volume;
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::VolumeTabView::_AddVolume(dev_t device)
|
|
{
|
|
// Make sure the volume is not already in the menu.
|
|
for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
|
|
if (item->Volume()->Device() == device)
|
|
return;
|
|
}
|
|
|
|
BVolume* volume = new BVolume(device);
|
|
|
|
VolumeTab* item = new VolumeTab(volume);
|
|
char name[B_PATH_NAME_LENGTH];
|
|
volume->GetName(name);
|
|
|
|
AddTab(new VolumeView(name, volume), item);
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::VolumeTabView::_RemoveVolume(dev_t device)
|
|
{
|
|
for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) {
|
|
if (item->Volume()->Device() == device) {
|
|
if (i == 0)
|
|
Select(1);
|
|
else
|
|
Select(i - 1);
|
|
RemoveTab(i);
|
|
delete item;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
ControlsView::ControlsView()
|
|
:
|
|
BView(NULL, B_WILL_DRAW)
|
|
{
|
|
SetLayout(new BGroupLayout(B_VERTICAL));
|
|
fVolumeTabView = new VolumeTabView();
|
|
AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
|
|
.Add(fVolumeTabView)
|
|
);
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::MessageReceived(BMessage* msg)
|
|
{
|
|
switch (msg->what) {
|
|
case B_SIMPLE_DATA:
|
|
case B_REFS_RECEIVED:
|
|
fVolumeTabView->MessageReceived(msg);
|
|
break;
|
|
|
|
case kBtnCancel:
|
|
case kBtnRescan:
|
|
fVolumeTabView->MessageReceived(msg);
|
|
break;
|
|
|
|
default:
|
|
BView::MessageReceived(msg);
|
|
}
|
|
}
|
|
|
|
|
|
ControlsView::~ControlsView()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::ShowInfo(const FileInfo* info)
|
|
{
|
|
((VolumeView*)fVolumeTabView->ViewForTab(
|
|
fVolumeTabView->Selection()))->ShowInfo(info);
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::EnableRescan()
|
|
{
|
|
((VolumeView*)fVolumeTabView->ViewForTab(
|
|
fVolumeTabView->Selection()))->EnableRescan();
|
|
}
|
|
|
|
|
|
void
|
|
ControlsView::EnableCancel()
|
|
{
|
|
((VolumeView*)fVolumeTabView->ViewForTab(
|
|
fVolumeTabView->Selection()))->EnableCancel();
|
|
}
|
|
|
|
|
|
BVolume*
|
|
ControlsView::FindDeviceFor(dev_t device, bool invoke)
|
|
{
|
|
return fVolumeTabView->FindDeviceFor(device, invoke);
|
|
}
|