haiku/src/apps/diskusage/ControlsView.cpp

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);
}