haiku/src/apps/drivesetup/DiskView.cpp

613 lines
14 KiB
C++

/*
* Copyright 2007-2010 Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Dancsó Róbert <dancso.robert@d-rendszer.hu>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "DiskView.h"
#include <stdio.h>
#include <Application.h>
#include <Bitmap.h>
#include <DiskDeviceVisitor.h>
#include <Catalog.h>
#include <GroupLayout.h>
#include <HashMap.h>
#include <IconUtils.h>
#include <LayoutItem.h>
#include <LayoutUtils.h>
#include <Locale.h>
#include <PartitioningInfo.h>
#include <Path.h>
#include <Resources.h>
#include <String.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "icons.h"
#include "EncryptionUtils.h"
#include "MainWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DiskView"
using BPrivate::HashMap;
using BPrivate::HashKey32;
static const pattern kStripes = { { 0xc7, 0x8f, 0x1f, 0x3e,
0x7c, 0xf8, 0xf1, 0xe3 } };
static const float kLayoutInset = 6;
class PartitionView : public BView {
public:
PartitionView(const char* name, float weight, off_t offset,
int32 level, partition_id id, BPartition* partition)
:
BView(name, B_WILL_DRAW | B_SUPPORTS_LAYOUT | B_FULL_UPDATE_ON_RESIZE),
fID(id),
fWeight(weight),
fOffset(offset),
fLevel(level),
fSelected(false),
fMouseOver(false),
fGroupLayout(new BGroupLayout(B_HORIZONTAL, kLayoutInset))
{
SetLayout(fGroupLayout);
SetViewColor(B_TRANSPARENT_COLOR);
rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
base = tint_color(base, B_LIGHTEN_2_TINT);
base = tint_color(base, 1 + 0.13 * (level - 1));
SetLowColor(base);
SetHighColor(tint_color(base, B_DARKEN_1_TINT));
BFont font;
GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.85));
font.SetRotation(90.0);
SetFont(&font);
fGroupLayout->SetInsets(kLayoutInset, kLayoutInset + font.Size(),
kLayoutInset, kLayoutInset);
SetExplicitMinSize(BSize(font.Size() + 20, 30));
BVolume volume;
partition->GetVolume(&volume);
BVolume boot;
BVolumeRoster().GetBootVolume(&boot);
fBoot = volume == boot;
fReadOnly = volume.IsReadOnly();
fShared = volume.IsShared();
fEncrypted = false;
_ComputeFullName(partition, name);
fUsed = 100.0 / ((float)volume.Capacity()
/ (volume.Capacity() - volume.FreeBytes()));
if (fUsed < 0)
fUsed = 100.0;
fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGBA32);
fBFS = BString(partition->ContentType()) == "Be File System";
if (fBoot)
BIconUtils::GetVectorIcon(kLeaf, sizeof(kLeaf), fIcon);
else if (fEncrypted)
BIconUtils::GetVectorIcon(kEncrypted, sizeof(kEncrypted), fIcon);
else if (fReadOnly)
BIconUtils::GetVectorIcon(kReadOnly, sizeof(kReadOnly), fIcon);
else if (fShared)
BIconUtils::GetVectorIcon(kShared, sizeof(kShared), fIcon);
else if (fVirtual)
BIconUtils::GetVectorIcon(kFile, sizeof(kFile), fIcon);
else {
delete fIcon;
fIcon = NULL;
}
}
virtual void MouseDown(BPoint where)
{
BMessage message(MSG_SELECTED_PARTITION_ID);
message.AddInt32("partition_id", fID);
Window()->PostMessage(&message);
}
virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*)
{
uint32 buttons;
if (Window()->CurrentMessage()->FindInt32("buttons",
(int32*)&buttons) < B_OK)
buttons = 0;
_SetMouseOver(buttons == 0
&& (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW));
}
virtual void Draw(BRect updateRect)
{
if (fMouseOver) {
float tint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0;
SetHighColor(tint_color(HighColor(), tint));
SetLowColor(tint_color(LowColor(), tint));
}
BRect b(Bounds());
if (fSelected) {
if (fReadOnly)
SetHighColor(make_color(255, 128, 128));
else if (fBoot || fBFS)
SetHighColor(make_color(128, 255, 128));
else
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
StrokeRect(b, B_SOLID_HIGH);
b.InsetBy(1, 1);
StrokeRect(b, B_SOLID_HIGH);
b.InsetBy(1, 1);
} else if (fLevel >= 0) {
StrokeRect(b, B_SOLID_HIGH);
b.InsetBy(1, 1);
}
if (CountChildren() == 0)
SetLowColor(make_color(255, 255, 255));
FillRect(b, B_SOLID_LOW);
if (fEncrypted && CountChildren() == 0) {
SetHighColor(make_color(192, 192, 192));
StrokeRect(b, B_SOLID_HIGH);
b.InsetBy(1, 1);
SetLowColor(make_color(224, 224, 0));
BRect e(BPoint(15, b.top), b.RightBottom());
e.InsetBy(1, 1);
FillRect(e, kStripes);
}
// prevent the text from moving when border width changes
if (!fSelected)
b.InsetBy(1, 1);
BRect used(b.LeftTop(), BSize(b.Width() / (100.0 / fUsed), b.Height()));
SetLowColor(make_color(172, 172, 255));
if (fReadOnly)
SetLowColor(make_color(255, 172, 172));
else if (fBoot || fBFS)
SetLowColor(make_color(190, 255, 190));
if (CountChildren() == 0)
FillRect(used, B_SOLID_LOW);
if (fIcon && CountChildren() == 0) {
BPoint where = b.RightBottom() - BPoint(fIcon->Bounds().Width(),
fIcon->Bounds().Height());
SetDrawingMode(B_OP_OVER);
DrawBitmap(fIcon, where);
SetDrawingMode(B_OP_COPY);
}
float width;
BFont font;
GetFont(&font);
font_height fh;
font.GetHeight(&fh);
// draw the partition label, but only if we have no child partition
// views
BPoint textOffset;
if (CountChildren() > 0) {
font.SetRotation(0.0);
SetFont(&font);
width = b.Width();
textOffset = b.LeftTop();
textOffset.x += 3;
textOffset.y += ceilf(fh.ascent);
} else {
width = b.Height();
textOffset = b.LeftBottom();
textOffset.x += ceilf(fh.ascent);
}
BString name(Name());
font.TruncateString(&name, B_TRUNCATE_END, width);
SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT));
DrawString(name.String(), textOffset);
}
float Weight() const
{
return fWeight;
}
off_t Offset() const
{
return fOffset;
}
int32 Level() const
{
return fLevel;
}
BGroupLayout* GroupLayout() const
{
return fGroupLayout;
}
void SetSelected(bool selected)
{
if (fSelected == selected)
return;
fSelected = selected;
Invalidate();
}
bool IsEncrypted()
{
return fEncrypted;
}
private:
void _SetMouseOver(bool mouseOver)
{
if (fMouseOver == mouseOver)
return;
fMouseOver = mouseOver;
Invalidate();
}
void _ComputeFullName(BPartition* partition, const char* name)
{
BString fullName(name);
fVirtual = partition->Device()->IsFile();
if (fVirtual)
fullName << " (" << B_TRANSLATE("Virtual") << ")";
while (partition != NULL) {
BPath path;
partition->GetPath(&path);
const char* encrypter = EncryptionType(path.Path());
if (encrypter != NULL) {
fullName << " (" << encrypter << ")";
fEncrypted = true;
break;
}
partition = partition->Parent();
}
SetName(fullName);
}
private:
partition_id fID;
float fWeight;
off_t fOffset;
int32 fLevel;
bool fSelected;
bool fMouseOver;
BGroupLayout* fGroupLayout;
bool fBoot;
bool fReadOnly;
bool fShared;
bool fEncrypted;
bool fVirtual;
float fUsed;
bool fBFS;
BBitmap* fIcon;
};
class DiskView::PartitionLayout : public BDiskDeviceVisitor {
public:
PartitionLayout(BView* view, SpaceIDMap& spaceIDMap)
:
fView(view),
fViewMap(),
fSelectedPartition(-1),
fSpaceIDMap(spaceIDMap)
{
}
virtual bool Visit(BDiskDevice* device)
{
const char* name;
if (device->Name() != NULL && device->Name()[0] != '\0')
name = device->Name();
else
name = B_TRANSLATE("Device");
if (BString(device->ContentName()).Length() > 0)
name = device->ContentName();
PartitionView* view = new PartitionView(name, 1.0,
device->Offset(), 0, device->ID(), device);
fViewMap.Put(device->ID(), view);
fView->GetLayout()->AddView(view);
_AddSpaces(device, view);
return false;
}
virtual bool Visit(BPartition* partition, int32 level)
{
if (!partition->Parent()
|| !fViewMap.ContainsKey(partition->Parent()->ID()))
return false;
// calculate size factor within parent frame
off_t offset = partition->Offset();
// off_t parentOffset = partition->Parent()->Offset();
off_t size = partition->Size();
off_t parentSize = partition->Parent()->Size();
double scale = (double)size / parentSize;
BString name = partition->ContentName();
if (name.Length() == 0) {
if (partition->CountChildren() > 0)
name << partition->Type();
else {
char buffer[64];
snprintf(buffer, 64, B_TRANSLATE("Partition %ld"),
partition->ID(), partition);
name << buffer;
}
}
partition_id id = partition->ID();
PartitionView* view = new PartitionView(name.String(), scale, offset,
level, id, partition);
view->SetSelected(id == fSelectedPartition);
PartitionView* parent = fViewMap.Get(partition->Parent()->ID());
BGroupLayout* layout = parent->GroupLayout();
layout->AddView(_FindInsertIndex(view, layout), view, scale);
fViewMap.Put(partition->ID(), view);
_AddSpaces(partition, view);
return false;
}
void SetSelectedPartition(partition_id id)
{
if (fSelectedPartition == id)
return;
if (fViewMap.ContainsKey(fSelectedPartition)) {
PartitionView* view = fViewMap.Get(fSelectedPartition);
view->SetSelected(false);
}
fSelectedPartition = id;
if (fViewMap.ContainsKey(fSelectedPartition)) {
PartitionView* view = fViewMap.Get(fSelectedPartition);
view->SetSelected(true);
}
}
void Unset()
{
fViewMap.Clear();
}
private:
void _AddSpaces(BPartition* partition, PartitionView* parentView)
{
// add any available space on the partition
BPartitioningInfo info;
if (partition->GetPartitioningInfo(&info) >= B_OK) {
off_t parentSize = partition->Size();
off_t offset;
off_t size;
for (int32 i = 0;
info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
i++) {
// TODO: remove again once Disk Device API is fixed
if (!is_valid_partitionable_space(size))
continue;
//
double scale = (double)size / parentSize;
partition_id id
= fSpaceIDMap.SpaceIDFor(partition->ID(), offset);
PartitionView* view = new PartitionView(
B_TRANSLATE("Empty space"), scale, offset,
parentView->Level() + 1, id, partition);
fViewMap.Put(id, view);
BGroupLayout* layout = parentView->GroupLayout();
layout->AddView(_FindInsertIndex(view, layout), view, scale);
}
}
}
int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const
{
int32 insertIndex = 0;
int32 count = layout->CountItems();
for (int32 i = 0; i < count; i++) {
BLayoutItem* item = layout->ItemAt(i);
if (!item)
break;
PartitionView* sibling
= dynamic_cast<PartitionView*>(item->View());
if (sibling && sibling->Offset() > view->Offset())
break;
insertIndex++;
}
return insertIndex;
}
typedef HashKey32<partition_id> PartitionKey;
typedef HashMap<PartitionKey, PartitionView* > PartitionViewMap;
BView* fView;
PartitionViewMap fViewMap;
partition_id fSelectedPartition;
SpaceIDMap& fSpaceIDMap;
};
// #pragma mark -
DiskView::DiskView(const BRect& frame, uint32 resizeMode,
SpaceIDMap& spaceIDMap)
:
Inherited(frame, "diskview", resizeMode,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fDiskCount(0),
fDisk(NULL),
fSpaceIDMap(spaceIDMap),
fPartitionLayout(new PartitionLayout(this, fSpaceIDMap))
{
BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset);
SetLayout(layout);
SetViewColor(B_TRANSPARENT_COLOR);
SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_2_TINT);
SetLowUIColor(B_PANEL_BACKGROUND_COLOR, 1.221f);
#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
PartitionView* view;
float scale = 1.0;
view = new PartitionView("Disk", scale, 0, 0, -1);
layout->AddView(view, scale);
layout = view->GroupLayout();
scale = 0.3;
view = new PartitionView("Primary", scale, 1, 50, -1);
layout->AddView(view, scale);
scale = 0.7;
view = new PartitionView("Extended", scale, 1, 100, -1);
layout->AddView(view, scale);
layout = view->GroupLayout();
scale = 0.2;
view = new PartitionView("Logical", scale, 2, 200, -1);
layout->AddView(view, scale);
scale = 0.5;
view = new PartitionView("Logical", scale, 2, 250, -1);
layout->AddView(view, scale);
scale = 0.005;
view = new PartitionView("Logical", scale, 2, 290, -1);
layout->AddView(view, scale);
scale = 0.295;
view = new PartitionView("Logical", scale, 2, 420, -1);
layout->AddView(view, scale);
#endif
}
DiskView::~DiskView()
{
SetDisk(NULL, -1);
delete fPartitionLayout;
}
void
DiskView::Draw(BRect updateRect)
{
BRect bounds(Bounds());
if (fDisk)
return;
FillRect(bounds, kStripes);
const char* helpfulMessage;
if (fDiskCount == 0)
helpfulMessage = B_TRANSLATE("No disk devices have been recognized.");
else
helpfulMessage =
B_TRANSLATE("Select a partition from the list below.");
float width = StringWidth(helpfulMessage);
font_height fh;
GetFontHeight(&fh);
BRect messageBounds(bounds);
messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0,
(bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0);
FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW);
rgb_color color = LowColor();
if (color.Brightness() > 100)
color = tint_color(color, B_DARKEN_4_TINT);
else
color = tint_color(color, B_LIGHTEN_2_TINT);
SetHighColor(color);
BPoint textOffset;
textOffset.x = messageBounds.left + fh.ascent;
textOffset.y = (messageBounds.top + messageBounds.bottom
- fh.ascent - fh.descent) / 2 + fh.ascent;
DrawString(helpfulMessage, textOffset);
}
void
DiskView::SetDiskCount(int32 count)
{
fDiskCount = count;
if (count == 1) {
BMessage message(MSG_SELECTED_PARTITION_ID);
message.AddInt32("partition_id", 0);
Window()->PostMessage(&message);
}
}
void
DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition)
{
if (fDisk != disk) {
fDisk = disk;
ForceUpdate();
}
fPartitionLayout->SetSelectedPartition(selectedPartition);
}
void
DiskView::ForceUpdate()
{
while (BView* view = ChildAt(0)) {
view->RemoveSelf();
delete view;
}
fPartitionLayout->Unset();
if (fDisk) {
// we need to prepare the disk for modifications, otherwise
// we cannot get information about available spaces on the
// device or any of its child partitions
// TODO: cancelling modifications here is of course undesired
// once we hold off the real modifications until an explicit
// command to write them to disk...
bool prepared = fDisk->PrepareModifications() == B_OK;
fDisk->VisitEachDescendant(fPartitionLayout);
if (prepared)
fDisk->CancelModifications();
}
Invalidate();
}