Imported the nice disk usage tool "Guido" into the repo as "DiskUsage". Guido

was originally written by Mike Steed. The code was freely available from his
website at one point, which I am no longer able to find. I believe the license
was a kind of public domain, at least compatible with our MIT/X11 license.

The original code was well written with it's own coding style which was not
very far from the Haiku coding style. I have hopefully adopted it completely
to our coding style. While I did this, I came across several places with
comments for a minor bug which I was able to resolve all. I also found ways
to simplify certain places and I elliminated all non-necessary "inter-class
knowledge" which was used to directly access members of other classes which
should have been private.

All in all, this should be a nice little addition, a useful tool which I found
handy a lot of times. If I should not have added this without first consulting
everyone, please voice your disagreement, and it can be removed it again. It
is not part of the image yet.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27357 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-09-07 14:18:01 +00:00
parent c909ca65bf
commit f75a7bf508
25 changed files with 2985 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
<html>
<head>
<title>DiskUsage</title>
</head>
<body bgcolor=#ffffff>
<br>
<br>
<h2><img src="icon.png">&nbsp;<code>DiskUsage</code><br>
Volume Utilization Visualizer</h2>
<hr>
<h3>What is it?</h3>
<code>DiskUsage</code> shows a graphical representation of how the space on your
volumes is utilized. It uses concentric circles to represent different levels
in the file system hierarchy. It is useful for answering the question, "Where
has all my disk space gone?" And sometimes it's fun just to look at the
pictures. <p>
Here is a picture of <code>DiskUsage</code> in action, so to speak. <p>
<center><img src="home.png"></center> <p>
The circle in the middle represents my <code>/boot/home</code> directory.
Each segment of the ring immediately outside that circle represents a file or
directory within <code>/boot/home</code>. And so on.
<h3>Usage</h3>
<strong>Startup.</strong> You can launch <code>DiskUsage</code> from the
<code>Tracker</code>, either directly or by dropping a volume or folder on its
icon; or from the <code>Terminal</code>, optionally passing the names of one
or more files or folders as arguments. If you launch <code>DiskUsage</code>
without specifying a volume, it will wait until you drop one or more volumes
or folders on its window or select a volume from the menu. <p>
While <code>DiskUsage</code> is scanning a volume, it displays a status bar
showing its progress. Multiple volumes are scanned in parallel (each in its
own thread). You can view the progress on a volume by selecting that volume
from the menu. <p>
<strong>The main display.</strong> When <code>DiskUsage</code> finishes scanning
a volume, it displays a graphical representation of the volume's files and
directories. As you move the mouse over each part of the picture, information
about each file or directory appears in the area at the bottom of the window.
You can resize the window to increase or decrease the number of levels that
<code>DiskUsage</code> displays. <p>
<strong>Zooming.</strong> You can "zoom" in on a directory by clicking the
primary mouse button on it. The picture is redrawn with that directory in the
center circle. You can zoom out one level by clicking the center circle. At
the outermost level, the center circle represents the volume itself, showing
free and used space as pie slices. <p>
<strong>Pop-up menu.</strong> Clicking the secondary mouse button on a part
of the picture pops up a menu with <code>Tracker</code>-like options
applicable to that file or directory. For directories, the menu includes an
option to rescan that directory and its subdirectories (not the entire
volume). This is useful since <code>DiskUsage</code> does not monitor changes
to the file system. <p>
<strong>Drag-and-drop.</strong> You can drag files and directories from
<code>DiskUsage</code> to other applications (or to the desktop, the trash, etc.)
with the primary mouse button. You can drop volumes and folders on
<code>DiskUsage</code>'s window to zoom directly to them. <p>
<strong>Other controls.</strong> The buttons in the top right corner of the
<code>DiskUsage</code> window rescan the selected volume and display the
documentation (this file). <p>
<strong>Notes:</strong>
<ul>
<li> If the graphical representation of a file or directory comprises less
than about two degrees of a circle, it is excluded from the display.
<li> The number of files that <code>DiskUsage</code> reports for a directory
includes files in subdirectories too. A directory counts as a file.
<li> <code>DiskUsage</code> ignores symbolic links.
<li> If you don't like the colors <code>DiskUsage</code> uses, you can change
them with your favorite resource editor.
</ul>
<h3>Credits, etc.</h3>
Thanks to Steffen Gerlach for creating the Windows
<a href="http://www.steffengerlach.de/freeware/index.html">Scanner</a>
utility, from which I took the idea for using concentric circles to represent
disk usage. <p>
Thanks to the <a href="http://www.vim.org/">Vim</a> development team for
creating the best programmer's editor on the planet, with which
<code>DiskUsage</code>'s original code and documentation were lovingly
hand-crafted. <p>
Thanks to the folks at <u><font color=#0000ff>B</font><font
color=#ff0000>e</font></u> for creating such a kick-butt OS. It has made
programming fun again. <p>
Thanks to Mike Steed for doing the programming. All the code is original. <p>
<code>DiskUsage</code> used to be called <code>guido</code> by it's original
author. The name <code>guido</code> originated from GUI + <code>du</code>
-- the command-line disk usage utility. Plus, one of his favorite languages,
<a href="http://www.python.org/">Python</a>, was created by a fine programmer
named Guido.
<h3>Known problems</h3>
<ul>
<li> You can drop multiple volumes or folders on the icon, but
<code>DiskUsage</code> sees only one of them. Why?
<li> Dropping a floppy disk volume on <code>DiskUsage</code>'s window works, but
dropping it on the icon does not work (unless you force it by holding the Ctrl
key while you drag). <em>Why?!</em>
</ul>
<h3>License</h3>
<code>DiskUsage</code> is Copyright (c) 1999 Mike Steed. You are free to use
and distribute this software as long as it is accompanied by this documentation
and copyright notice. The software comes with no warranty, etc. <p>
<hr>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

