diff --git a/docs/apps/diskusage/DiskUsage.html b/docs/apps/diskusage/DiskUsage.html
new file mode 100644
index 0000000000..c275e5f67e
--- /dev/null
+++ b/docs/apps/diskusage/DiskUsage.html
@@ -0,0 +1,142 @@
+
+
+DiskUsage
+
+
+
+
+
+
+
DiskUsage
+Volume Utilization Visualizer
+
+
+
+
+What is it?
+
+DiskUsage 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.
+
+Here is a picture of DiskUsage in action, so to speak.
+
+
+
+The circle in the middle represents my /boot/home directory.
+Each segment of the ring immediately outside that circle represents a file or
+directory within /boot/home. And so on.
+
+
+
Usage
+
+Startup. You can launch DiskUsage from the
+Tracker, either directly or by dropping a volume or folder on its
+icon; or from the Terminal, optionally passing the names of one
+or more files or folders as arguments. If you launch DiskUsage
+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.
+
+While DiskUsage 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.
+
+The main display. When DiskUsage 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
+DiskUsage displays.
+
+Zooming. 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.
+
+Pop-up menu. Clicking the secondary mouse button on a part
+of the picture pops up a menu with Tracker-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 DiskUsage does not monitor changes
+to the file system.
+
+Drag-and-drop. You can drag files and directories from
+DiskUsage to other applications (or to the desktop, the trash, etc.)
+with the primary mouse button. You can drop volumes and folders on
+DiskUsage's window to zoom directly to them.
+
+Other controls. The buttons in the top right corner of the
+DiskUsage window rescan the selected volume and display the
+documentation (this file).
+
+Notes:
+
+
+
+- If the graphical representation of a file or directory comprises less
+than about two degrees of a circle, it is excluded from the display.
+
+
- The number of files that
DiskUsage reports for a directory
+includes files in subdirectories too. A directory counts as a file.
+
+ -
DiskUsage ignores symbolic links.
+
+ - If you don't like the colors
DiskUsage uses, you can change
+them with your favorite resource editor.
+
+
+
+
+Credits, etc.
+
+Thanks to Steffen Gerlach for creating the Windows
+Scanner
+utility, from which I took the idea for using concentric circles to represent
+disk usage.
+
+Thanks to the Vim development team for
+creating the best programmer's editor on the planet, with which
+DiskUsage's original code and documentation were lovingly
+hand-crafted.
+
+Thanks to the folks at Be for creating such a kick-butt OS. It has made
+programming fun again.
+
+Thanks to Mike Steed for doing the programming. All the code is original.
+
+DiskUsage used to be called guido by it's original
+author. The name guido originated from GUI + du
+ -- the command-line disk usage utility. Plus, one of his favorite languages,
+Python, was created by a fine programmer
+named Guido.
+
+
+
Known problems
+
+
+
+- You can drop multiple volumes or folders on the icon, but
+
DiskUsage sees only one of them. Why?
+
+ - Dropping a floppy disk volume on
DiskUsage's window works, but
+dropping it on the icon does not work (unless you force it by holding the Ctrl
+key while you drag). Why?!
+
+
+
+
+License
+
+DiskUsage 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.
+
+
+
+
+
diff --git a/docs/apps/diskusage/home.png b/docs/apps/diskusage/home.png
new file mode 100644
index 0000000000..d8632ecedf
Binary files /dev/null and b/docs/apps/diskusage/home.png differ
diff --git a/docs/apps/diskusage/icon.png b/docs/apps/diskusage/icon.png
new file mode 100644
index 0000000000..a35ae694e7
Binary files /dev/null and b/docs/apps/diskusage/icon.png differ
diff --git a/src/apps/Jamfile b/src/apps/Jamfile
index 0c76bbda65..795d13bd8d 100644
--- a/src/apps/Jamfile
+++ b/src/apps/Jamfile
@@ -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 ;
diff --git a/src/apps/diskusage/App.cpp b/src/apps/diskusage/App.cpp
new file mode 100644
index 0000000000..5e5b03b046
--- /dev/null
+++ b/src/apps/diskusage/App.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+
+#include
+#include
+#include
+#include
+
+#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;
+}
+
diff --git a/src/apps/diskusage/App.h b/src/apps/diskusage/App.h
new file mode 100644
index 0000000000..fe39ac2179
--- /dev/null
+++ b/src/apps/diskusage/App.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+
+
+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
+
diff --git a/src/apps/diskusage/Common.cpp b/src/apps/diskusage/Common.cpp
new file mode 100644
index 0000000000..25ede95817
--- /dev/null
+++ b/src/apps/diskusage/Common.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+
+#include
+#include
+#include
+#include
+
+
+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);
+ }
+}
+
diff --git a/src/apps/diskusage/Common.h b/src/apps/diskusage/Common.h
new file mode 100644
index 0000000000..83ce776e42
--- /dev/null
+++ b/src/apps/diskusage/Common.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+#include
+#include
+
+
+#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
+
diff --git a/src/apps/diskusage/ControlsView.cpp b/src/apps/diskusage/ControlsView.cpp
new file mode 100644
index 0000000000..7ba86f80ef
--- /dev/null
+++ b/src/apps/diskusage/ControlsView.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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);
+}
+
+
diff --git a/src/apps/diskusage/ControlsView.h b/src/apps/diskusage/ControlsView.h
new file mode 100644
index 0000000000..b57f0558d2
--- /dev/null
+++ b/src/apps/diskusage/ControlsView.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+
+
+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
+
diff --git a/src/apps/diskusage/DiskUsage.rdef b/src/apps/diskusage/DiskUsage.rdef
new file mode 100644
index 0000000000..8c04e378dd
--- /dev/null
+++ b/src/apps/diskusage/DiskUsage.rdef
@@ -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";
diff --git a/src/apps/diskusage/InfoWindow.cpp b/src/apps/diskusage/InfoWindow.cpp
new file mode 100644
index 0000000000..70e64c3dea
--- /dev/null
+++ b/src/apps/diskusage/InfoWindow.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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 Item;
+ typedef vector- 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()
+{
+}
+
+
diff --git a/src/apps/diskusage/InfoWindow.h b/src/apps/diskusage/InfoWindow.h
new file mode 100644
index 0000000000..b72902e35b
--- /dev/null
+++ b/src/apps/diskusage/InfoWindow.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+
+
+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
+
diff --git a/src/apps/diskusage/Jamfile b/src/apps/diskusage/Jamfile
new file mode 100644
index 0000000000..11132a97df
--- /dev/null
+++ b/src/apps/diskusage/Jamfile
@@ -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
+;
+
diff --git a/src/apps/diskusage/MainWindow.cpp b/src/apps/diskusage/MainWindow.cpp
new file mode 100644
index 0000000000..8a57e838fe
--- /dev/null
+++ b/src/apps/diskusage/MainWindow.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+#include
+#include
+
+#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;
+}
+
+
diff --git a/src/apps/diskusage/MainWindow.h b/src/apps/diskusage/MainWindow.h
new file mode 100644
index 0000000000..0333cddb38
--- /dev/null
+++ b/src/apps/diskusage/MainWindow.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+
+
+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
diff --git a/src/apps/diskusage/PieView.cpp b/src/apps/diskusage/PieView.cpp
new file mode 100644
index 0000000000..c6c06afd20
--- /dev/null
+++ b/src/apps/diskusage/PieView.cpp
@@ -0,0 +1,817 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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::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& list, const char* appSig,
+ int category)
+{
+ // skip self.
+ if (strcmp(appSig, kAppSignature) == 0)
+ return;
+
+ AppMenuItem* item = new AppMenuItem(appSig, category);
+ if (item->IsValid()) {
+ vector::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 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::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);
+ }
+}
+
+
diff --git a/src/apps/diskusage/PieView.h b/src/apps/diskusage/PieView.h
new file mode 100644
index 0000000000..92f5afc9a2
--- /dev/null
+++ b/src/apps/diskusage/PieView.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008 Stephan Aßmus . 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
+
+#include