From f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 7 Sep 2008 14:18:01 +0000 Subject: [PATCH] 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 --- docs/apps/diskusage/DiskUsage.html | 142 +++++ docs/apps/diskusage/home.png | Bin 0 -> 14085 bytes docs/apps/diskusage/icon.png | Bin 0 -> 437 bytes src/apps/Jamfile | 1 + src/apps/diskusage/App.cpp | 109 ++++ src/apps/diskusage/App.h | 34 ++ src/apps/diskusage/Common.cpp | 119 ++++ src/apps/diskusage/Common.h | 107 ++++ src/apps/diskusage/ControlsView.cpp | 298 ++++++++++ src/apps/diskusage/ControlsView.h | 38 ++ src/apps/diskusage/DiskUsage.rdef | 78 +++ src/apps/diskusage/InfoWindow.cpp | 174 ++++++ src/apps/diskusage/InfoWindow.h | 37 ++ src/apps/diskusage/Jamfile | 16 + src/apps/diskusage/MainWindow.cpp | 171 ++++++ src/apps/diskusage/MainWindow.h | 44 ++ src/apps/diskusage/PieView.cpp | 817 ++++++++++++++++++++++++++++ src/apps/diskusage/PieView.h | 91 ++++ src/apps/diskusage/Scanner.cpp | 270 +++++++++ src/apps/diskusage/Scanner.h | 71 +++ src/apps/diskusage/Snapshot.cpp | 126 +++++ src/apps/diskusage/Snapshot.h | 51 ++ src/apps/diskusage/StatusView.cpp | 134 +++++ src/apps/diskusage/StatusView.h | 35 ++ src/apps/diskusage/TODO | 22 + 25 files changed, 2985 insertions(+) create mode 100644 docs/apps/diskusage/DiskUsage.html create mode 100644 docs/apps/diskusage/home.png create mode 100644 docs/apps/diskusage/icon.png create mode 100644 src/apps/diskusage/App.cpp create mode 100644 src/apps/diskusage/App.h create mode 100644 src/apps/diskusage/Common.cpp create mode 100644 src/apps/diskusage/Common.h create mode 100644 src/apps/diskusage/ControlsView.cpp create mode 100644 src/apps/diskusage/ControlsView.h create mode 100644 src/apps/diskusage/DiskUsage.rdef create mode 100644 src/apps/diskusage/InfoWindow.cpp create mode 100644 src/apps/diskusage/InfoWindow.h create mode 100644 src/apps/diskusage/Jamfile create mode 100644 src/apps/diskusage/MainWindow.cpp create mode 100644 src/apps/diskusage/MainWindow.h create mode 100644 src/apps/diskusage/PieView.cpp create mode 100644 src/apps/diskusage/PieView.h create mode 100644 src/apps/diskusage/Scanner.cpp create mode 100644 src/apps/diskusage/Scanner.h create mode 100644 src/apps/diskusage/Snapshot.cpp create mode 100644 src/apps/diskusage/Snapshot.h create mode 100644 src/apps/diskusage/StatusView.cpp create mode 100644 src/apps/diskusage/StatusView.h create mode 100644 src/apps/diskusage/TODO 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: + +

+ + +

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

+ + + + +

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 0000000000000000000000000000000000000000..d8632ecedff5049131b0fd33fb4a39de8388d556 GIT binary patch literal 14085 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 D^BeR; literal 0 HcmV?d00001 diff --git a/docs/apps/diskusage/icon.png b/docs/apps/diskusage/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a35ae694e77654537a1c2909efaede8d2cd2cab9 GIT binary patch literal 437 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE%)r2Cym(gtkfR&m6XN>+|9>F!`QzvJZr?k0 z_!z@!An#mpVevTzhU&`dy}R}@G%+v$l{^PRhDqnYMFZtI^WEHvfiwi%(%J@8%)nXT z5eXFJUjV|43$l93fi|gnx;TbZ%vl-~Iqk3k2lMQTg>!fQFVsG;#B*28nGd&)cr+z6 zXyha(Nlo@hRa2?pDHd0eS#KD{e59P!SbTcm>4utII?LR>J(VI#!0UC@F}M| z{F>_|&;B@>XW*#H*R<5|jk4~hO-g&)56tcC$l9gdyXxG{m-b<;`u{ctEPAx`65rHN zOWn||rykypRhyt^#I*4rlf(SKWsm-vIheeAw$Jy)vBJAgqy0no9(<@C^7|coNZ&rk zsrrlm%q~p)=XQO?|K^VFCm-i_EMA(Y^j3>WH&gcvYyyk+o>-v%J@5949{XSW#V36^Gxh(@|9rDOQmif)a{uS4s}QMOcz#jW$?xI6 ZG@aV~OS*oU|LV165Y^)L75ks<0RWN-yrlpD literal 0 HcmV?d00001 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 +#include + + +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& 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 SegmentList; + typedef map MouseOverInfo; + + typedef map 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 + diff --git a/src/apps/diskusage/Scanner.cpp b/src/apps/diskusage/Scanner.cpp new file mode 100644 index 0000000000..01dd03c08b --- /dev/null +++ b/src/apps/diskusage/Scanner.cpp @@ -0,0 +1,270 @@ +/* + * 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 "Scanner.h" + +#include + +#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::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::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(); +} + + diff --git a/src/apps/diskusage/Scanner.h b/src/apps/diskusage/Scanner.h new file mode 100644 index 0000000000..0b0a64f9c6 --- /dev/null +++ b/src/apps/diskusage/Scanner.h @@ -0,0 +1,71 @@ +/* + * 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 SCANNER_H +#define SCANNER_H + +#include + +#include +#include +#include +#include + +#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 + diff --git a/src/apps/diskusage/Snapshot.cpp b/src/apps/diskusage/Snapshot.cpp new file mode 100644 index 0000000000..b9100bc62d --- /dev/null +++ b/src/apps/diskusage/Snapshot.cpp @@ -0,0 +1,126 @@ +/* + * 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 "Snapshot.h" + +#include + +#include +#include +#include +#include +#include + + +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::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; +} + diff --git a/src/apps/diskusage/Snapshot.h b/src/apps/diskusage/Snapshot.h new file mode 100644 index 0000000000..691ee35433 --- /dev/null +++ b/src/apps/diskusage/Snapshot.h @@ -0,0 +1,51 @@ +/* + * 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 SNAPSHOT_H +#define SNAPSHOT_H + +#include +#include + +#include + + +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 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 + diff --git a/src/apps/diskusage/StatusView.cpp b/src/apps/diskusage/StatusView.cpp new file mode 100644 index 0000000000..a43a9cbf37 --- /dev/null +++ b/src/apps/diskusage/StatusView.cpp @@ -0,0 +1,134 @@ +/* + * 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 "StatusView.h" + +#include +#include + +#include +#include +#include +#include + +#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); + } +} + + diff --git a/src/apps/diskusage/StatusView.h b/src/apps/diskusage/StatusView.h new file mode 100644 index 0000000000..eaf409b5d0 --- /dev/null +++ b/src/apps/diskusage/StatusView.h @@ -0,0 +1,35 @@ +/* + * 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 STATUS_VIEW_H +#define STATUS_VIEW_H + +#include +#include +#include + + +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 + diff --git a/src/apps/diskusage/TODO b/src/apps/diskusage/TODO new file mode 100644 index 0000000000..14dfdd1974 --- /dev/null +++ b/src/apps/diskusage/TODO @@ -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). +