+1
View File
@@ -13,6 +13,7 @@ SubInclude HAIKU_TOP src apps cortex ;
SubInclude HAIKU_TOP src apps deskbar ;
SubInclude HAIKU_TOP src apps deskcalc ;
SubInclude HAIKU_TOP src apps diskprobe ;
SubInclude HAIKU_TOP src apps diskusage ;
SubInclude HAIKU_TOP src apps drivesetup ;
SubInclude HAIKU_TOP src apps expander ;
SubInclude HAIKU_TOP src apps fontdemo ;
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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 "App.h"
#include <stdio.h>
#include <File.h>
#include <FindDirectory.h>
#include <Node.h>
#include <Path.h>
#include "Common.h"
#include "MainWindow.h"
App::App()
: BApplication(kAppSignature),
fResources(read_resources(kAppSignature)),
fMainWindow(NULL)
{
}
App::~App()
{
delete fResources;
}
void
App::ArgvReceived(int32 argc, char** argv)
{
BMessage refsReceived(B_REFS_RECEIVED);
for (int32 i = 1; i < argc; i++) {
BEntry entry(argv[i], true);
entry_ref ref;
if (entry.GetRef(&ref) == B_OK)
refsReceived.AddRef("refs", &ref);
}
if (refsReceived.HasRef("refs"))
PostMessage(&refsReceived);
}
void
App::RefsReceived(BMessage* message)
{
fMainWindow->PostMessage(message);
}
void
App::ReadyToRun()
{
BRect frame;
BPath path;
BFile settingsFile;
BMessage settings;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|| path.Append("DiskUsage") != B_OK
|| settingsFile.SetTo(path.Path(), B_READ_ONLY) != B_OK
|| settings.Unflatten(&settingsFile) != B_OK
|| settings.FindRect("window frame", &frame) != B_OK) {
// use default window frame
frame.Set(0, 0, kDefaultPieSize, kDefaultPieSize);
frame.OffsetTo(50, 50);
}
fMainWindow = new MainWindow(frame);
}
bool
App::QuitRequested()
{
// Save the settings.
BPath path;
BFile settingsFile;
BMessage settings;
if (settings.AddRect("window frame", fMainWindow->Frame()) != B_OK
|| find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|| path.Append("DiskUsage") != B_OK
|| settingsFile.SetTo(path.Path(), B_WRITE_ONLY) != B_OK
|| settings.Flatten(&settingsFile) != B_OK) {
fprintf(stderr, "Failed to write application settings.\n");
}
return BApplication::QuitRequested();
}
// #pragma mark -
int
main()
{
App app;
app.Run();
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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.
*/
#ifndef APP_H
#define APP_H
#include <Application.h>
class MainWindow;
class App: public BApplication {
public:
App();
virtual ~App();
virtual void ArgvReceived(int32 argc, char** argv);
virtual void RefsReceived(BMessage* message);
virtual void ReadyToRun();
virtual bool QuitRequested();
private:
BResources* fResources;
MainWindow* fMainWindow;
};
#endif // APP_H
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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.
*/
#define ASSIGN_RESOURCES
#include "Common.h"
#include <stdio.h>
#include <string.h>
#include <Application.h>
#include <FindDirectory.h>
#include <Roster.h>
#include <Path.h>
BResources*
read_resources(const char* appSignature)
{
status_t ret = be_app->GetAppInfo(&kAppInfo);
if (ret != B_OK) {
fprintf(stderr, "Failed to init application info: %s\n",
strerror(ret));
exit(1);
}
BFile file(&kAppInfo.ref, O_RDONLY);
ret = file.InitCheck();
if (ret != B_OK) {
fprintf(stderr, "Failed to init application file to read resources: "
"%s\n", strerror(ret));
exit(1);
}
BResources* r = new BResources(&file);
#define LoadString(n) (char*)r->LoadResource(B_STRING_TYPE, n, &ignore)
#define LoadColor(n) *(rgb_color*)r->LoadResource(B_RGB_COLOR_TYPE, n, &ignore)
#define LoadUint8(n) *(uint8*)r->LoadResource(B_UINT8_TYPE, n, &ignore)
size_t ignore;
kVolMenuLabel = LoadString("STR_VM_LABEL");
kOneFile = LoadString("STR_1_FILE");
kManyFiles = LoadString("STR_N_FILES");
kStrRescan = LoadString("STR_RESCAN");
kStrScanningX = LoadString("STR_SCN_X");
kStrUnavail = LoadString("STR_UNAVAIL");
kVolMenuDefault = LoadString("STR_VM_DFLT");
kVolPrompt = LoadString("STR_VPROMPT");
kMenuGetInfo = LoadString("STR_M_INFO");
kMenuOpen = LoadString("STR_M_OPEN");
kMenuOpenWith = LoadString("STR_M_OPENW");
kMenuNoApps = LoadString("STR_M_NAPPS");
kInfoSize = LoadString("STR_SIZE");
kInfoInFiles = LoadString("STR_INFILES");
kInfoCreated = LoadString("STR_MADE");
kInfoModified = LoadString("STR_MOD");
kInfoTimeFmt = LoadString("STR_TIMEFMT");
kInfoKind = LoadString("STR_KIND");
kInfoPath = LoadString("STR_PATH");
kWindowColor = ui_color(B_PANEL_BACKGROUND_COLOR);
kOutlineColor = LoadColor("RGB_PIE_OL");
kPieBGColor = LoadColor("RGB_PIE_BG");
kEmptySpcColor = LoadColor("RGB_PIE_MT");
kBasePieColorCount = LoadUint8("N_PIE_COLORS");
kBasePieColor = new rgb_color[kBasePieColorCount];
for (int i = 0; i < kBasePieColorCount; i++) {
char colorName[16] = "RGB_PIE_n";
colorName[8] = '1' + i;
kBasePieColor[i] = LoadColor(colorName);
}
// Get a reference to the help file.
BPath path;
if (find_directory(B_BEOS_DOCUMENTATION_DIRECTORY, &path) == B_OK
&& path.Append(kHelpFileName) == B_OK) {
printf("help file =? %s\n", path.Path());
BEntry entry(path.Path());
kFoundHelpFile = entry.Exists();
entry.GetRef(&kHelpFileRef);
} else
kFoundHelpFile = false;
return r;
}
void
size_to_string(off_t byteCount, char* name)
{
struct {
off_t limit;
float divisor;
char* format;
} scale[] = {
{ 0x100000, 1024.0, "%.2f KB" },
{ 0x40000000, 1048576.0, "%.2f MB" },
{ 0x10000000000ull, 1073741824.0, "%.2f GB" },
{ 0x4000000000000ull, 1.09951162778e+12, "%.2f TB" }
};
if (byteCount < 1024) {
sprintf(name, "%lld bytes", byteCount);
} else {
int i = 0;
while (byteCount >= scale[i].limit)
i++;
sprintf(name, scale[i].format, byteCount / scale[i].divisor);
}
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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.
*/
#ifndef COMMON_H
#define COMMON_H
#include <Entry.h>
#include <GraphicsDefs.h>
#include <Resources.h>
#include <Roster.h>
#ifdef ASSIGN_RESOURCES
# define EXTERN
# define EQ(x) =x
#else
# define EXTERN extern
# define EQ(x)
#endif
#define PUBLIC_CONST extern const
// Resources
EXTERN char* kStrRescan;
EXTERN char* kStrScanningX;
EXTERN char* kStrUnavail;
EXTERN char* kVolMenuLabel;
EXTERN char* kVolMenuDefault;
EXTERN char* kVolPrompt;
EXTERN char* kOneFile;
EXTERN char* kManyFiles;
EXTERN char* kMenuGetInfo;
EXTERN char* kMenuOpen;
EXTERN char* kMenuOpenWith;
EXTERN char* kMenuNoApps;
EXTERN char* kInfoSize;
EXTERN char* kInfoInFiles;
EXTERN char* kInfoCreated;
EXTERN char* kInfoModified;
EXTERN char* kInfoTimeFmt;
EXTERN char* kInfoKind;
EXTERN char* kInfoPath;
EXTERN rgb_color kWindowColor;
EXTERN rgb_color kOutlineColor;
EXTERN rgb_color kPieBGColor;
EXTERN rgb_color kEmptySpcColor;
EXTERN int kBasePieColorCount;
EXTERN rgb_color *kBasePieColor;
// Non-resources :)
PUBLIC_CONST char* kAppSignature EQ("application/x-vnd.haiku-diskusage");
PUBLIC_CONST char* kHelpFileName EQ("apps/diskusage/DiskUsage.html");
PUBLIC_CONST char* kPieRectAttrName EQ("mainrect");
PUBLIC_CONST char* kHelpBtnLabel EQ("?");
PUBLIC_CONST char* kEmptyStr EQ("");
PUBLIC_CONST char* kNameVolPtr EQ("vol");
PUBLIC_CONST char* kNameFilePtr EQ("file");
PUBLIC_CONST float kSmallHMargin EQ(5.0);
PUBLIC_CONST float kSmallVMargin EQ(2.0);
PUBLIC_CONST float kButtonMargin EQ(20.0);
PUBLIC_CONST float kMinButtonWidth EQ(60.0);
PUBLIC_CONST float kProgBarWidth EQ(150.0);
PUBLIC_CONST float kProgBarHeight EQ(16.0);
PUBLIC_CONST float kReportInterval EQ(2.5);
PUBLIC_CONST float kDefaultPieSize EQ(400.0);
PUBLIC_CONST float kPieCenterSize EQ(80.0);
PUBLIC_CONST float kPieRingSize EQ(20.0);
PUBLIC_CONST float kPieInnerMargin EQ(10.0);
PUBLIC_CONST float kPieOuterMargin EQ(10.0);
PUBLIC_CONST float kMinSegmentSpan EQ(2.0);
PUBLIC_CONST int kLightenFactor EQ(0x12);
PUBLIC_CONST float kDragThreshold EQ(5.0);
EXTERN app_info kAppInfo;
EXTERN entry_ref kHelpFileRef;
EXTERN bool kFoundHelpFile;
#define kMenuSelectVol 'gMSV'
#define kBtnRescan 'gBRF'
#define kBtnHelp 'gHLP'
#define kScanRefresh 'gSRF'
#define kScanProgress 'gSPR'
#define kScanDone 'gSDN'
// support functions
#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))
#define deg2rad(x) (2.0 * M_PI * (x) / 360.0)
#define rad2deg(x) (360.0 * (x) / (2.0 * M_PI))
BResources* read_resources(const char* appSignature);
void size_to_string(off_t byteCount, char* name);
#endif // COMMON_H
+298
View File
@@ -0,0 +1,298 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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 <Bitmap.h>
#include <Box.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <NodeMonitor.h>
#include <PopUpMenu.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#include "Common.h"
class VolumeMenuItem: public BMenuItem {
public:
VolumeMenuItem(BVolume* volume, BMessage* message);
virtual ~VolumeMenuItem();
virtual void GetContentSize(float* width, float* height);
virtual void DrawContent();
BVolume* Volume() const
{ return fVolume; }
status_t Invoke()
{ return BMenuItem::Invoke(); }
private:
BBitmap* fIcon;
BVolume* fVolume;
};
VolumeMenuItem::VolumeMenuItem(BVolume* volume, BMessage* message)
: BMenuItem(kEmptyStr, message),
fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
fVolume(volume)
{
char name[B_PATH_NAME_LENGTH];
fVolume->GetName(name);
SetLabel(name);
if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
delete fIcon;
fIcon = NULL;
}
}
VolumeMenuItem::~VolumeMenuItem()
{
delete fIcon;
delete fVolume;
}
void
VolumeMenuItem::GetContentSize(float* width, float* height)
{
*width = be_plain_font->StringWidth(Label());
struct font_height fh;
be_plain_font->GetHeight(&fh);
float fontHeight = fh.ascent + fh.descent + fh.leading;
if (fIcon) {
*height = max(fontHeight, fIcon->Bounds().Height());
*width += fIcon->Bounds().Width() + kSmallHMargin;
} else
*height = fontHeight;
}
void
VolumeMenuItem::DrawContent()
{
if (fIcon) {
Menu()->SetDrawingMode(B_OP_OVER);
Menu()->MovePenBy(0.0, -1.0);
Menu()->DrawBitmap(fIcon);
Menu()->SetDrawingMode(B_OP_COPY);
Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0);
}
BMenuItem::DrawContent();
}
// #pragma mark -
class ControlsView::VolumePopup: public BMenuField {
public:
VolumePopup(BRect r);
virtual ~VolumePopup();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message);
BVolume* FindDeviceFor(dev_t device,
bool invoke = false);
private:
void _AddVolume(dev_t device);
void _RemoveVolume(dev_t device);
BVolumeRoster* fVolumeRoster;
};
ControlsView::VolumePopup::VolumePopup(BRect r)
: BMenuField(r, NULL, kVolMenuLabel, new BPopUpMenu(kVolMenuDefault),
false, B_FOLLOW_LEFT)
{
SetViewColor(kWindowColor);
SetLowColor(kWindowColor);
SetDivider(kSmallHMargin + StringWidth(kVolMenuLabel));
// Populate the menu with the persistent volumes.
fVolumeRoster = new BVolumeRoster();
BVolume tempVolume;
while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
if (tempVolume.IsPersistent()) {
BVolume* volume = new BVolume(tempVolume);
BMessage* message = new BMessage(kMenuSelectVol);
message->AddPointer(kNameVolPtr, volume);
VolumeMenuItem *item = new VolumeMenuItem(volume, message);
Menu()->AddItem(item);
}
}
}
ControlsView::VolumePopup::~VolumePopup()
{
fVolumeRoster->StopWatching();
delete fVolumeRoster;
}
void
ControlsView::VolumePopup::AttachedToWindow()
{
// Begin watching mount and unmount events.
fVolumeRoster->StartWatching(BMessenger(this));
}
void
ControlsView::VolumePopup::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;
default:
BMenuField::MessageReceived(message);
break;
}
}
BVolume*
ControlsView::VolumePopup::FindDeviceFor(dev_t device, bool invoke)
{
BVolume* volume = NULL;
// Iterate through items looking for a BVolume representing this device.
for (int i = 0; VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); i++) {
if (item->Volume()->Device() == device) {
volume = item->Volume();
if (invoke)
item->Invoke();
break;
}
}
return volume;
}
void
ControlsView::VolumePopup::_AddVolume(dev_t device)
{
// Make sure the volume is not already in the menu.
for (int i = 0; i < Menu()->CountItems(); i++) {
VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
if (item->Volume()->Device() == device)
return;
}
// Add the newly mounted volume to the menu.
BVolume* volume = new BVolume(device);
BMessage* message = new BMessage(kMenuSelectVol);
message->AddPointer(kNameVolPtr, volume);
VolumeMenuItem* item = new VolumeMenuItem(volume, message);
Menu()->AddItem(item);
}
void
ControlsView::VolumePopup::_RemoveVolume(dev_t device)
{
for (int i = 0; i < Menu()->CountItems(); i++) {
VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
if (item->Volume()->Device() == device) {
// If the volume being removed is currently selected, prompt for a
// different volume.
if (item->IsMarked()) {
// update the displayed volume label now that there is no marked
// item:
Menu()->Superitem()->SetLabel(kVolMenuDefault);
BMessage messae(kMenuSelectVol);
messae.AddPointer(kNameVolPtr, NULL);
Window()->PostMessage(&messae);
}
Menu()->RemoveItem(item);
return;
}
}
}
// #pragma mark -
ControlsView::ControlsView(BRect r)
: BView(r, NULL, B_FOLLOW_LEFT_RIGHT, 0)
{
SetViewColor(kWindowColor);
r.top += kSmallVMargin;
r.right -= kSmallHMargin;
float buttonWidth = kButtonMargin + StringWidth(kHelpBtnLabel);
r.left = r.right - buttonWidth;
BButton* helpButton = new BButton(r, NULL, kHelpBtnLabel, new BMessage(kBtnHelp),
B_FOLLOW_RIGHT);
if (!kFoundHelpFile)
helpButton->SetEnabled(false);
r.right = r.left - kSmallHMargin;
buttonWidth = kButtonMargin + StringWidth(kStrRescan);
r.left = r.right - max(kMinButtonWidth, buttonWidth);
fRescanButton = new BButton(r, NULL, kStrRescan, new BMessage(kBtnRescan),
B_FOLLOW_RIGHT);
fVolumePopup = new VolumePopup(
BRect(kSmallHMargin, kSmallVMargin, r.left - kSmallHMargin, kSmallVMargin));
float width, height;
fRescanButton->GetPreferredSize(&width, &height);
ResizeTo(Bounds().Width(), height + 6.0);
// Horizontal divider
BRect r = Bounds();
r.top = r.bottom - 1.0;
r.left -= 5.0; r.right += 5.0;
BBox* divider = new BBox(r, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW);
AddChild(fVolumePopup);
AddChild(divider);
AddChild(fRescanButton);
AddChild(helpButton);
}
ControlsView::~ControlsView()
{
}
BVolume*
ControlsView::FindDeviceFor(dev_t device, bool invoke)
{
return fVolumePopup->FindDeviceFor(device, invoke);
}
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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.
*/
#ifndef CONTROLS_VIEW_H
#define CONTROLS_VIEW_H
#include <Button.h>
#include <View.h>
class BVolume;
class ControlsView: public BView {
public:
ControlsView(BRect r);
virtual ~ControlsView();
void SetRescanEnabled(bool enable)
{ fRescanButton->SetEnabled(enable); }
BVolume* FindDeviceFor(dev_t device,
bool invoke = false);
private:
class VolumePopup;
VolumePopup* fVolumePopup;
BButton* fRescanButton;
};
#endif // CONTROLS_VIEW_H
+78
View File
@@ -0,0 +1,78 @@
resource app_signature "application/x-vnd.haiku-diskusage";
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_FINAL,
internal = 0,
short_info = "1.0.1",
long_info = "1.0.1 (c) 1999 Mike Steed, (c) 2008 Haiku"
};
resource app_flags B_MULTIPLE_LAUNCH;
resource file_types message {
"types" = "application/x-vnd.Be-volume",
"types" = "application/x-vnd.Be-directory"
};
resource vector_icon {
$"6E6369660F03010000020016023AF81C3830AFBB29EB3E24FE4903D742949400"
$"8EFF47020016023AFDE4B927BB3BBB443D89C84A587E467D6A007DFFB0020016"
$"023BFC1538B2EABB58DE3E788D4A21874591FF0096FFB0020006023C96323A4D"
$"3FBAFC013D5A974B57A549844D00474747FFA5A0A002001602BC592FBB29A73C"
$"0CE4BD0B7C4892C04B7966007DFFD40200060238DBB4399733BC4A333BA54248"
$"6E6649EE7B00596756FFEBB2B203A7FF0003FF0000040180020306033E800000"
$"00000000003DC00049E00049200000007F7F7EB1EAEAFF4F9DA4020306033E80"
$"000000000000003DC00049E00049200000E3B78D86F4F8C9F4888C9802030603"
$"3E80000000000000003DC00049E00049200000A9525286F5C0D3F4B05A660203"
$"06033E80000000000000003DC00049E00049200000888C9886EBEDF2F4888C98"
$"020016033D400000000000000040000048800000000000FF9BD3FFCD0F0A0622"
$"3C2249445B5A3E5A3139250A05223C2246B54AC1D7393239250A055A31392539"
$"32C7D0BE1E5A3C0A043932B4CBC2564458C921BE980A04444B445B5A3E5A310A"
$"04223C2249445B444B08022743B814C1F10802264329440A05445D495D603E5A"
$"3B5B3F02043F2EC3882EBB2B2E2C3A2CBAB82CBFFF3F46BB2B46C38846523A52"
$"BFFF52BAB80604BF3E2E3E2EBAF2B8B32C3A2CBACC2CBDF9B84CBF1AB7F7BE90"
$"B84CBF1A3E3906033E4039C6D5BCA1C6D5BCA1C651BA7FC024B89AC392B8D4C0"
$"24B89A0604FE3F3AB915C00AB915C00ABA72C14E3F46BCBE46C33846C6DDBDF1"
$"C668C04FC6DDBDF10204423942BCBD42BD2E3F3AC0023ABEB13A3C393CBD2E3C"
$"BCBD3F38BEB138C002380A04233C444B59323926110A090108000A0001001001"
$"178400040A010101000A020102000A030103000A00030A0B0C1001178402040A"
$"0A010A000A0B010B000A0C010C000A00010D1001178400040A0D010D000A0401"
$"04000A050105000A0601061001178520040A0801073024B39901178200040A07"
$"01073020B2E601178200040A0E010E1815FF0117812204"
};
resource(101, "RGB_WIN") rgb_color { 0xDE, 0xDB, 0xDE, 0xFF };
resource(102, "RGB_PIE_OL") rgb_color { 0x80, 0x80, 0x80, 0xFF };
resource(103, "RGB_PIE_BG") rgb_color { 0xFF, 0xFF, 0xFF, 0xFF };
resource(104, "RGB_PIE_MT") rgb_color { 0xA0, 0xA0, 0xA0, 0xFF };
resource(105, "RGB_PIE_1") rgb_color { 0x00, 0x60, 0x60, 0xFF };
resource(106, "RGB_PIE_2") rgb_color { 0x00, 0x00, 0x68, 0xFF };
resource(107, "RGB_PIE_3") rgb_color { 0x60, 0x00, 0x60, 0xFF };
resource(108, "RGB_PIE_4") rgb_color { 0x68, 0x00, 0x00, 0xFF };
resource(101, "N_PIE_COLORS") (uint8)4;
resource(103, "STR_N_FILES") "%d files";
resource(102, "STR_1_FILE") "%d file";
resource(101, "STR_VM_LABEL") "Volume:";
resource(104, "STR_RESCAN") "Rescan";
resource(105, "STR_SCN_X") "Scanning ";
resource(106, "STR_UNAVAIL") "file unavailable";
resource(107, "STR_VM_DFLT") "(Select)";
resource(108, "STR_VPROMPT") "Select a volume to scan";
resource(109, "STR_M_INFO") "Get Info";
resource(110, "STR_M_OPEN") "Open";
resource(111, "STR_M_OPENW") "Open With";
resource(112, "STR_M_NAPPS") "no supporting apps";
resource(113, "STR_SIZE") "Size";
resource(114, "STR_INFILES") " in %d files";
resource(115, "STR_MADE") "Created";
resource(116, "STR_MOD") "Modified";
resource(117, "STR_TIMEFMT") "%a, %d %b %Y, %r";
resource(118, "STR_KIND") "Kind";
resource(119, "STR_PATH") "Path";
+174
View File
@@ -0,0 +1,174 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. 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 "InfoWindow.h"
#include <pair.h>
#include <stdio.h>
#include <time.h>
#include <vector>
#include <StringView.h>
#include <Bitmap.h>
#include <NodeInfo.h>
#include "Common.h"
#include "Snapshot.h"
LeftView::LeftView(BRect frame, BBitmap* icon)
: BView(frame, NULL, B_FOLLOW_NONE, B_WILL_DRAW),
fIcon(icon)
{
SetViewColor(tint_color(kWindowColor, B_LIGHTEN_1_TINT));
}
LeftView::~LeftView()
{
delete fIcon;
}
void
LeftView::Draw(BRect updateRect)
{
float right = Bounds().Width() - kSmallHMargin;
BRect iconRect(right - 31.0, kSmallVMargin, right, kSmallVMargin + 31.0);
if (updateRect.Intersects(iconRect)) {
SetDrawingMode(B_OP_OVER);
DrawBitmap(fIcon, iconRect);
}
}
InfoWin::InfoWin(BPoint p, FileInfo *f, BWindow* parent)
: BWindow(BRect(p, p), kEmptyStr, B_FLOATING_WINDOW_LOOK,
B_FLOATING_SUBSET_WINDOW_FEEL,
B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE)
{
AddToSubset(parent);
typedef pair<string, string> Item;
typedef vector<Item> InfoList;
char name[B_PATH_NAME_LENGTH];
strcpy(name, f->ref.name);
strcat(name, " info");
SetTitle(name);
InfoList info;
Item item;
// Size
size_to_string(f->size, name);
if (f->count > 0) {
// This is a directory.
char str[64];
sprintf(str, kInfoInFiles, f->count);
strcat(name, str);
}
info.push_back(Item(kInfoSize, name));
// Created & modified dates
BEntry entry(&f->ref);
time_t t;
entry.GetCreationTime(&t);
strftime(name, 64, kInfoTimeFmt, localtime(&t));
info.push_back(Item(kInfoCreated, name));
entry.GetModificationTime(&t);
strftime(name, 64, kInfoTimeFmt, localtime(&t));
info.push_back(Item(kInfoModified, name));
// Kind
BMimeType* type = f->Type();
type->GetShortDescription(name);
info.push_back(Item(kInfoKind, name));
delete type;
// Path
string path;
f->GetPath(path);
info.push_back(Item(kInfoPath, path));
// Icon
BBitmap *icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32);
entry_ref ref;
entry.GetRef(&ref);
BNodeInfo::GetTrackerIcon(&ref, icon, B_LARGE_ICON);
// Compute the window size and add the views.
BFont smallFont(be_plain_font);
smallFont.SetSize(floorf(smallFont.Size() * 0.95));
struct font_height fh;
smallFont.GetHeight(&fh);
float fontHeight = fh.ascent + fh.descent + fh.leading;
float leftWidth = 32.0;
float rightWidth = 0.0;
InfoList::iterator i = info.begin();
while (i != info.end()) {
float w = smallFont.StringWidth((*i).first.c_str()) + 2.0 * kSmallHMargin;
leftWidth = max(leftWidth, w);
w = smallFont.StringWidth((*i).second.c_str()) + 2.0 * kSmallHMargin;
rightWidth = max(rightWidth, w);
i++;
}
float winHeight = 32.0 + 4.0 * kSmallVMargin + 5.0 * (fontHeight + kSmallVMargin);
float winWidth = leftWidth + rightWidth;
ResizeTo(winWidth, winHeight);
LeftView *leftView = new LeftView(BRect(0.0, 0.0, leftWidth, winHeight), icon);
BView *rightView = new BView(
BRect(leftWidth + 1.0, 0.0, winWidth, winHeight), NULL,
B_FOLLOW_NONE, B_WILL_DRAW);
AddChild(leftView);
AddChild(rightView);
BStringView *sv = new BStringView(
BRect(kSmallHMargin, kSmallVMargin, rightView->Bounds().Width(), kSmallVMargin + 30.0),
NULL, f->ref.name, B_FOLLOW_ALL);
BFont largeFont(be_plain_font);
largeFont.SetSize(ceilf(largeFont.Size() * 1.1));
sv->SetFont(&largeFont);
rightView->AddChild(sv);
float y = 32.0 + 4.0 * kSmallVMargin;
i = info.begin();
while (i != info.end()) {
sv = new BStringView(
BRect(kSmallHMargin, y, leftView->Bounds().Width(), y + fontHeight),
NULL, (*i).first.c_str());
sv->SetFont(&smallFont);
sv->SetAlignment(B_ALIGN_RIGHT);
sv->SetHighColor(kBasePieColor[1]); // arbitrary
leftView->AddChild(sv);
sv = new BStringView(
BRect(kSmallHMargin, y, rightView->Bounds().Width(), y + fontHeight),
NULL, (*i).second.c_str());
sv->SetFont(&smallFont);
rightView->AddChild(sv);
y += fontHeight + kSmallVMargin;
i++;
}
Show();
}
InfoWin::~InfoWin()
{
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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.
*/
#ifndef INFO_WINDOW_H
#define INFO_WINDOW_H
#include <Window.h>
struct FileInfo;
class LeftView: public BView {
public:
LeftView(BRect frame, BBitmap* icon);
virtual ~LeftView();
virtual void Draw(BRect updateRect);
private:
BBitmap* fIcon;
};
class InfoWin: public BWindow {
public:
InfoWin(BPoint location, FileInfo* info,
BWindow* parent);
virtual ~InfoWin();
};
#endif // INFO_WINDOW_H
+16
View File
@@ -0,0 +1,16 @@
SubDir HAIKU_TOP src apps diskusage ;
Application DiskUsage :
App.cpp
Common.cpp
ControlsView.cpp
InfoWindow.cpp
MainWindow.cpp
PieView.cpp
Scanner.cpp
Snapshot.cpp
StatusView.cpp
: be $(TARGET_LIBSTDC++)
: DiskUsage.rdef
;
+171
View File
@@ -0,0 +1,171 @@
/*
* 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 "MainWindow.h"
#include <Application.h>
#include <Node.h>
#include <Roster.h>
#include <Screen.h>
#include "Common.h"
#include "ControlsView.h"
#include "PieView.h"
#include "StatusView.h"
const float kMinWinSize = 275.0;
MainWindow::MainWindow(BRect pieRect)
: BWindow(pieRect, "DiskUsage", B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE)
{
BRect r = Bounds();
r.bottom = r.top + 1.0;
fControlsView = new ControlsView(r);
float cvHeight = fControlsView->Bounds().Height();
AddChild(fControlsView);
r = Bounds();
r.top = r.bottom - 1.0;
fStatusView = new StatusView(r);
float svHeight = fStatusView->Bounds().Height();
float winHeight = pieRect.Height() + cvHeight + svHeight;
fStatusView->MoveTo(0.0, winHeight - svHeight);
ResizeTo(r.Width(), winHeight);
AddChild(fStatusView);
r = fControlsView->Frame();
r.top = r.bottom + 1.0;
r.bottom = fStatusView->Frame().top - 1.0;
fPieView = new PieView(r, this);
AddChild(fPieView);
Show();
// Note: The following code is semi-broken because BScreen::Frame()
// returns incorrect dimensions for the G200 in 1152x864 mode. I reported
// this bug, and Be said it's not a bug -- the Matrox driver actually uses
// a resolution of 1152x900 in that mode. Oh well.
Lock();
float extraHeight = fControlsView->Bounds().Height() + fStatusView->Bounds().Height();
float minHeight = kMinWinSize + extraHeight;
float maxHeight = BScreen(this).Frame().Height();
float maxWidth = maxHeight - extraHeight;
Unlock();
SetSizeLimits(kMinWinSize, maxWidth, minHeight, maxHeight);
}
MainWindow::~MainWindow()
{
}
void
MainWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kBtnRescan:
case kMenuSelectVol:
case B_REFS_RECEIVED:
fPieView->MessageReceived(message);
break;
case kBtnHelp:
be_roster->Launch(&kHelpFileRef);
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
MainWindow::Zoom(BPoint origin, float width, float height)
{
width = Frame().Width();
height = Frame().Height();
if (_FixAspectRatio(&width, &height))
SetZoomLimits(width, height);
BWindow::Zoom(Frame().LeftTop(), width, height);
}
void
MainWindow::FrameResized(float width, float height)
{
if (_FixAspectRatio(&width, &height))
ResizeTo(width, height);
}
bool
MainWindow::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
// #pragma mark -
void
MainWindow::ShowInfo(const FileInfo* info)
{
fStatusView->Show(info);
}
void
MainWindow::SetRescanEnabled(bool enabled)
{
fControlsView->SetRescanEnabled(enabled);
}
BVolume*
MainWindow::FindDeviceFor(dev_t device, bool invoke)
{
return fControlsView->FindDeviceFor(device, invoke);
}
// #pragma mark -
bool
MainWindow::_FixAspectRatio(float* width, float* height)
{
float ctrlViewHeight = fControlsView->Bounds().Height();
float statusViewHeight = fStatusView->Bounds().Height();
float newPieSize = *height - ctrlViewHeight - statusViewHeight;
if (*width < newPieSize)
newPieSize = *width;
float newWidth = newPieSize;
float newHeight = newPieSize + ctrlViewHeight + statusViewHeight;
if (*width != newWidth || *height != newHeight) {
*width = newWidth;
*height = newHeight;
return true;
}
return false;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* 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.
*/
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <Window.h>
class BVolume;
class ControlsView;
struct FileInfo;
class PieView;
class StatusView;
class MainWindow: public BWindow {
public:
MainWindow(BRect pieRect);
virtual ~MainWindow();
virtual void MessageReceived(BMessage* message);
virtual void Zoom(BPoint origin, float width, float height);
virtual void FrameResized(float width, float height);
virtual bool QuitRequested();
void ShowInfo(const FileInfo* info);
void SetRescanEnabled(bool enabled);
BVolume* FindDeviceFor(dev_t device,
bool invoke = false);
private:
bool _FixAspectRatio(float* width, float* height);
ControlsView* fControlsView;
PieView* fPieView;
StatusView* fStatusView;
};
#endif // MAIN_WINDOW_H
+817
View File
@@ -0,0 +1,817 @@
/*
* 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 "PieView.h"
#include <fs_info.h>
#include <math.h>
#include <AppFileInfo.h>
#include <Bitmap.h>
#include <Entry.h>
#include <File.h>
#include <MenuItem.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <String.h>
#include <Volume.h>
#include "Common.h"
#include "InfoWindow.h"
#include "MainWindow.h"
#include "Scanner.h"
static const int32 kIdxGetInfo = 0;
static const int32 kIdxOpen = 1;
static const int32 kIdxOpenWith = 2;
static const int32 kIdxRescan = 3;
// TODO: It would be nice to make a common base class for AppMenuItem and
// VolMenuItem (menu items that include an icon).
class AppMenuItem: public BMenuItem {
public:
AppMenuItem(const char* appSig, int category);
virtual ~AppMenuItem();
virtual void GetContentSize(float* _width, float* _height);
virtual void DrawContent();
int Category() const
{ return fCategory; }
const entry_ref* AppRef() const
{ return &fAppRef; }
bool IsValid() const
{ return fIsValid; }
private:
int fCategory;
BBitmap* fIcon;
entry_ref fAppRef;
bool fIsValid;
};
AppMenuItem::AppMenuItem(const char* appSig, int category):
BMenuItem(kEmptyStr, NULL), fCategory(category), fIcon(NULL), fIsValid(false)
{
if (be_roster->FindApp(appSig, &fAppRef) == B_NO_ERROR) {
fIcon = new BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_RGBA32);
if (BNodeInfo::GetTrackerIcon(&fAppRef, fIcon, B_MINI_ICON) == B_OK) {
BEntry appEntry(&fAppRef);
if (appEntry.InitCheck() == B_OK) {
char name[B_FILE_NAME_LENGTH];
appEntry.GetName(name);
SetLabel(name);
fIsValid = true;
}
}
}
}
AppMenuItem::~AppMenuItem()
{
delete fIcon;
}
void
AppMenuItem::GetContentSize(float* _width, float* _height)
{
if (_width)
*_width = fIcon->Bounds().Width() + be_plain_font->StringWidth(Label());
if (_height) {
struct font_height fh;
be_plain_font->GetHeight(&fh);
float fontHeight = ceilf(fh.ascent) + ceilf(fh.descent)
+ ceilf(fh.leading);
*_height = max(fontHeight, fIcon->Bounds().Height());
}
}
void
AppMenuItem::DrawContent()
{
Menu()->SetDrawingMode(B_OP_OVER);
Menu()->MovePenBy(0.0, -1.0);
Menu()->DrawBitmap(fIcon);
Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0);
BMenuItem::DrawContent();
}
// #pragma mark - PieView
PieView::PieView(BRect frame, MainWindow* window)
: BView(frame, NULL, B_FOLLOW_ALL,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_SUBPIXEL_PRECISE),
fWindow(window),
fScanners(),
fCurrentVolume(NULL),
fMouseOverInfo(),
fClicked(false),
fDragging(false)
{
SetViewColor(B_TRANSPARENT_COLOR);
SetLowColor(kWindowColor);
fMouseOverMenu = new BPopUpMenu(kEmptyStr, false, false);
fMouseOverMenu->AddItem(new BMenuItem(kMenuGetInfo, NULL), kIdxGetInfo);
fMouseOverMenu->AddItem(new BMenuItem(kMenuOpen, NULL), kIdxOpen);
fFileUnavailableMenu = new BPopUpMenu(kEmptyStr, false, false);
BMenuItem* item = new BMenuItem(kStrUnavail, NULL);
item->SetEnabled(false);
fFileUnavailableMenu->AddItem(item);
BFont font;
GetFont(&font);
font.SetSize(ceilf(font.Size() * 1.33));
font.SetFace(B_BOLD_FACE);
SetFont(&font);
struct font_height fh;
font.GetHeight(&fh);
fFontHeight = ceilf(fh.ascent) + ceilf(fh.descent) + ceilf(fh.leading);
}
PieView::~PieView()
{
delete fMouseOverMenu;
delete fFileUnavailableMenu;
while (fScanners.size() != 0) {
ScannerMap::iterator i = fScanners.begin();
(*i).second->RequestQuit();
fScanners.erase(i);
}
}
void
PieView::MessageReceived(BMessage* message)
{
switch (message->what) {
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);
_HandleArg(&entry);
}
break;
}
case kBtnRescan:
if (fCurrentVolume != NULL) {
fScanners[fCurrentVolume]->Refresh();
Invalidate();
}
break;
case kMenuSelectVol:
{
BVolume* volume;
if (message->FindPointer(kNameVolPtr, (void**)&volume) == B_OK)
_ShowVolume(volume);
break;
}
case kScanProgress:
case kScanDone:
{
BVolume* volume;
if (message->FindPointer(kNameVolPtr, (void**)&volume) == B_OK
&& volume == fCurrentVolume) {
Invalidate();
}
break;
}
default:
BView::MessageReceived(message);
break;
}
}
void
PieView::MouseDown(BPoint where)
{
uint32 buttons;
BMessage* current = Window()->CurrentMessage();
if (current->FindInt32("buttons", (int32*)&buttons) != B_OK)
buttons = B_PRIMARY_MOUSE_BUTTON;
FileInfo* info = _FileAt(where);
if (info == NULL || info->pseudo)
return;
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
fClicked = true;
fDragStart = where;
fClickedFile = info;
SetMouseEventMask(B_POINTER_EVENTS);
} else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
where = ConvertToScreen(where);
_ShowContextMenu(info, where);
}
}
void
PieView::MouseUp(BPoint where)
{
// If the primary button was released and there was no dragging happening,
// just zoom in or out.
if (fClicked && !fDragging) {
Scanner* scanner = fScanners[fCurrentVolume];
FileInfo* info = _FileAt(where);
if (info != NULL) {
if (info == scanner->CurrentDir()) {
scanner->ChangeDir(info->parent);
Invalidate();
} else if (info->children.size() > 0) {
scanner->ChangeDir(info);
Invalidate();
}
}
}
fClicked = false;
fDragging = false;
}
void
PieView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
{
if (fClicked) {
// Primary mouse button is down.
if (fDragging)
return;
// If the mouse has moved far enough, initiate dragging.
BPoint diff = where - fDragStart;
float distance = sqrtf(diff.x * diff.x + diff.y * diff.x);
if (distance > kDragThreshold) {
fDragging = true;
BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32);
if (BNodeInfo::GetTrackerIcon(&fClickedFile->ref, icon,
B_LARGE_ICON) == B_OK) {
BMessage msg(B_SIMPLE_DATA);
msg.AddRef("refs", &fClickedFile->ref);
DragMessage(&msg, icon, B_OP_BLEND, BPoint(15.0, 15.0));
} else
delete icon;
}
} else {
// Mouse button is not down, display file info.
if (transit == B_EXITED_VIEW) {
// Clear status view
fWindow->ShowInfo(NULL);
} else {
// Display file information.
fWindow->ShowInfo(_FileAt(where));
}
}
}
void
PieView::Draw(BRect updateRect)
{
if (fScanners.find(fCurrentVolume) != fScanners.end()) {
// There is a current volume.
Scanner* scanner = fScanners[fCurrentVolume];
if (scanner->IsBusy()) {
// Show progress of scanning.
_DrawProgressBar(updateRect);
fWindow->SetRescanEnabled(false);
} else {
_DrawPieChart(updateRect);
fWindow->SetRescanEnabled(true);
}
} else {
// No volume has been selected, so display a prompt for one.
FillRect(updateRect, B_SOLID_LOW);
BRect b = Bounds();
float strWidth = StringWidth(kVolPrompt);
float bx = (b.left + b.Width() - strWidth) / 2.0;
float by = (b.top + b.Height()) / 2.0;
SetHighColor(0x00, 0x00, 0x00);
DrawString(kVolPrompt, BPoint(bx, by));
fWindow->SetRescanEnabled(false);
}
}
// #pragma mark - private
void
PieView::_HandleArg(const BEntry* entry)
{
// Going through these seemingly pointless gyrations (instead of getting
// the dev_t directly from the BEntry) allows us to process volumes
// properly. It looks like a dropped volume refers to dev_t 1 (why?), but
// getting the dev_t from the path allows us to get at the dev_t where the
// root directory actually lives.
BPath path;
entry->GetPath(&path);
dev_t device = dev_for_path(path.Path());
// Set the desired path in the scanner.
Scanner* scanner = NULL;
ScannerMap::iterator i = fScanners.begin();
while (i != fScanners.end()) {
if ((*i).second->Device() == device) {
scanner = (*i).second;
break;
}
i++;
}
if (scanner == NULL) {
BVolume* volume = fWindow->FindDeviceFor(device);
if (volume != NULL)
scanner = fScanners[volume] = new Scanner(volume, this);
}
if (scanner != NULL) {
string desiredPath(path.Path());
scanner->SetDesiredPath(desiredPath);
// Select the volume on the menu.
fWindow->FindDeviceFor(device, true);
}
}
void
PieView::_ShowVolume(BVolume* volume)
{
if (volume != NULL) {
if (fScanners.find(volume) == fScanners.end())
fScanners[volume] = new Scanner(volume, this);
Scanner* scanner = fScanners[volume];
if (scanner->Snapshot() == NULL)
scanner->Refresh();
}
fCurrentVolume = volume;
Invalidate();
}
void
PieView::_DrawProgressBar(BRect updateRect)
{
// Show the progress of the scanning operation.
fMouseOverInfo.clear();
FillRect(updateRect, B_SOLID_LOW);
// Draw the progress bar.
BRect b = Bounds();
float bx = floorf((b.left + b.Width() - kProgBarWidth) / 2.0);
float by = floorf((b.top + b.Height() - kProgBarHeight) / 2.0);
float ex = bx + kProgBarWidth;
float ey = by + kProgBarHeight;
SetPenSize(1.0);
SetHighColor(tint_color(kWindowColor, B_LIGHTEN_2_TINT));
StrokeLine(BPoint(bx, ey), BPoint(ex, ey));
StrokeLine(BPoint(ex, by), BPoint(ex, ey));
SetHighColor(tint_color(kWindowColor, B_DARKEN_2_TINT));
StrokeLine(BPoint(bx, by), BPoint(ex, by));
StrokeLine(BPoint(bx, by), BPoint(bx, ey));
bx += 1.0; by += 1.0;
ex -= 1.0; ey -= 1.0;
float mx = bx + floorf((kProgBarWidth - 2.0)
* fScanners[fCurrentVolume]->Progress() / 100.0 + 0.5);
SetHighColor(tint_color(kWindowColor, B_DARKEN_1_TINT));
FillRect(BRect(mx, by, ex, ey));
SetHighColor(0x00, 0x20, 0x90);
StrokeLine(BPoint(bx, ey), BPoint(mx, ey));
StrokeLine(BPoint(mx, by), BPoint(mx, ey));
SetHighColor(0x00, 0xc0, 0xff);
StrokeLine(BPoint(bx, by), BPoint(mx, by));
StrokeLine(BPoint(bx, by), BPoint(bx, ey));
bx += 1.0; by += 1.0;
mx -= 1.0; ey -= 1.0;
SetHighColor(0x00, 0x80, 0xf0);
FillRect(BRect(bx, by, mx, ey));
// Tell what we are doing.
const char* task = fScanners[fCurrentVolume]->Task();
float strWidth = StringWidth(task);
bx = (b.left + b.Width() - strWidth) / 2.0;
by -= fFontHeight + 2.0 * kSmallVMargin;
SetHighColor(0, 0, 0);
DrawString(task, BPoint(bx, by));
}
void
PieView::_DrawPieChart(BRect updateRect)
{
BRect pieRect = Bounds();
pieRect.InsetBy(kPieOuterMargin, kPieOuterMargin);
if (!updateRect.Intersects(pieRect))
return;
SetHighColor(kPieBGColor);
FillRect(updateRect);
int colorIdx = 0;
FileInfo* currentDir = fScanners[fCurrentVolume]->CurrentDir();
FileInfo* parent = currentDir;
while (parent != NULL) {
parent = parent->parent;
colorIdx++;
}
_DrawDirectory(currentDir, 0.0, 0.0, colorIdx % kBasePieColorCount, 0);
// This is just for the case when the mouse hovers over the view
// while the scanning process is running and then does not move
// until after the results are shown. In that case the info will
// not automatically update. (TODO: fix this and put into MessageReceived()
// kScanDone - currently, fileAt() returns NULL (mouseoverinfo...))
BPoint where;
uint32 ignore;
GetMouse(&where, &ignore, false);
fWindow->ShowInfo(_FileAt(where));
}
float
PieView::_DrawDirectory(FileInfo* info, float parentSpan, float beginAngle,
int colorIdx, int level)
{
BRect b = Bounds();
if (b.Width() < 2.0 * (kPieCenterSize + level * kPieRingSize
+ kPieOuterMargin + kPieInnerMargin)) {
return 0.0;
}
VolumeSnapshot* snapshot = fScanners[fCurrentVolume]->Snapshot();
float cx = floorf(b.left + b.Width() / 2.0 + 0.5);
float cy = floorf(b.top + b.Height() / 2.0 + 0.5);
float mySpan;
if (level == 0) {
// Make room for mouse over info.
fMouseOverInfo.clear();
fMouseOverInfo[0] = SegmentList();
// Draw the center circle.
const char* displayName;
if (info == NULL) {
// NULL represents the entire volume. Show used and free space in
// the center circle, with the used segment representing the
// volume's root directory.
off_t volCapacity = snapshot->capacity;
mySpan = 360.0 * (volCapacity - snapshot->freeBytes) / volCapacity;
SetHighColor(kEmptySpcColor);
FillEllipse(BPoint(cx, cy), kPieCenterSize, kPieCenterSize);
SetHighColor(kBasePieColor[0]);
FillArc(BPoint(cx, cy), kPieCenterSize, kPieCenterSize, 0.0, mySpan);
// Show total volume capacity.
char label[B_PATH_NAME_LENGTH];
size_to_string(volCapacity, label);
SetHighColor(kPieBGColor);
SetDrawingMode(B_OP_OVER);
DrawString(label, BPoint(cx - StringWidth(label) / 2.0,
cy + fFontHeight + kSmallVMargin));
SetDrawingMode(B_OP_COPY);
displayName = snapshot->name.c_str();
// Record in-use space and free space for use during MouseMoved().
info = snapshot->rootDir;
fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info));
if (mySpan < 360.0 - kMinSegmentSpan) {
fMouseOverInfo[0].push_back(Segment(mySpan, 360.0,
snapshot->freeSpace));
}
} else {
// Show a normal directory.
SetHighColor(kBasePieColor[colorIdx]);
FillEllipse(BRect(cx - kPieCenterSize, cy - kPieCenterSize,
cx + kPieCenterSize + 0.5, cy + kPieCenterSize + 0.5));
displayName = info->ref.name;
mySpan = 360.0;
// Record the segment for use during MouseMoved().
fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info));
}
SetPenSize(1.0);
SetHighColor(kOutlineColor);
StrokeEllipse(BPoint(cx, cy), kPieCenterSize + 0.5, kPieCenterSize + 0.5);
// Show the name of the volume or directory.
BString label(displayName);
BFont font;
GetFont(&font);
font.TruncateString(&label, B_TRUNCATE_END,
2.0 * (kPieCenterSize - kSmallHMargin));
float labelWidth = font.StringWidth(label.String());
SetHighColor(kPieBGColor);
SetDrawingMode(B_OP_OVER);
DrawString(label.String(), BPoint(cx - labelWidth / 2.0, cy));
SetDrawingMode(B_OP_COPY);
beginAngle = 0.0;
} else {
// Draw an exterior segment.
float parentSize;
if (info->parent == NULL)
parentSize = (float)snapshot->capacity;
else
parentSize = (float)info->parent->size;
mySpan = parentSpan * (float)info->size / parentSize;
if (mySpan >= kMinSegmentSpan) {
rgb_color color = kBasePieColor[colorIdx];
color.red += kLightenFactor * level;
color.green += kLightenFactor * level;
color.blue += kLightenFactor * level;
SetHighColor(color);
SetPenSize(kPieRingSize);
float radius = kPieCenterSize + level * kPieRingSize
- kPieRingSize / 2.0;
StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan);
SetHighColor(kOutlineColor);
SetPenSize(0.0);
float segBeginRadius = kPieCenterSize + (level - 1)
* kPieRingSize + 0.5;
float segLength = kPieRingSize - 0.5;
float rad = deg2rad(beginAngle);
float bx = cx + segBeginRadius * cos(rad);
float by = cy - segBeginRadius * sin(rad);
float ex = bx + segLength * cos(rad);
float ey = by - segLength * sin(rad);
StrokeLine(BPoint(bx, by), BPoint(ex, ey));
rad = deg2rad(beginAngle + mySpan);
bx = cx + segBeginRadius * cos(rad);
by = cy - segBeginRadius * sin(rad);
ex = bx + segLength * cos(rad);
ey = by - segLength * sin(rad);
StrokeLine(BPoint(bx, by), BPoint(ex, ey));
SetPenSize(0.0);
SetHighColor(kOutlineColor);
radius += kPieRingSize / 2.0;
StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan);
// Record the segment for use during MouseMoved().
if (fMouseOverInfo.find(level) == fMouseOverInfo.end())
fMouseOverInfo[level] = SegmentList();
fMouseOverInfo[level].push_back(
Segment(beginAngle, beginAngle + mySpan, info));
}
}
// Draw children.
vector<FileInfo*>::iterator i = info->children.begin();
while (i != info->children.end()) {
float childSpan
= _DrawDirectory(*i, mySpan, beginAngle, colorIdx, level + 1);
if (childSpan >= kMinSegmentSpan) {
beginAngle += childSpan;
colorIdx = (colorIdx + 1) % kBasePieColorCount;
}
i++;
}
return mySpan;
}
FileInfo*
PieView::_FileAt(const BPoint& where)
{
BRect b = Bounds();
float cx = b.left + b.Width() / 2.0;
float cy = b.top + b.Height() / 2.0;
float dx = where.x - cx;
float dy = where.y - cy;
float dist = sqrt(dx*dx + dy*dy);
int level;
if (dist < kPieCenterSize)
level = 0;
else
level = 1 + (int)((dist - kPieCenterSize) / kPieRingSize);
float angle = rad2deg(atan(dy / dx));
angle = ((dx < 0.0) ? 180.0 : (dy < 0.0) ? 0.0 : 360.0) - angle;
if (fMouseOverInfo.find(level) == fMouseOverInfo.end()) {
// No files in this level (ring) of the pie.
return NULL;
}
SegmentList s = fMouseOverInfo[level];
SegmentList::iterator i = s.begin();
while (i != s.end() && (angle < (*i).begin || (*i).end < angle))
i++;
if (i == s.end()) {
// Nothing at this angle.
return NULL;
}
return (*i).info;
}
void
PieView::_AddAppToList(vector<AppMenuItem*>& list, const char* appSig,
int category)
{
// skip self.
if (strcmp(appSig, kAppSignature) == 0)
return;
AppMenuItem* item = new AppMenuItem(appSig, category);
if (item->IsValid()) {
vector<AppMenuItem*>::iterator i = list.begin();
while (i != list.end()) {
if (*item->AppRef() == *(*i)->AppRef()) {
// Skip duplicates.
delete item;
return;
}
i++;
}
list.push_back(item);
} else {
// Skip items that weren't constructed successfully.
delete item;
}
}
BMenu*
PieView::_BuildOpenWithMenu(FileInfo* info)
{
vector<AppMenuItem*> appList;
// Get preferred app.
BMimeType* type = info->Type();
char appSignature[B_MIME_TYPE_LENGTH];
if (type->GetPreferredApp(appSignature) == B_OK)
_AddAppToList(appList, appSignature, 1);
// Get apps that handle this subtype and supertype.
BMessage msg;
if (type->GetSupportingApps(&msg) == B_OK) {
int32 subs, supers, i;
msg.FindInt32("be:sub", &subs);
msg.FindInt32("be:super", &supers);
const char* appSig;
for (i = 0; i < subs; i++) {
msg.FindString("applications", i, &appSig);
_AddAppToList(appList, appSig, 2);
}
int hold = i;
for (i = 0; i < supers; i++) {
msg.FindString("applications", i + hold, &appSig);
_AddAppToList(appList, appSig, 3);
}
}
// Get apps that handle any type.
if (BMimeType::GetWildcardApps(&msg) == B_OK) {
const char* appSig;
for (int32 i = 0; true; i++) {
if (msg.FindString("applications", i, &appSig) == B_OK)
_AddAppToList(appList, appSig, 4);
else
break;
}
}
delete type;
BMenu* openWith = new BMenu(kMenuOpenWith);
if (appList.size() == 0) {
BMenuItem* item = new BMenuItem(kMenuNoApps, NULL);
item->SetEnabled(false);
openWith->AddItem(item);
} else {
vector<AppMenuItem*>::iterator i = appList.begin();
int category = (*i)->Category();
while (i != appList.end()) {
if (category != (*i)->Category()) {
openWith->AddSeparatorItem();
category = (*i)->Category();
}
openWith->AddItem(*i);
i++;
}
}
return openWith;
}
void
PieView::_ShowContextMenu(FileInfo* info, BPoint p)
{
BRect openRect(p.x - 5.0, p.y - 5.0, p.x + 5.0, p.y + 5.0);
// Display the open-with menu only if the file is still available.
BNode node(&info->ref);
if (node.InitCheck() == B_OK) {
// Add "Open With" submenu.
BMenu* openWith = _BuildOpenWithMenu(info);
fMouseOverMenu->AddItem(openWith, kIdxOpenWith);
// Add a "Rescan" option for folders.
BMenuItem* rescan = NULL;
if (info->children.size() > 0) {
rescan = new BMenuItem(kStrRescan, NULL);
fMouseOverMenu->AddItem(rescan, kIdxRescan);
}
BMenuItem* item = fMouseOverMenu->Go(p, false, true, openRect);
if (item != NULL) {
switch (fMouseOverMenu->IndexOf(item)) {
case kIdxGetInfo:
new InfoWin(p, info, Window());
break;
case kIdxOpen:
_Launch(info);
break;
case kIdxRescan:
fScanners[fCurrentVolume]->Refresh(info);
Invalidate();
break;
default: // must be "Open With" submenu
_Launch(info, ((AppMenuItem*)item)->AppRef());
break;
}
}
if (rescan != NULL) {
fMouseOverMenu->RemoveItem(rescan);
delete rescan;
}
fMouseOverMenu->RemoveItem(openWith);
delete openWith;
}
else {
// The file is no longer available.
fFileUnavailableMenu->Go(p, false, true, openRect);
}
}
void
PieView::_Launch(FileInfo* info, const entry_ref* appRef)
{
BMessage msg(B_REFS_RECEIVED);
msg.AddRef("refs", &info->ref);
if (appRef == NULL) {
// Let the registrar pick an app based on the file's MIME type.
BMimeType* type = info->Type();
be_roster->Launch(type->Type(), &msg);
delete type;
} else {
// Launch a designated app to handle this file.
be_roster->Launch(appRef, &msg);
}
}
+91
View File
@@ -0,0 +1,91 @@
/*
* 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.
*/
#ifndef PIE_VIEW_H
#define PIE_VIEW_H
#include <View.h>
#include <map>
#include <vector>
class AppMenuItem;
class BEntry;
class BMenu;
class BPopUpMenu;
class BVolume;
struct entry_ref;
struct FileInfo;
class Scanner;
class MainWindow;
class PieView: public BView {
public:
PieView(BRect frame, MainWindow* window);
virtual ~PieView();
virtual void MessageReceived(BMessage* message);
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
virtual void Draw(BRect updateRect);
private:
void _HandleArg(const BEntry* entry);
void _ShowVolume(BVolume* volume);
void _DrawProgressBar(BRect updateRect);
void _DrawPieChart(BRect updateRect);
float _DrawDirectory(FileInfo* info,
float parentSpan, float beginAngle,
int colorIdx, int level);
FileInfo* _FileAt(const BPoint& where);
void _AddAppToList(vector<AppMenuItem*>& list,
const char* appSignature, int category);
BMenu* _BuildOpenWithMenu(FileInfo* info);
void _ShowContextMenu(FileInfo* info, BPoint where);
void _Launch(FileInfo* info,
const entry_ref* ref = NULL);
private:
struct Segment {
Segment()
: begin(0.0), end(0.0), info(NULL) { }
Segment(float b, float e, FileInfo* i)
: begin(b), end(e), info(i) { }
Segment(const Segment &s)
: begin(s.begin), end(s.end), info(s.info) { }
~Segment() { }
float begin;
float end;
FileInfo* info;
};
typedef vector<Segment> SegmentList;
typedef map<int, SegmentList> MouseOverInfo;
typedef map<BVolume*, Scanner*> ScannerMap;
private:
MainWindow* fWindow;
ScannerMap fScanners;
BVolume* fCurrentVolume;
FileInfo* fCurrentDir;
MouseOverInfo fMouseOverInfo;
BPopUpMenu* fMouseOverMenu;
BPopUpMenu* fFileUnavailableMenu;
float fFontHeight;
bool fClicked;
bool fDragging;
BPoint fDragStart;
FileInfo* fClickedFile;
};
#endif // PIE_VIEW_H
+270
View File
@@ -0,0 +1,270 @@
/*
* 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 "Scanner.h"
#include <Directory.h>
#include "Common.h"
Scanner::Scanner(BVolume *v, BHandler *handler)
: BLooper(),
fListener(handler),
fDoneMessage(kScanDone),
fProgressMessage(kScanProgress),
fVolume(v),
fSnapshot(NULL),
fDesiredPath(),
fTask(),
fBusy(false),
fQuitRequested(false)
{
fDoneMessage.AddPointer(kNameVolPtr, fVolume);
fProgressMessage.AddPointer(kNameVolPtr, fVolume);
Run();
}
Scanner::~Scanner()
{
delete fSnapshot;
}
void
Scanner::MessageReceived(BMessage* message)
{
switch (message->what) {
case kScanRefresh:
{
FileInfo* startInfo;
if (message->FindPointer(kNameFilePtr, (void **)&startInfo) == B_OK)
_RunScan(startInfo);
break;
}
default:
BLooper::MessageReceived(message);
break;
}
}
void
Scanner::Refresh(FileInfo* startInfo)
{
if (fBusy)
return;
fBusy = true;
// Remember the current directory, if any, so we can return to it after
// the scanning is done.
if (fSnapshot != NULL && fSnapshot->currentDir != NULL)
fSnapshot->currentDir->GetPath(fDesiredPath);
fTask.assign(kEmptyStr);
BMessage msg(kScanRefresh);
msg.AddPointer(kNameFilePtr, startInfo);
PostMessage(&msg);
}
void
Scanner::SetDesiredPath(string &path)
{
fDesiredPath = path;
if (fSnapshot == NULL)
Refresh();
else
_ChangeToDesired();
}
void
Scanner::RequestQuit()
{
// If the looper thread is scanning, we won't succeed to grab the lock,
// since the thread is scanning from within MessageReceived(). Then by
// setting fQuitRequested, the thread will stop scanning and only after
// that happened we succeed to grab the lock. So this line of action
// should be safe.
fQuitRequested = true;
Lock();
Quit();
}
// #pragma mark - private
void
Scanner::_RunScan(FileInfo* startInfo)
{
if (startInfo == NULL || startInfo == fSnapshot->rootDir) {
delete fSnapshot;
fSnapshot = new VolumeSnapshot(fVolume);
fTask = kStrScanningX + fSnapshot->name;
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
fVolumeBytesScanned = 0;
fProgress = 0.0;
fLastReport = -100.0;
BDirectory root;
fVolume->GetRootDirectory(&root);
fSnapshot->rootDir = _GetFileInfo(&root, NULL);
FileInfo* freeSpace = new FileInfo;
freeSpace->pseudo = true;
string s = "Free on " + fSnapshot->name;
freeSpace->ref.set_name(s.c_str());
freeSpace->size = fSnapshot->freeBytes;
fSnapshot->freeSpace = freeSpace;
fSnapshot->currentDir = NULL;
} else {
fSnapshot->capacity = fVolume->Capacity();
fSnapshot->freeBytes = fVolume->FreeBytes();
fTask = kStrScanningX + string(startInfo->ref.name);
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
fVolumeBytesScanned = fVolumeBytesInUse - startInfo->size; // best guess
fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse;
fLastReport = -100.0;
BDirectory startDir(&startInfo->ref);
if (startDir.InitCheck() == B_OK) {
FileInfo *parent = startInfo->parent;
vector<FileInfo *>::iterator i = parent->children.begin();
FileInfo* newInfo = _GetFileInfo(&startDir, parent);
while (i != parent->children.end() && *i != startInfo)
i++;
int idx = i - parent->children.begin();
parent->children[idx] = newInfo;
// Fixup count and size fields in parent directory.
parent->size += newInfo->size - startInfo->size;
parent->count += newInfo->count - startInfo->count;
delete startInfo;
}
}
fBusy = false;
_ChangeToDesired();
fListener.SendMessage(&fDoneMessage);
}
FileInfo*
Scanner::_GetFileInfo(BDirectory* dir, FileInfo* parent)
{
FileInfo* thisDir = new FileInfo;
BEntry entry;
dir->GetEntry(&entry);
entry.GetRef(&thisDir->ref);
thisDir->parent = parent;
thisDir->count = 0;
while (true) {
if (fQuitRequested)
break;
if (dir->GetNextEntry(&entry) == B_ENTRY_NOT_FOUND)
break;
if (entry.IsSymLink())
continue;
if (entry.IsFile()) {
FileInfo *child = new FileInfo;
entry.GetRef(&child->ref);
entry.GetSize(&child->size);
child->parent = thisDir;
thisDir->children.push_back(child);
// Send a progress report periodically.
fVolumeBytesScanned += child->size;
fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse;
if (fProgress - fLastReport > kReportInterval) {
fLastReport = fProgress;
fListener.SendMessage(&fProgressMessage);
}
}
else if (entry.IsDirectory()) {
BDirectory childDir(&entry);
thisDir->children.push_back(_GetFileInfo(&childDir, thisDir));
}
thisDir->count++;
}
vector<FileInfo *>::iterator i = thisDir->children.begin();
while (i != thisDir->children.end()) {
thisDir->size += (*i)->size;
thisDir->count += (*i)->count;
i++;
}
return thisDir;
}
void
Scanner::_ChangeToDesired()
{
if (fBusy || fDesiredPath.size() <= 0)
return;
char* workPath = strdup(fDesiredPath.c_str());
char* pathPtr = &workPath[1]; // skip leading '/'
char* endOfPath = pathPtr + strlen(pathPtr);
// Check the name of the root directory. It is a special case because
// it has no parent data structure.
FileInfo* checkDir = fSnapshot->rootDir;
FileInfo* prefDir = NULL;
char* sep = strchr(pathPtr, '/');
if (sep != NULL)
*sep = '\0';
if (strcmp(pathPtr, checkDir->ref.name) == 0) {
// Root directory matches, so follow the remainder of the path as
// far as possible.
prefDir = checkDir;
pathPtr += 1 + strlen(pathPtr);
while (pathPtr < endOfPath) {
sep = strchr(pathPtr, '/');
if (sep != NULL)
*sep = '\0';
checkDir = prefDir->FindChild(pathPtr);
if (checkDir == NULL || checkDir->children.size() == 0)
break;
pathPtr += 1 + strlen(pathPtr);
prefDir = checkDir;
}
}
// If the desired path is the volume's root directory, default to the
// volume display.
if (prefDir == fSnapshot->rootDir)
prefDir = NULL;
ChangeDir(prefDir);
free(workPath);
fDesiredPath.erase();
}
+71
View File
@@ -0,0 +1,71 @@
/*
* 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.
*/
#ifndef SCANNER_H
#define SCANNER_H
#include <string>
#include <Looper.h>
#include <Message.h>
#include <Messenger.h>
#include <Volume.h>
#include "Snapshot.h"
class BDirectory;
class Scanner: public BLooper {
public:
Scanner(BVolume* volume, BHandler* handler);
virtual ~Scanner();
virtual void MessageReceived(BMessage* message);
VolumeSnapshot* Snapshot() const
{ return fBusy ? NULL : fSnapshot; }
void Refresh(FileInfo* startInfo = NULL);
bool IsBusy() const
{ return fBusy; }
const char* Task() const
{ return fTask.c_str(); }
float Progress() const
{ return min_c(100.0, fProgress); }
FileInfo* CurrentDir() const
{ return fBusy ? NULL : fSnapshot->currentDir; }
void ChangeDir(FileInfo* info)
{ fSnapshot->currentDir = info; }
void SetDesiredPath(string &path);
dev_t Device() const
{ return fVolume->Device(); }
void RequestQuit();
private:
void _RunScan(FileInfo *startInfo);
FileInfo* _GetFileInfo(BDirectory* dir, FileInfo* parent);
void _ChangeToDesired();
BMessenger fListener;
BMessage fDoneMessage;
BMessage fProgressMessage;
BVolume* fVolume;
off_t fVolumeBytesInUse;
off_t fVolumeBytesScanned;
float fProgress;
float fLastReport;
VolumeSnapshot* fSnapshot;
string fDesiredPath;
string fTask;
bool fBusy;
bool fQuitRequested;
};
#endif // SCANNER_H
+126
View File
@@ -0,0 +1,126 @@
/*
* 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 "Snapshot.h"
#include <stdio.h>
#include <Directory.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Volume.h>
static const char* kFileType = B_FILE_MIME_TYPE;
static const char* kDirType = "application/x-vnd.Be-directory";
static const char* kVolumeType = "application/x-vnd.Be-volume";
FileInfo::FileInfo()
: pseudo(false),
size(0),
count(0),
parent(NULL),
children()
{
}
FileInfo::~FileInfo()
{
while (children.size() != 0) {
FileInfo* child = *children.begin();
delete child;
children.erase(children.begin());
}
}
void
FileInfo::GetPath(string& path) const
{
if (pseudo) {
path.assign(ref.name);
} else {
BEntry entry(&ref, true);
BPath pathObj(&entry);
path.assign(pathObj.Path());
}
}
FileInfo*
FileInfo::FindChild(const char* name) const
{
vector<FileInfo*>::const_iterator i = children.begin();
while (i != children.end()) {
if (strcmp((*i)->ref.name, name) == 0)
return *i;
i++;
}
return NULL;
}
BMimeType*
FileInfo::Type() const
{
char mimeStr[B_MIME_TYPE_LENGTH] = { '\0' };
if (parent == NULL) {
// This is the volume's root directory; treat it as a volume type.
strcpy(mimeStr, kVolumeType);
} else {
// Get the MIME type from the registrar.
BNode node(&ref);
if (node.InitCheck() == B_OK) {
BNodeInfo nodeInfo(&node);
if (nodeInfo.InitCheck() == B_OK) {
status_t s = nodeInfo.GetType(mimeStr);
if (s != B_OK && children.size() > 0) {
if (s == B_ENTRY_NOT_FOUND) {
// This status appears to be returned only for files on
// BFS volumes (e.g., CDFS volumes return B_BAD_VALUE).
//nodeInfo.SetType(kDirType);
}
strcpy(mimeStr, kDirType);
}
}
}
}
if (strlen(mimeStr) == 0)
strcpy(mimeStr, kFileType);
return new BMimeType(mimeStr);
}
// #pragma mark -
VolumeSnapshot::VolumeSnapshot(const BVolume* volume)
{
char nameBuffer[B_FILE_NAME_LENGTH];
volume->GetName(nameBuffer);
name = nameBuffer;
capacity = volume->Capacity();
freeBytes = volume->FreeBytes();
rootDir = NULL;
freeSpace = NULL;
}
VolumeSnapshot::~VolumeSnapshot()
{
delete rootDir;
delete freeSpace;
}
+51
View File
@@ -0,0 +1,51 @@
/*
* 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.
*/
#ifndef SNAPSHOT_H
#define SNAPSHOT_H
#include <string>
#include <vector>
#include <Entry.h>
class BMimeType;
class BVolume;
struct FileInfo {
FileInfo();
~FileInfo();
void GetPath(string& path) const;
FileInfo* FindChild(const char* name) const;
BMimeType* Type() const;
bool pseudo;
entry_ref ref;
off_t size;
int count;
FileInfo* parent;
vector<FileInfo*> children;
};
struct VolumeSnapshot {
VolumeSnapshot(const BVolume* volume);
~VolumeSnapshot();
string name;
off_t capacity;
off_t freeBytes;
FileInfo* rootDir;
FileInfo* freeSpace;
FileInfo* currentDir;
};
#endif // SNAPSHOT_H
+134
View File
@@ -0,0 +1,134 @@
/*
* 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 "StatusView.h"
#include <math.h>
#include <stdio.h>
#include <Box.h>
#include <Node.h>
#include <String.h>
#include <StringView.h>
#include "Common.h"
#include "Scanner.h"
StatusView::StatusView(BRect r)
: BView(r, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM, B_WILL_DRAW),
fCurrentFileInfo(NULL)
{
SetViewColor(kWindowColor);
struct font_height fh;
be_plain_font->GetHeight(&fh);
float plainHeight = ceilf(fh.ascent) + ceilf(fh.descent)
+ ceilf(fh.leading);
float fixedHeight = ceilf(fh.ascent) + ceilf(fh.descent)
+ ceilf(fh.leading);
ResizeTo(Bounds().Width(),
2.0 * kSmallVMargin + max(plainHeight, fixedHeight));
BRect r = Bounds();
r.top += kSmallVMargin;
// Size display
r.right -= kSmallHMargin;
r.left = r.right - StringWidth("9999.99 GB");
fSizeView = new BStringView(r, NULL, kEmptyStr,
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM);
AddChild(fSizeView);
// Vertical divider
r.right = r.left - kSmallHMargin;
r.left = r.right - 1.0;
AddChild(new BBox(r.InsetByCopy(0, -5), NULL,
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM));
// Count display
char testLabel[256];
sprintf(testLabel, kManyFiles, 999999);
r.right = r.left - kSmallHMargin;
r.left = r.right - (StringWidth(testLabel) + kSmallHMargin);
fCountView = new BStringView(r, NULL, kEmptyStr,
B_FOLLOW_RIGHT|B_FOLLOW_TOP_BOTTOM);
AddChild(fCountView);
// Vertical divider
r.right = r.left - kSmallHMargin;
r.left = r.right - 1.0;
AddChild(new BBox(r.InsetByCopy(0, -5), NULL,
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM));
// Path display
r.right = r.left - kSmallHMargin;
r.left = kSmallHMargin;
fPathView = new BStringView(r, NULL, kEmptyStr,
B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP_BOTTOM);
AddChild(fPathView);
// Horizontal divider
r = Bounds();
r.bottom = r.top + 1.0;
AddChild(new BBox(r.InsetByCopy(-5, 0), NULL,
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM, B_WILL_DRAW));
}
StatusView::~StatusView()
{
}
void
StatusView::Show(const FileInfo* info)
{
if (info == fCurrentFileInfo)
return;
fCurrentFileInfo = info;
if (info == NULL) {
fPathView->SetText(kEmptyStr);
fSizeView->SetText(kEmptyStr);
fCountView->SetText(kEmptyStr);
return;
}
if (!info->pseudo) {
BNode node(&info->ref);
if (node.InitCheck() != B_OK) {
fPathView->SetText(kStrUnavail);
fSizeView->SetText(kEmptyStr);
fCountView->SetText(kEmptyStr);
return;
}
}
float viewWidth = fPathView->Bounds().Width();
string path;
info->GetPath(path);
BString pathLabel = path.c_str();
be_plain_font->TruncateString(&pathLabel, B_TRUNCATE_BEGINNING, viewWidth);
fPathView->SetText(pathLabel.String());
char label[B_PATH_NAME_LENGTH];
size_to_string(info->size, label);
fSizeView->SetText(label);
if (info->count > 0) {
char label[256];
sprintf(label, (info->count == 1) ? kOneFile : kManyFiles, info->count);
fCountView->SetText(label);
} else {
fCountView->SetText(kEmptyStr);
}
}
+35
View File
@@ -0,0 +1,35 @@
/*
* 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.
*/
#ifndef STATUS_VIEW_H
#define STATUS_VIEW_H
#include <View.h>
#include <StringView.h>
#include <Rect.h>
struct FileInfo;
class StatusView: public BView {
public:
StatusView(BRect frame);
virtual ~StatusView();
void Show(const FileInfo* info);
private:
BStringView* fPathView;
BStringView* fSizeView;
BStringView* fCountView;
const FileInfo* fCurrentFileInfo;
};
#endif // STATUS_VIEW_H
+22
View File
@@ -0,0 +1,22 @@
---Bugs---
Why doesn't drag-and-drop on the icon work the same as dropping on the
open view? (e.g., multiple volumes don't work; floppy isn't accepted.)
---Notes---
Remember to change the alpha component of RGB colors to 0xff with
DiskProbe, if you use Resourcer. They live in block 6.
---Future/maybe/bloat---
Display unavailable files in a distinct color (maybe gray)?
Find a way to do redrawing more efficiently (BPicture?)
Add <- and -> buttons to traverse the zoom history.
Display pie in grayscale+patterns / color?
Display an icon somewhere while volumes are being scanned. Check out the
barber shop icon in the Tracker during queries!
Handle the special case where the volume is full: the volume display is
the same as the root directory display except for the colors!
When scanning all volumes, scan different physical devices in parallel,
and multiple partitions on a device in sequence.
Add a cancel button during scanning?
A button to cycle colors? (Useless toy).