Imported the nice disk usage tool "Guido" into the repo as "DiskUsage". Guido
was originally written by Mike Steed. The code was freely available from his website at one point, which I am no longer able to find. I believe the license was a kind of public domain, at least compatible with our MIT/X11 license. The original code was well written with it's own coding style which was not very far from the Haiku coding style. I have hopefully adopted it completely to our coding style. While I did this, I came across several places with comments for a minor bug which I was able to resolve all. I also found ways to simplify certain places and I elliminated all non-necessary "inter-class knowledge" which was used to directly access members of other classes which should have been private. All in all, this should be a nice little addition, a useful tool which I found handy a lot of times. If I should not have added this without first consulting everyone, please voice your disagreement, and it can be removed it again. It is not part of the image yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27357 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,142 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>DiskUsage</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body bgcolor=#ffffff>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h2><img src="icon.png"> <code>DiskUsage</code><br>
|
||||||
|
Volume Utilization Visualizer</h2>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>What is it?</h3>
|
||||||
|
|
||||||
|
<code>DiskUsage</code> shows a graphical representation of how the space on your
|
||||||
|
volumes is utilized. It uses concentric circles to represent different levels
|
||||||
|
in the file system hierarchy. It is useful for answering the question, "Where
|
||||||
|
has all my disk space gone?" And sometimes it's fun just to look at the
|
||||||
|
pictures. <p>
|
||||||
|
|
||||||
|
Here is a picture of <code>DiskUsage</code> in action, so to speak. <p>
|
||||||
|
|
||||||
|
<center><img src="home.png"></center> <p>
|
||||||
|
|
||||||
|
The circle in the middle represents my <code>/boot/home</code> directory.
|
||||||
|
Each segment of the ring immediately outside that circle represents a file or
|
||||||
|
directory within <code>/boot/home</code>. And so on.
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Usage</h3>
|
||||||
|
|
||||||
|
<strong>Startup.</strong> You can launch <code>DiskUsage</code> from the
|
||||||
|
<code>Tracker</code>, either directly or by dropping a volume or folder on its
|
||||||
|
icon; or from the <code>Terminal</code>, optionally passing the names of one
|
||||||
|
or more files or folders as arguments. If you launch <code>DiskUsage</code>
|
||||||
|
without specifying a volume, it will wait until you drop one or more volumes
|
||||||
|
or folders on its window or select a volume from the menu. <p>
|
||||||
|
|
||||||
|
While <code>DiskUsage</code> is scanning a volume, it displays a status bar
|
||||||
|
showing its progress. Multiple volumes are scanned in parallel (each in its
|
||||||
|
own thread). You can view the progress on a volume by selecting that volume
|
||||||
|
from the menu. <p>
|
||||||
|
|
||||||
|
<strong>The main display.</strong> When <code>DiskUsage</code> finishes scanning
|
||||||
|
a volume, it displays a graphical representation of the volume's files and
|
||||||
|
directories. As you move the mouse over each part of the picture, information
|
||||||
|
about each file or directory appears in the area at the bottom of the window.
|
||||||
|
You can resize the window to increase or decrease the number of levels that
|
||||||
|
<code>DiskUsage</code> displays. <p>
|
||||||
|
|
||||||
|
<strong>Zooming.</strong> You can "zoom" in on a directory by clicking the
|
||||||
|
primary mouse button on it. The picture is redrawn with that directory in the
|
||||||
|
center circle. You can zoom out one level by clicking the center circle. At
|
||||||
|
the outermost level, the center circle represents the volume itself, showing
|
||||||
|
free and used space as pie slices. <p>
|
||||||
|
|
||||||
|
<strong>Pop-up menu.</strong> Clicking the secondary mouse button on a part
|
||||||
|
of the picture pops up a menu with <code>Tracker</code>-like options
|
||||||
|
applicable to that file or directory. For directories, the menu includes an
|
||||||
|
option to rescan that directory and its subdirectories (not the entire
|
||||||
|
volume). This is useful since <code>DiskUsage</code> does not monitor changes
|
||||||
|
to the file system. <p>
|
||||||
|
|
||||||
|
<strong>Drag-and-drop.</strong> You can drag files and directories from
|
||||||
|
<code>DiskUsage</code> to other applications (or to the desktop, the trash, etc.)
|
||||||
|
with the primary mouse button. You can drop volumes and folders on
|
||||||
|
<code>DiskUsage</code>'s window to zoom directly to them. <p>
|
||||||
|
|
||||||
|
<strong>Other controls.</strong> The buttons in the top right corner of the
|
||||||
|
<code>DiskUsage</code> window rescan the selected volume and display the
|
||||||
|
documentation (this file). <p>
|
||||||
|
|
||||||
|
<strong>Notes:</strong>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> If the graphical representation of a file or directory comprises less
|
||||||
|
than about two degrees of a circle, it is excluded from the display.
|
||||||
|
|
||||||
|
<li> The number of files that <code>DiskUsage</code> reports for a directory
|
||||||
|
includes files in subdirectories too. A directory counts as a file.
|
||||||
|
|
||||||
|
<li> <code>DiskUsage</code> ignores symbolic links.
|
||||||
|
|
||||||
|
<li> If you don't like the colors <code>DiskUsage</code> uses, you can change
|
||||||
|
them with your favorite resource editor.
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Credits, etc.</h3>
|
||||||
|
|
||||||
|
Thanks to Steffen Gerlach for creating the Windows
|
||||||
|
<a href="http://www.steffengerlach.de/freeware/index.html">Scanner</a>
|
||||||
|
utility, from which I took the idea for using concentric circles to represent
|
||||||
|
disk usage. <p>
|
||||||
|
|
||||||
|
Thanks to the <a href="http://www.vim.org/">Vim</a> development team for
|
||||||
|
creating the best programmer's editor on the planet, with which
|
||||||
|
<code>DiskUsage</code>'s original code and documentation were lovingly
|
||||||
|
hand-crafted. <p>
|
||||||
|
|
||||||
|
Thanks to the folks at <u><font color=#0000ff>B</font><font
|
||||||
|
color=#ff0000>e</font></u> for creating such a kick-butt OS. It has made
|
||||||
|
programming fun again. <p>
|
||||||
|
|
||||||
|
Thanks to Mike Steed for doing the programming. All the code is original. <p>
|
||||||
|
|
||||||
|
<code>DiskUsage</code> used to be called <code>guido</code> by it's original
|
||||||
|
author. The name <code>guido</code> originated from GUI + <code>du</code>
|
||||||
|
-- the command-line disk usage utility. Plus, one of his favorite languages,
|
||||||
|
<a href="http://www.python.org/">Python</a>, was created by a fine programmer
|
||||||
|
named Guido.
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Known problems</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li> You can drop multiple volumes or folders on the icon, but
|
||||||
|
<code>DiskUsage</code> sees only one of them. Why?
|
||||||
|
|
||||||
|
<li> Dropping a floppy disk volume on <code>DiskUsage</code>'s window works, but
|
||||||
|
dropping it on the icon does not work (unless you force it by holding the Ctrl
|
||||||
|
key while you drag). <em>Why?!</em>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>License</h3>
|
||||||
|
|
||||||
|
<code>DiskUsage</code> is Copyright (c) 1999 Mike Steed. You are free to use
|
||||||
|
and distribute this software as long as it is accompanied by this documentation
|
||||||
|
and copyright notice. The software comes with no warranty, etc. <p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 437 B |
@@ -13,6 +13,7 @@ SubInclude HAIKU_TOP src apps cortex ;
|
|||||||
SubInclude HAIKU_TOP src apps deskbar ;
|
SubInclude HAIKU_TOP src apps deskbar ;
|
||||||
SubInclude HAIKU_TOP src apps deskcalc ;
|
SubInclude HAIKU_TOP src apps deskcalc ;
|
||||||
SubInclude HAIKU_TOP src apps diskprobe ;
|
SubInclude HAIKU_TOP src apps diskprobe ;
|
||||||
|
SubInclude HAIKU_TOP src apps diskusage ;
|
||||||
SubInclude HAIKU_TOP src apps drivesetup ;
|
SubInclude HAIKU_TOP src apps drivesetup ;
|
||||||
SubInclude HAIKU_TOP src apps expander ;
|
SubInclude HAIKU_TOP src apps expander ;
|
||||||
SubInclude HAIKU_TOP src apps fontdemo ;
|
SubInclude HAIKU_TOP src apps fontdemo ;
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "App.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <File.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
App::App()
|
||||||
|
: BApplication(kAppSignature),
|
||||||
|
fResources(read_resources(kAppSignature)),
|
||||||
|
fMainWindow(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
App::~App()
|
||||||
|
{
|
||||||
|
delete fResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
App::ArgvReceived(int32 argc, char** argv)
|
||||||
|
{
|
||||||
|
BMessage refsReceived(B_REFS_RECEIVED);
|
||||||
|
for (int32 i = 1; i < argc; i++) {
|
||||||
|
BEntry entry(argv[i], true);
|
||||||
|
entry_ref ref;
|
||||||
|
if (entry.GetRef(&ref) == B_OK)
|
||||||
|
refsReceived.AddRef("refs", &ref);
|
||||||
|
}
|
||||||
|
if (refsReceived.HasRef("refs"))
|
||||||
|
PostMessage(&refsReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
App::RefsReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
fMainWindow->PostMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
App::ReadyToRun()
|
||||||
|
{
|
||||||
|
BRect frame;
|
||||||
|
|
||||||
|
BPath path;
|
||||||
|
BFile settingsFile;
|
||||||
|
BMessage settings;
|
||||||
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|
||||||
|
|| path.Append("DiskUsage") != B_OK
|
||||||
|
|| settingsFile.SetTo(path.Path(), B_READ_ONLY) != B_OK
|
||||||
|
|| settings.Unflatten(&settingsFile) != B_OK
|
||||||
|
|| settings.FindRect("window frame", &frame) != B_OK) {
|
||||||
|
// use default window frame
|
||||||
|
frame.Set(0, 0, kDefaultPieSize, kDefaultPieSize);
|
||||||
|
frame.OffsetTo(50, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
fMainWindow = new MainWindow(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
App::QuitRequested()
|
||||||
|
{
|
||||||
|
// Save the settings.
|
||||||
|
BPath path;
|
||||||
|
BFile settingsFile;
|
||||||
|
BMessage settings;
|
||||||
|
if (settings.AddRect("window frame", fMainWindow->Frame()) != B_OK
|
||||||
|
|| find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|
||||||
|
|| path.Append("DiskUsage") != B_OK
|
||||||
|
|| settingsFile.SetTo(path.Path(), B_WRITE_ONLY) != B_OK
|
||||||
|
|| settings.Flatten(&settingsFile) != B_OK) {
|
||||||
|
fprintf(stderr, "Failed to write application settings.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return BApplication::QuitRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
App app;
|
||||||
|
app.Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef APP_H
|
||||||
|
#define APP_H
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
|
||||||
|
|
||||||
|
class MainWindow;
|
||||||
|
|
||||||
|
class App: public BApplication {
|
||||||
|
public:
|
||||||
|
App();
|
||||||
|
virtual ~App();
|
||||||
|
|
||||||
|
virtual void ArgvReceived(int32 argc, char** argv);
|
||||||
|
virtual void RefsReceived(BMessage* message);
|
||||||
|
|
||||||
|
virtual void ReadyToRun();
|
||||||
|
virtual bool QuitRequested();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BResources* fResources;
|
||||||
|
MainWindow* fMainWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APP_H
|
||||||
|
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#define ASSIGN_RESOURCES
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Roster.h>
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
|
||||||
|
BResources*
|
||||||
|
read_resources(const char* appSignature)
|
||||||
|
{
|
||||||
|
status_t ret = be_app->GetAppInfo(&kAppInfo);
|
||||||
|
if (ret != B_OK) {
|
||||||
|
fprintf(stderr, "Failed to init application info: %s\n",
|
||||||
|
strerror(ret));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BFile file(&kAppInfo.ref, O_RDONLY);
|
||||||
|
ret = file.InitCheck();
|
||||||
|
if (ret != B_OK) {
|
||||||
|
fprintf(stderr, "Failed to init application file to read resources: "
|
||||||
|
"%s\n", strerror(ret));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BResources* r = new BResources(&file);
|
||||||
|
|
||||||
|
#define LoadString(n) (char*)r->LoadResource(B_STRING_TYPE, n, &ignore)
|
||||||
|
#define LoadColor(n) *(rgb_color*)r->LoadResource(B_RGB_COLOR_TYPE, n, &ignore)
|
||||||
|
#define LoadUint8(n) *(uint8*)r->LoadResource(B_UINT8_TYPE, n, &ignore)
|
||||||
|
|
||||||
|
size_t ignore;
|
||||||
|
kVolMenuLabel = LoadString("STR_VM_LABEL");
|
||||||
|
kOneFile = LoadString("STR_1_FILE");
|
||||||
|
kManyFiles = LoadString("STR_N_FILES");
|
||||||
|
kStrRescan = LoadString("STR_RESCAN");
|
||||||
|
kStrScanningX = LoadString("STR_SCN_X");
|
||||||
|
kStrUnavail = LoadString("STR_UNAVAIL");
|
||||||
|
kVolMenuDefault = LoadString("STR_VM_DFLT");
|
||||||
|
kVolPrompt = LoadString("STR_VPROMPT");
|
||||||
|
kMenuGetInfo = LoadString("STR_M_INFO");
|
||||||
|
kMenuOpen = LoadString("STR_M_OPEN");
|
||||||
|
kMenuOpenWith = LoadString("STR_M_OPENW");
|
||||||
|
kMenuNoApps = LoadString("STR_M_NAPPS");
|
||||||
|
kInfoSize = LoadString("STR_SIZE");
|
||||||
|
kInfoInFiles = LoadString("STR_INFILES");
|
||||||
|
kInfoCreated = LoadString("STR_MADE");
|
||||||
|
kInfoModified = LoadString("STR_MOD");
|
||||||
|
kInfoTimeFmt = LoadString("STR_TIMEFMT");
|
||||||
|
kInfoKind = LoadString("STR_KIND");
|
||||||
|
kInfoPath = LoadString("STR_PATH");
|
||||||
|
|
||||||
|
kWindowColor = ui_color(B_PANEL_BACKGROUND_COLOR);
|
||||||
|
kOutlineColor = LoadColor("RGB_PIE_OL");
|
||||||
|
kPieBGColor = LoadColor("RGB_PIE_BG");
|
||||||
|
kEmptySpcColor = LoadColor("RGB_PIE_MT");
|
||||||
|
|
||||||
|
kBasePieColorCount = LoadUint8("N_PIE_COLORS");
|
||||||
|
kBasePieColor = new rgb_color[kBasePieColorCount];
|
||||||
|
for (int i = 0; i < kBasePieColorCount; i++) {
|
||||||
|
char colorName[16] = "RGB_PIE_n";
|
||||||
|
colorName[8] = '1' + i;
|
||||||
|
kBasePieColor[i] = LoadColor(colorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a reference to the help file.
|
||||||
|
BPath path;
|
||||||
|
if (find_directory(B_BEOS_DOCUMENTATION_DIRECTORY, &path) == B_OK
|
||||||
|
&& path.Append(kHelpFileName) == B_OK) {
|
||||||
|
printf("help file =? %s\n", path.Path());
|
||||||
|
BEntry entry(path.Path());
|
||||||
|
kFoundHelpFile = entry.Exists();
|
||||||
|
entry.GetRef(&kHelpFileRef);
|
||||||
|
} else
|
||||||
|
kFoundHelpFile = false;
|
||||||
|
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
size_to_string(off_t byteCount, char* name)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
off_t limit;
|
||||||
|
float divisor;
|
||||||
|
char* format;
|
||||||
|
} scale[] = {
|
||||||
|
{ 0x100000, 1024.0, "%.2f KB" },
|
||||||
|
{ 0x40000000, 1048576.0, "%.2f MB" },
|
||||||
|
{ 0x10000000000ull, 1073741824.0, "%.2f GB" },
|
||||||
|
{ 0x4000000000000ull, 1.09951162778e+12, "%.2f TB" }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (byteCount < 1024) {
|
||||||
|
sprintf(name, "%lld bytes", byteCount);
|
||||||
|
} else {
|
||||||
|
int i = 0;
|
||||||
|
while (byteCount >= scale[i].limit)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
sprintf(name, scale[i].format, byteCount / scale[i].divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <GraphicsDefs.h>
|
||||||
|
#include <Resources.h>
|
||||||
|
#include <Roster.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ASSIGN_RESOURCES
|
||||||
|
# define EXTERN
|
||||||
|
# define EQ(x) =x
|
||||||
|
#else
|
||||||
|
# define EXTERN extern
|
||||||
|
# define EQ(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PUBLIC_CONST extern const
|
||||||
|
|
||||||
|
|
||||||
|
// Resources
|
||||||
|
EXTERN char* kStrRescan;
|
||||||
|
EXTERN char* kStrScanningX;
|
||||||
|
EXTERN char* kStrUnavail;
|
||||||
|
EXTERN char* kVolMenuLabel;
|
||||||
|
EXTERN char* kVolMenuDefault;
|
||||||
|
EXTERN char* kVolPrompt;
|
||||||
|
EXTERN char* kOneFile;
|
||||||
|
EXTERN char* kManyFiles;
|
||||||
|
EXTERN char* kMenuGetInfo;
|
||||||
|
EXTERN char* kMenuOpen;
|
||||||
|
EXTERN char* kMenuOpenWith;
|
||||||
|
EXTERN char* kMenuNoApps;
|
||||||
|
EXTERN char* kInfoSize;
|
||||||
|
EXTERN char* kInfoInFiles;
|
||||||
|
EXTERN char* kInfoCreated;
|
||||||
|
EXTERN char* kInfoModified;
|
||||||
|
EXTERN char* kInfoTimeFmt;
|
||||||
|
EXTERN char* kInfoKind;
|
||||||
|
EXTERN char* kInfoPath;
|
||||||
|
|
||||||
|
EXTERN rgb_color kWindowColor;
|
||||||
|
EXTERN rgb_color kOutlineColor;
|
||||||
|
EXTERN rgb_color kPieBGColor;
|
||||||
|
EXTERN rgb_color kEmptySpcColor;
|
||||||
|
EXTERN int kBasePieColorCount;
|
||||||
|
EXTERN rgb_color *kBasePieColor;
|
||||||
|
|
||||||
|
// Non-resources :)
|
||||||
|
PUBLIC_CONST char* kAppSignature EQ("application/x-vnd.haiku-diskusage");
|
||||||
|
PUBLIC_CONST char* kHelpFileName EQ("apps/diskusage/DiskUsage.html");
|
||||||
|
PUBLIC_CONST char* kPieRectAttrName EQ("mainrect");
|
||||||
|
|
||||||
|
PUBLIC_CONST char* kHelpBtnLabel EQ("?");
|
||||||
|
PUBLIC_CONST char* kEmptyStr EQ("");
|
||||||
|
PUBLIC_CONST char* kNameVolPtr EQ("vol");
|
||||||
|
PUBLIC_CONST char* kNameFilePtr EQ("file");
|
||||||
|
|
||||||
|
PUBLIC_CONST float kSmallHMargin EQ(5.0);
|
||||||
|
PUBLIC_CONST float kSmallVMargin EQ(2.0);
|
||||||
|
PUBLIC_CONST float kButtonMargin EQ(20.0);
|
||||||
|
PUBLIC_CONST float kMinButtonWidth EQ(60.0);
|
||||||
|
|
||||||
|
PUBLIC_CONST float kProgBarWidth EQ(150.0);
|
||||||
|
PUBLIC_CONST float kProgBarHeight EQ(16.0);
|
||||||
|
PUBLIC_CONST float kReportInterval EQ(2.5);
|
||||||
|
|
||||||
|
PUBLIC_CONST float kDefaultPieSize EQ(400.0);
|
||||||
|
PUBLIC_CONST float kPieCenterSize EQ(80.0);
|
||||||
|
PUBLIC_CONST float kPieRingSize EQ(20.0);
|
||||||
|
PUBLIC_CONST float kPieInnerMargin EQ(10.0);
|
||||||
|
PUBLIC_CONST float kPieOuterMargin EQ(10.0);
|
||||||
|
PUBLIC_CONST float kMinSegmentSpan EQ(2.0);
|
||||||
|
PUBLIC_CONST int kLightenFactor EQ(0x12);
|
||||||
|
PUBLIC_CONST float kDragThreshold EQ(5.0);
|
||||||
|
|
||||||
|
EXTERN app_info kAppInfo;
|
||||||
|
EXTERN entry_ref kHelpFileRef;
|
||||||
|
EXTERN bool kFoundHelpFile;
|
||||||
|
|
||||||
|
#define kMenuSelectVol 'gMSV'
|
||||||
|
#define kBtnRescan 'gBRF'
|
||||||
|
#define kBtnHelp 'gHLP'
|
||||||
|
#define kScanRefresh 'gSRF'
|
||||||
|
#define kScanProgress 'gSPR'
|
||||||
|
#define kScanDone 'gSDN'
|
||||||
|
|
||||||
|
// support functions
|
||||||
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
#define deg2rad(x) (2.0 * M_PI * (x) / 360.0)
|
||||||
|
#define rad2deg(x) (360.0 * (x) / (2.0 * M_PI))
|
||||||
|
|
||||||
|
BResources* read_resources(const char* appSignature);
|
||||||
|
void size_to_string(off_t byteCount, char* name);
|
||||||
|
|
||||||
|
#endif // COMMON_H
|
||||||
|
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "ControlsView.h"
|
||||||
|
|
||||||
|
#include <Bitmap.h>
|
||||||
|
#include <Box.h>
|
||||||
|
#include <MenuField.h>
|
||||||
|
#include <MenuItem.h>
|
||||||
|
#include <NodeMonitor.h>
|
||||||
|
#include <PopUpMenu.h>
|
||||||
|
#include <Volume.h>
|
||||||
|
#include <VolumeRoster.h>
|
||||||
|
#include <Window.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeMenuItem: public BMenuItem {
|
||||||
|
public:
|
||||||
|
VolumeMenuItem(BVolume* volume, BMessage* message);
|
||||||
|
virtual ~VolumeMenuItem();
|
||||||
|
|
||||||
|
virtual void GetContentSize(float* width, float* height);
|
||||||
|
virtual void DrawContent();
|
||||||
|
|
||||||
|
BVolume* Volume() const
|
||||||
|
{ return fVolume; }
|
||||||
|
status_t Invoke()
|
||||||
|
{ return BMenuItem::Invoke(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BBitmap* fIcon;
|
||||||
|
BVolume* fVolume;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
VolumeMenuItem::VolumeMenuItem(BVolume* volume, BMessage* message)
|
||||||
|
: BMenuItem(kEmptyStr, message),
|
||||||
|
fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)),
|
||||||
|
fVolume(volume)
|
||||||
|
{
|
||||||
|
char name[B_PATH_NAME_LENGTH];
|
||||||
|
fVolume->GetName(name);
|
||||||
|
SetLabel(name);
|
||||||
|
|
||||||
|
if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) {
|
||||||
|
delete fIcon;
|
||||||
|
fIcon = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VolumeMenuItem::~VolumeMenuItem()
|
||||||
|
{
|
||||||
|
delete fIcon;
|
||||||
|
delete fVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VolumeMenuItem::GetContentSize(float* width, float* height)
|
||||||
|
{
|
||||||
|
*width = be_plain_font->StringWidth(Label());
|
||||||
|
|
||||||
|
struct font_height fh;
|
||||||
|
be_plain_font->GetHeight(&fh);
|
||||||
|
float fontHeight = fh.ascent + fh.descent + fh.leading;
|
||||||
|
if (fIcon) {
|
||||||
|
*height = max(fontHeight, fIcon->Bounds().Height());
|
||||||
|
*width += fIcon->Bounds().Width() + kSmallHMargin;
|
||||||
|
} else
|
||||||
|
*height = fontHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VolumeMenuItem::DrawContent()
|
||||||
|
{
|
||||||
|
if (fIcon) {
|
||||||
|
Menu()->SetDrawingMode(B_OP_OVER);
|
||||||
|
Menu()->MovePenBy(0.0, -1.0);
|
||||||
|
Menu()->DrawBitmap(fIcon);
|
||||||
|
Menu()->SetDrawingMode(B_OP_COPY);
|
||||||
|
Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0);
|
||||||
|
}
|
||||||
|
BMenuItem::DrawContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
class ControlsView::VolumePopup: public BMenuField {
|
||||||
|
public:
|
||||||
|
VolumePopup(BRect r);
|
||||||
|
virtual ~VolumePopup();
|
||||||
|
|
||||||
|
virtual void AttachedToWindow();
|
||||||
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
|
BVolume* FindDeviceFor(dev_t device,
|
||||||
|
bool invoke = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _AddVolume(dev_t device);
|
||||||
|
void _RemoveVolume(dev_t device);
|
||||||
|
|
||||||
|
BVolumeRoster* fVolumeRoster;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ControlsView::VolumePopup::VolumePopup(BRect r)
|
||||||
|
: BMenuField(r, NULL, kVolMenuLabel, new BPopUpMenu(kVolMenuDefault),
|
||||||
|
false, B_FOLLOW_LEFT)
|
||||||
|
{
|
||||||
|
SetViewColor(kWindowColor);
|
||||||
|
SetLowColor(kWindowColor);
|
||||||
|
|
||||||
|
SetDivider(kSmallHMargin + StringWidth(kVolMenuLabel));
|
||||||
|
|
||||||
|
// Populate the menu with the persistent volumes.
|
||||||
|
fVolumeRoster = new BVolumeRoster();
|
||||||
|
|
||||||
|
BVolume tempVolume;
|
||||||
|
while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) {
|
||||||
|
if (tempVolume.IsPersistent()) {
|
||||||
|
BVolume* volume = new BVolume(tempVolume);
|
||||||
|
BMessage* message = new BMessage(kMenuSelectVol);
|
||||||
|
message->AddPointer(kNameVolPtr, volume);
|
||||||
|
VolumeMenuItem *item = new VolumeMenuItem(volume, message);
|
||||||
|
Menu()->AddItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ControlsView::VolumePopup::~VolumePopup()
|
||||||
|
{
|
||||||
|
fVolumeRoster->StopWatching();
|
||||||
|
delete fVolumeRoster;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ControlsView::VolumePopup::AttachedToWindow()
|
||||||
|
{
|
||||||
|
// Begin watching mount and unmount events.
|
||||||
|
fVolumeRoster->StartWatching(BMessenger(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ControlsView::VolumePopup::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what) {
|
||||||
|
case B_NODE_MONITOR:
|
||||||
|
switch (message->FindInt32("opcode")) {
|
||||||
|
case B_DEVICE_MOUNTED:
|
||||||
|
_AddVolume(message->FindInt32("new device"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_DEVICE_UNMOUNTED:
|
||||||
|
_RemoveVolume(message->FindInt32("device"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BMenuField::MessageReceived(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BVolume*
|
||||||
|
ControlsView::VolumePopup::FindDeviceFor(dev_t device, bool invoke)
|
||||||
|
{
|
||||||
|
BVolume* volume = NULL;
|
||||||
|
|
||||||
|
// Iterate through items looking for a BVolume representing this device.
|
||||||
|
for (int i = 0; VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); i++) {
|
||||||
|
if (item->Volume()->Device() == device) {
|
||||||
|
volume = item->Volume();
|
||||||
|
if (invoke)
|
||||||
|
item->Invoke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ControlsView::VolumePopup::_AddVolume(dev_t device)
|
||||||
|
{
|
||||||
|
// Make sure the volume is not already in the menu.
|
||||||
|
for (int i = 0; i < Menu()->CountItems(); i++) {
|
||||||
|
VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
|
||||||
|
if (item->Volume()->Device() == device)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the newly mounted volume to the menu.
|
||||||
|
BVolume* volume = new BVolume(device);
|
||||||
|
BMessage* message = new BMessage(kMenuSelectVol);
|
||||||
|
message->AddPointer(kNameVolPtr, volume);
|
||||||
|
VolumeMenuItem* item = new VolumeMenuItem(volume, message);
|
||||||
|
Menu()->AddItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ControlsView::VolumePopup::_RemoveVolume(dev_t device)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Menu()->CountItems(); i++) {
|
||||||
|
VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i);
|
||||||
|
if (item->Volume()->Device() == device) {
|
||||||
|
// If the volume being removed is currently selected, prompt for a
|
||||||
|
// different volume.
|
||||||
|
if (item->IsMarked()) {
|
||||||
|
// update the displayed volume label now that there is no marked
|
||||||
|
// item:
|
||||||
|
Menu()->Superitem()->SetLabel(kVolMenuDefault);
|
||||||
|
|
||||||
|
BMessage messae(kMenuSelectVol);
|
||||||
|
messae.AddPointer(kNameVolPtr, NULL);
|
||||||
|
Window()->PostMessage(&messae);
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu()->RemoveItem(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
ControlsView::ControlsView(BRect r)
|
||||||
|
: BView(r, NULL, B_FOLLOW_LEFT_RIGHT, 0)
|
||||||
|
{
|
||||||
|
SetViewColor(kWindowColor);
|
||||||
|
|
||||||
|
r.top += kSmallVMargin;
|
||||||
|
r.right -= kSmallHMargin;
|
||||||
|
float buttonWidth = kButtonMargin + StringWidth(kHelpBtnLabel);
|
||||||
|
r.left = r.right - buttonWidth;
|
||||||
|
BButton* helpButton = new BButton(r, NULL, kHelpBtnLabel, new BMessage(kBtnHelp),
|
||||||
|
B_FOLLOW_RIGHT);
|
||||||
|
if (!kFoundHelpFile)
|
||||||
|
helpButton->SetEnabled(false);
|
||||||
|
|
||||||
|
r.right = r.left - kSmallHMargin;
|
||||||
|
buttonWidth = kButtonMargin + StringWidth(kStrRescan);
|
||||||
|
r.left = r.right - max(kMinButtonWidth, buttonWidth);
|
||||||
|
fRescanButton = new BButton(r, NULL, kStrRescan, new BMessage(kBtnRescan),
|
||||||
|
B_FOLLOW_RIGHT);
|
||||||
|
|
||||||
|
fVolumePopup = new VolumePopup(
|
||||||
|
BRect(kSmallHMargin, kSmallVMargin, r.left - kSmallHMargin, kSmallVMargin));
|
||||||
|
|
||||||
|
float width, height;
|
||||||
|
fRescanButton->GetPreferredSize(&width, &height);
|
||||||
|
ResizeTo(Bounds().Width(), height + 6.0);
|
||||||
|
|
||||||
|
// Horizontal divider
|
||||||
|
BRect r = Bounds();
|
||||||
|
r.top = r.bottom - 1.0;
|
||||||
|
r.left -= 5.0; r.right += 5.0;
|
||||||
|
BBox* divider = new BBox(r, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW);
|
||||||
|
|
||||||
|
AddChild(fVolumePopup);
|
||||||
|
AddChild(divider);
|
||||||
|
AddChild(fRescanButton);
|
||||||
|
AddChild(helpButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ControlsView::~ControlsView()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BVolume*
|
||||||
|
ControlsView::FindDeviceFor(dev_t device, bool invoke)
|
||||||
|
{
|
||||||
|
return fVolumePopup->FindDeviceFor(device, invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef CONTROLS_VIEW_H
|
||||||
|
#define CONTROLS_VIEW_H
|
||||||
|
|
||||||
|
#include <Button.h>
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BVolume;
|
||||||
|
|
||||||
|
class ControlsView: public BView {
|
||||||
|
public:
|
||||||
|
ControlsView(BRect r);
|
||||||
|
virtual ~ControlsView();
|
||||||
|
|
||||||
|
void SetRescanEnabled(bool enable)
|
||||||
|
{ fRescanButton->SetEnabled(enable); }
|
||||||
|
|
||||||
|
BVolume* FindDeviceFor(dev_t device,
|
||||||
|
bool invoke = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class VolumePopup;
|
||||||
|
|
||||||
|
VolumePopup* fVolumePopup;
|
||||||
|
BButton* fRescanButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CONTROLS_VIEW_H
|
||||||
|
|
||||||
@@ -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";
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "InfoWindow.h"
|
||||||
|
|
||||||
|
#include <pair.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <StringView.h>
|
||||||
|
#include <Bitmap.h>
|
||||||
|
#include <NodeInfo.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Snapshot.h"
|
||||||
|
|
||||||
|
|
||||||
|
LeftView::LeftView(BRect frame, BBitmap* icon)
|
||||||
|
: BView(frame, NULL, B_FOLLOW_NONE, B_WILL_DRAW),
|
||||||
|
fIcon(icon)
|
||||||
|
{
|
||||||
|
SetViewColor(tint_color(kWindowColor, B_LIGHTEN_1_TINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LeftView::~LeftView()
|
||||||
|
{
|
||||||
|
delete fIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LeftView::Draw(BRect updateRect)
|
||||||
|
{
|
||||||
|
float right = Bounds().Width() - kSmallHMargin;
|
||||||
|
BRect iconRect(right - 31.0, kSmallVMargin, right, kSmallVMargin + 31.0);
|
||||||
|
if (updateRect.Intersects(iconRect)) {
|
||||||
|
SetDrawingMode(B_OP_OVER);
|
||||||
|
DrawBitmap(fIcon, iconRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InfoWin::InfoWin(BPoint p, FileInfo *f, BWindow* parent)
|
||||||
|
: BWindow(BRect(p, p), kEmptyStr, B_FLOATING_WINDOW_LOOK,
|
||||||
|
B_FLOATING_SUBSET_WINDOW_FEEL,
|
||||||
|
B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE)
|
||||||
|
{
|
||||||
|
AddToSubset(parent);
|
||||||
|
|
||||||
|
typedef pair<string, string> Item;
|
||||||
|
typedef vector<Item> InfoList;
|
||||||
|
|
||||||
|
char name[B_PATH_NAME_LENGTH];
|
||||||
|
strcpy(name, f->ref.name);
|
||||||
|
strcat(name, " info");
|
||||||
|
SetTitle(name);
|
||||||
|
|
||||||
|
InfoList info;
|
||||||
|
Item item;
|
||||||
|
|
||||||
|
// Size
|
||||||
|
size_to_string(f->size, name);
|
||||||
|
if (f->count > 0) {
|
||||||
|
// This is a directory.
|
||||||
|
char str[64];
|
||||||
|
sprintf(str, kInfoInFiles, f->count);
|
||||||
|
strcat(name, str);
|
||||||
|
}
|
||||||
|
info.push_back(Item(kInfoSize, name));
|
||||||
|
|
||||||
|
// Created & modified dates
|
||||||
|
BEntry entry(&f->ref);
|
||||||
|
time_t t;
|
||||||
|
entry.GetCreationTime(&t);
|
||||||
|
strftime(name, 64, kInfoTimeFmt, localtime(&t));
|
||||||
|
info.push_back(Item(kInfoCreated, name));
|
||||||
|
entry.GetModificationTime(&t);
|
||||||
|
strftime(name, 64, kInfoTimeFmt, localtime(&t));
|
||||||
|
info.push_back(Item(kInfoModified, name));
|
||||||
|
|
||||||
|
// Kind
|
||||||
|
BMimeType* type = f->Type();
|
||||||
|
type->GetShortDescription(name);
|
||||||
|
info.push_back(Item(kInfoKind, name));
|
||||||
|
delete type;
|
||||||
|
|
||||||
|
// Path
|
||||||
|
string path;
|
||||||
|
f->GetPath(path);
|
||||||
|
info.push_back(Item(kInfoPath, path));
|
||||||
|
|
||||||
|
// Icon
|
||||||
|
BBitmap *icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32);
|
||||||
|
entry_ref ref;
|
||||||
|
entry.GetRef(&ref);
|
||||||
|
BNodeInfo::GetTrackerIcon(&ref, icon, B_LARGE_ICON);
|
||||||
|
|
||||||
|
// Compute the window size and add the views.
|
||||||
|
BFont smallFont(be_plain_font);
|
||||||
|
smallFont.SetSize(floorf(smallFont.Size() * 0.95));
|
||||||
|
|
||||||
|
struct font_height fh;
|
||||||
|
smallFont.GetHeight(&fh);
|
||||||
|
float fontHeight = fh.ascent + fh.descent + fh.leading;
|
||||||
|
|
||||||
|
float leftWidth = 32.0;
|
||||||
|
float rightWidth = 0.0;
|
||||||
|
InfoList::iterator i = info.begin();
|
||||||
|
while (i != info.end()) {
|
||||||
|
float w = smallFont.StringWidth((*i).first.c_str()) + 2.0 * kSmallHMargin;
|
||||||
|
leftWidth = max(leftWidth, w);
|
||||||
|
w = smallFont.StringWidth((*i).second.c_str()) + 2.0 * kSmallHMargin;
|
||||||
|
rightWidth = max(rightWidth, w);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
float winHeight = 32.0 + 4.0 * kSmallVMargin + 5.0 * (fontHeight + kSmallVMargin);
|
||||||
|
float winWidth = leftWidth + rightWidth;
|
||||||
|
ResizeTo(winWidth, winHeight);
|
||||||
|
|
||||||
|
LeftView *leftView = new LeftView(BRect(0.0, 0.0, leftWidth, winHeight), icon);
|
||||||
|
BView *rightView = new BView(
|
||||||
|
BRect(leftWidth + 1.0, 0.0, winWidth, winHeight), NULL,
|
||||||
|
B_FOLLOW_NONE, B_WILL_DRAW);
|
||||||
|
|
||||||
|
AddChild(leftView);
|
||||||
|
AddChild(rightView);
|
||||||
|
|
||||||
|
BStringView *sv = new BStringView(
|
||||||
|
BRect(kSmallHMargin, kSmallVMargin, rightView->Bounds().Width(), kSmallVMargin + 30.0),
|
||||||
|
NULL, f->ref.name, B_FOLLOW_ALL);
|
||||||
|
|
||||||
|
BFont largeFont(be_plain_font);
|
||||||
|
largeFont.SetSize(ceilf(largeFont.Size() * 1.1));
|
||||||
|
sv->SetFont(&largeFont);
|
||||||
|
rightView->AddChild(sv);
|
||||||
|
|
||||||
|
float y = 32.0 + 4.0 * kSmallVMargin;
|
||||||
|
i = info.begin();
|
||||||
|
while (i != info.end()) {
|
||||||
|
sv = new BStringView(
|
||||||
|
BRect(kSmallHMargin, y, leftView->Bounds().Width(), y + fontHeight),
|
||||||
|
NULL, (*i).first.c_str());
|
||||||
|
sv->SetFont(&smallFont);
|
||||||
|
sv->SetAlignment(B_ALIGN_RIGHT);
|
||||||
|
sv->SetHighColor(kBasePieColor[1]); // arbitrary
|
||||||
|
leftView->AddChild(sv);
|
||||||
|
|
||||||
|
sv = new BStringView(
|
||||||
|
BRect(kSmallHMargin, y, rightView->Bounds().Width(), y + fontHeight),
|
||||||
|
NULL, (*i).second.c_str());
|
||||||
|
sv->SetFont(&smallFont);
|
||||||
|
rightView->AddChild(sv);
|
||||||
|
|
||||||
|
y += fontHeight + kSmallVMargin;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InfoWin::~InfoWin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef INFO_WINDOW_H
|
||||||
|
#define INFO_WINDOW_H
|
||||||
|
|
||||||
|
#include <Window.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct FileInfo;
|
||||||
|
|
||||||
|
class LeftView: public BView {
|
||||||
|
public:
|
||||||
|
LeftView(BRect frame, BBitmap* icon);
|
||||||
|
virtual ~LeftView();
|
||||||
|
|
||||||
|
virtual void Draw(BRect updateRect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BBitmap* fIcon;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class InfoWin: public BWindow {
|
||||||
|
public:
|
||||||
|
InfoWin(BPoint location, FileInfo* info,
|
||||||
|
BWindow* parent);
|
||||||
|
virtual ~InfoWin();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INFO_WINDOW_H
|
||||||
|
|
||||||
@@ -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
|
||||||
|
;
|
||||||
|
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <Roster.h>
|
||||||
|
#include <Screen.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "ControlsView.h"
|
||||||
|
#include "PieView.h"
|
||||||
|
#include "StatusView.h"
|
||||||
|
|
||||||
|
|
||||||
|
const float kMinWinSize = 275.0;
|
||||||
|
|
||||||
|
|
||||||
|
MainWindow::MainWindow(BRect pieRect)
|
||||||
|
: BWindow(pieRect, "DiskUsage", B_TITLED_WINDOW,
|
||||||
|
B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE)
|
||||||
|
{
|
||||||
|
BRect r = Bounds();
|
||||||
|
r.bottom = r.top + 1.0;
|
||||||
|
fControlsView = new ControlsView(r);
|
||||||
|
float cvHeight = fControlsView->Bounds().Height();
|
||||||
|
AddChild(fControlsView);
|
||||||
|
|
||||||
|
r = Bounds();
|
||||||
|
r.top = r.bottom - 1.0;
|
||||||
|
fStatusView = new StatusView(r);
|
||||||
|
float svHeight = fStatusView->Bounds().Height();
|
||||||
|
|
||||||
|
float winHeight = pieRect.Height() + cvHeight + svHeight;
|
||||||
|
fStatusView->MoveTo(0.0, winHeight - svHeight);
|
||||||
|
ResizeTo(r.Width(), winHeight);
|
||||||
|
|
||||||
|
AddChild(fStatusView);
|
||||||
|
|
||||||
|
r = fControlsView->Frame();
|
||||||
|
r.top = r.bottom + 1.0;
|
||||||
|
r.bottom = fStatusView->Frame().top - 1.0;
|
||||||
|
fPieView = new PieView(r, this);
|
||||||
|
AddChild(fPieView);
|
||||||
|
|
||||||
|
Show();
|
||||||
|
|
||||||
|
// Note: The following code is semi-broken because BScreen::Frame()
|
||||||
|
// returns incorrect dimensions for the G200 in 1152x864 mode. I reported
|
||||||
|
// this bug, and Be said it's not a bug -- the Matrox driver actually uses
|
||||||
|
// a resolution of 1152x900 in that mode. Oh well.
|
||||||
|
Lock();
|
||||||
|
float extraHeight = fControlsView->Bounds().Height() + fStatusView->Bounds().Height();
|
||||||
|
float minHeight = kMinWinSize + extraHeight;
|
||||||
|
float maxHeight = BScreen(this).Frame().Height();
|
||||||
|
float maxWidth = maxHeight - extraHeight;
|
||||||
|
Unlock();
|
||||||
|
|
||||||
|
SetSizeLimits(kMinWinSize, maxWidth, minHeight, maxHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what) {
|
||||||
|
case kBtnRescan:
|
||||||
|
case kMenuSelectVol:
|
||||||
|
case B_REFS_RECEIVED:
|
||||||
|
fPieView->MessageReceived(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kBtnHelp:
|
||||||
|
be_roster->Launch(&kHelpFileRef);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BWindow::MessageReceived(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::Zoom(BPoint origin, float width, float height)
|
||||||
|
{
|
||||||
|
width = Frame().Width();
|
||||||
|
height = Frame().Height();
|
||||||
|
if (_FixAspectRatio(&width, &height))
|
||||||
|
SetZoomLimits(width, height);
|
||||||
|
|
||||||
|
BWindow::Zoom(Frame().LeftTop(), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::FrameResized(float width, float height)
|
||||||
|
{
|
||||||
|
if (_FixAspectRatio(&width, &height))
|
||||||
|
ResizeTo(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
MainWindow::QuitRequested()
|
||||||
|
{
|
||||||
|
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::ShowInfo(const FileInfo* info)
|
||||||
|
{
|
||||||
|
fStatusView->Show(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::SetRescanEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
fControlsView->SetRescanEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BVolume*
|
||||||
|
MainWindow::FindDeviceFor(dev_t device, bool invoke)
|
||||||
|
{
|
||||||
|
return fControlsView->FindDeviceFor(device, invoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
MainWindow::_FixAspectRatio(float* width, float* height)
|
||||||
|
{
|
||||||
|
float ctrlViewHeight = fControlsView->Bounds().Height();
|
||||||
|
float statusViewHeight = fStatusView->Bounds().Height();
|
||||||
|
|
||||||
|
float newPieSize = *height - ctrlViewHeight - statusViewHeight;
|
||||||
|
if (*width < newPieSize)
|
||||||
|
newPieSize = *width;
|
||||||
|
|
||||||
|
float newWidth = newPieSize;
|
||||||
|
float newHeight = newPieSize + ctrlViewHeight + statusViewHeight;
|
||||||
|
if (*width != newWidth || *height != newHeight) {
|
||||||
|
*width = newWidth;
|
||||||
|
*height = newHeight;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef MAIN_WINDOW_H
|
||||||
|
#define MAIN_WINDOW_H
|
||||||
|
|
||||||
|
#include <Window.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BVolume;
|
||||||
|
class ControlsView;
|
||||||
|
struct FileInfo;
|
||||||
|
class PieView;
|
||||||
|
class StatusView;
|
||||||
|
|
||||||
|
class MainWindow: public BWindow {
|
||||||
|
public:
|
||||||
|
MainWindow(BRect pieRect);
|
||||||
|
virtual ~MainWindow();
|
||||||
|
|
||||||
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
virtual void Zoom(BPoint origin, float width, float height);
|
||||||
|
virtual void FrameResized(float width, float height);
|
||||||
|
virtual bool QuitRequested();
|
||||||
|
|
||||||
|
void ShowInfo(const FileInfo* info);
|
||||||
|
void SetRescanEnabled(bool enabled);
|
||||||
|
BVolume* FindDeviceFor(dev_t device,
|
||||||
|
bool invoke = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _FixAspectRatio(float* width, float* height);
|
||||||
|
|
||||||
|
ControlsView* fControlsView;
|
||||||
|
PieView* fPieView;
|
||||||
|
StatusView* fStatusView;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAIN_WINDOW_H
|
||||||
@@ -0,0 +1,817 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "PieView.h"
|
||||||
|
|
||||||
|
#include <fs_info.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <AppFileInfo.h>
|
||||||
|
#include <Bitmap.h>
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <File.h>
|
||||||
|
#include <MenuItem.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <PopUpMenu.h>
|
||||||
|
#include <Roster.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <Volume.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "InfoWindow.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
|
#include "Scanner.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const int32 kIdxGetInfo = 0;
|
||||||
|
static const int32 kIdxOpen = 1;
|
||||||
|
static const int32 kIdxOpenWith = 2;
|
||||||
|
static const int32 kIdxRescan = 3;
|
||||||
|
|
||||||
|
// TODO: It would be nice to make a common base class for AppMenuItem and
|
||||||
|
// VolMenuItem (menu items that include an icon).
|
||||||
|
|
||||||
|
class AppMenuItem: public BMenuItem {
|
||||||
|
public:
|
||||||
|
AppMenuItem(const char* appSig, int category);
|
||||||
|
virtual ~AppMenuItem();
|
||||||
|
|
||||||
|
virtual void GetContentSize(float* _width, float* _height);
|
||||||
|
virtual void DrawContent();
|
||||||
|
|
||||||
|
int Category() const
|
||||||
|
{ return fCategory; }
|
||||||
|
const entry_ref* AppRef() const
|
||||||
|
{ return &fAppRef; }
|
||||||
|
bool IsValid() const
|
||||||
|
{ return fIsValid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fCategory;
|
||||||
|
BBitmap* fIcon;
|
||||||
|
entry_ref fAppRef;
|
||||||
|
bool fIsValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AppMenuItem::AppMenuItem(const char* appSig, int category):
|
||||||
|
BMenuItem(kEmptyStr, NULL), fCategory(category), fIcon(NULL), fIsValid(false)
|
||||||
|
{
|
||||||
|
if (be_roster->FindApp(appSig, &fAppRef) == B_NO_ERROR) {
|
||||||
|
fIcon = new BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_RGBA32);
|
||||||
|
if (BNodeInfo::GetTrackerIcon(&fAppRef, fIcon, B_MINI_ICON) == B_OK) {
|
||||||
|
BEntry appEntry(&fAppRef);
|
||||||
|
if (appEntry.InitCheck() == B_OK) {
|
||||||
|
char name[B_FILE_NAME_LENGTH];
|
||||||
|
appEntry.GetName(name);
|
||||||
|
SetLabel(name);
|
||||||
|
fIsValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AppMenuItem::~AppMenuItem()
|
||||||
|
{
|
||||||
|
delete fIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AppMenuItem::GetContentSize(float* _width, float* _height)
|
||||||
|
{
|
||||||
|
if (_width)
|
||||||
|
*_width = fIcon->Bounds().Width() + be_plain_font->StringWidth(Label());
|
||||||
|
|
||||||
|
if (_height) {
|
||||||
|
struct font_height fh;
|
||||||
|
be_plain_font->GetHeight(&fh);
|
||||||
|
float fontHeight = ceilf(fh.ascent) + ceilf(fh.descent)
|
||||||
|
+ ceilf(fh.leading);
|
||||||
|
*_height = max(fontHeight, fIcon->Bounds().Height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AppMenuItem::DrawContent()
|
||||||
|
{
|
||||||
|
Menu()->SetDrawingMode(B_OP_OVER);
|
||||||
|
Menu()->MovePenBy(0.0, -1.0);
|
||||||
|
Menu()->DrawBitmap(fIcon);
|
||||||
|
Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0);
|
||||||
|
BMenuItem::DrawContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - PieView
|
||||||
|
|
||||||
|
|
||||||
|
PieView::PieView(BRect frame, MainWindow* window)
|
||||||
|
: BView(frame, NULL, B_FOLLOW_ALL,
|
||||||
|
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_SUBPIXEL_PRECISE),
|
||||||
|
fWindow(window),
|
||||||
|
fScanners(),
|
||||||
|
fCurrentVolume(NULL),
|
||||||
|
fMouseOverInfo(),
|
||||||
|
fClicked(false),
|
||||||
|
fDragging(false)
|
||||||
|
{
|
||||||
|
SetViewColor(B_TRANSPARENT_COLOR);
|
||||||
|
SetLowColor(kWindowColor);
|
||||||
|
|
||||||
|
fMouseOverMenu = new BPopUpMenu(kEmptyStr, false, false);
|
||||||
|
fMouseOverMenu->AddItem(new BMenuItem(kMenuGetInfo, NULL), kIdxGetInfo);
|
||||||
|
fMouseOverMenu->AddItem(new BMenuItem(kMenuOpen, NULL), kIdxOpen);
|
||||||
|
|
||||||
|
fFileUnavailableMenu = new BPopUpMenu(kEmptyStr, false, false);
|
||||||
|
BMenuItem* item = new BMenuItem(kStrUnavail, NULL);
|
||||||
|
item->SetEnabled(false);
|
||||||
|
fFileUnavailableMenu->AddItem(item);
|
||||||
|
|
||||||
|
BFont font;
|
||||||
|
GetFont(&font);
|
||||||
|
font.SetSize(ceilf(font.Size() * 1.33));
|
||||||
|
font.SetFace(B_BOLD_FACE);
|
||||||
|
SetFont(&font);
|
||||||
|
|
||||||
|
struct font_height fh;
|
||||||
|
font.GetHeight(&fh);
|
||||||
|
fFontHeight = ceilf(fh.ascent) + ceilf(fh.descent) + ceilf(fh.leading);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PieView::~PieView()
|
||||||
|
{
|
||||||
|
delete fMouseOverMenu;
|
||||||
|
delete fFileUnavailableMenu;
|
||||||
|
|
||||||
|
while (fScanners.size() != 0) {
|
||||||
|
ScannerMap::iterator i = fScanners.begin();
|
||||||
|
(*i).second->RequestQuit();
|
||||||
|
fScanners.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what) {
|
||||||
|
case B_SIMPLE_DATA:
|
||||||
|
case B_REFS_RECEIVED:
|
||||||
|
{
|
||||||
|
entry_ref ref;
|
||||||
|
for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
|
||||||
|
BEntry entry(&ref, true);
|
||||||
|
_HandleArg(&entry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kBtnRescan:
|
||||||
|
if (fCurrentVolume != NULL) {
|
||||||
|
fScanners[fCurrentVolume]->Refresh();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kMenuSelectVol:
|
||||||
|
{
|
||||||
|
BVolume* volume;
|
||||||
|
if (message->FindPointer(kNameVolPtr, (void**)&volume) == B_OK)
|
||||||
|
_ShowVolume(volume);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kScanProgress:
|
||||||
|
case kScanDone:
|
||||||
|
{
|
||||||
|
BVolume* volume;
|
||||||
|
if (message->FindPointer(kNameVolPtr, (void**)&volume) == B_OK
|
||||||
|
&& volume == fCurrentVolume) {
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
BView::MessageReceived(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::MouseDown(BPoint where)
|
||||||
|
{
|
||||||
|
uint32 buttons;
|
||||||
|
BMessage* current = Window()->CurrentMessage();
|
||||||
|
if (current->FindInt32("buttons", (int32*)&buttons) != B_OK)
|
||||||
|
buttons = B_PRIMARY_MOUSE_BUTTON;
|
||||||
|
|
||||||
|
FileInfo* info = _FileAt(where);
|
||||||
|
if (info == NULL || info->pseudo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
|
||||||
|
fClicked = true;
|
||||||
|
fDragStart = where;
|
||||||
|
fClickedFile = info;
|
||||||
|
SetMouseEventMask(B_POINTER_EVENTS);
|
||||||
|
} else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
|
||||||
|
where = ConvertToScreen(where);
|
||||||
|
_ShowContextMenu(info, where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::MouseUp(BPoint where)
|
||||||
|
{
|
||||||
|
// If the primary button was released and there was no dragging happening,
|
||||||
|
// just zoom in or out.
|
||||||
|
if (fClicked && !fDragging) {
|
||||||
|
Scanner* scanner = fScanners[fCurrentVolume];
|
||||||
|
FileInfo* info = _FileAt(where);
|
||||||
|
if (info != NULL) {
|
||||||
|
if (info == scanner->CurrentDir()) {
|
||||||
|
scanner->ChangeDir(info->parent);
|
||||||
|
Invalidate();
|
||||||
|
} else if (info->children.size() > 0) {
|
||||||
|
scanner->ChangeDir(info);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fClicked = false;
|
||||||
|
fDragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
|
||||||
|
{
|
||||||
|
if (fClicked) {
|
||||||
|
// Primary mouse button is down.
|
||||||
|
if (fDragging)
|
||||||
|
return;
|
||||||
|
// If the mouse has moved far enough, initiate dragging.
|
||||||
|
BPoint diff = where - fDragStart;
|
||||||
|
float distance = sqrtf(diff.x * diff.x + diff.y * diff.x);
|
||||||
|
if (distance > kDragThreshold) {
|
||||||
|
fDragging = true;
|
||||||
|
|
||||||
|
BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32);
|
||||||
|
if (BNodeInfo::GetTrackerIcon(&fClickedFile->ref, icon,
|
||||||
|
B_LARGE_ICON) == B_OK) {
|
||||||
|
BMessage msg(B_SIMPLE_DATA);
|
||||||
|
msg.AddRef("refs", &fClickedFile->ref);
|
||||||
|
DragMessage(&msg, icon, B_OP_BLEND, BPoint(15.0, 15.0));
|
||||||
|
} else
|
||||||
|
delete icon;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Mouse button is not down, display file info.
|
||||||
|
if (transit == B_EXITED_VIEW) {
|
||||||
|
// Clear status view
|
||||||
|
fWindow->ShowInfo(NULL);
|
||||||
|
} else {
|
||||||
|
// Display file information.
|
||||||
|
fWindow->ShowInfo(_FileAt(where));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::Draw(BRect updateRect)
|
||||||
|
{
|
||||||
|
if (fScanners.find(fCurrentVolume) != fScanners.end()) {
|
||||||
|
// There is a current volume.
|
||||||
|
Scanner* scanner = fScanners[fCurrentVolume];
|
||||||
|
if (scanner->IsBusy()) {
|
||||||
|
// Show progress of scanning.
|
||||||
|
_DrawProgressBar(updateRect);
|
||||||
|
fWindow->SetRescanEnabled(false);
|
||||||
|
} else {
|
||||||
|
_DrawPieChart(updateRect);
|
||||||
|
fWindow->SetRescanEnabled(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No volume has been selected, so display a prompt for one.
|
||||||
|
FillRect(updateRect, B_SOLID_LOW);
|
||||||
|
BRect b = Bounds();
|
||||||
|
float strWidth = StringWidth(kVolPrompt);
|
||||||
|
float bx = (b.left + b.Width() - strWidth) / 2.0;
|
||||||
|
float by = (b.top + b.Height()) / 2.0;
|
||||||
|
SetHighColor(0x00, 0x00, 0x00);
|
||||||
|
DrawString(kVolPrompt, BPoint(bx, by));
|
||||||
|
fWindow->SetRescanEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - private
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_HandleArg(const BEntry* entry)
|
||||||
|
{
|
||||||
|
// Going through these seemingly pointless gyrations (instead of getting
|
||||||
|
// the dev_t directly from the BEntry) allows us to process volumes
|
||||||
|
// properly. It looks like a dropped volume refers to dev_t 1 (why?), but
|
||||||
|
// getting the dev_t from the path allows us to get at the dev_t where the
|
||||||
|
// root directory actually lives.
|
||||||
|
BPath path;
|
||||||
|
entry->GetPath(&path);
|
||||||
|
dev_t device = dev_for_path(path.Path());
|
||||||
|
|
||||||
|
// Set the desired path in the scanner.
|
||||||
|
Scanner* scanner = NULL;
|
||||||
|
ScannerMap::iterator i = fScanners.begin();
|
||||||
|
while (i != fScanners.end()) {
|
||||||
|
if ((*i).second->Device() == device) {
|
||||||
|
scanner = (*i).second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scanner == NULL) {
|
||||||
|
BVolume* volume = fWindow->FindDeviceFor(device);
|
||||||
|
if (volume != NULL)
|
||||||
|
scanner = fScanners[volume] = new Scanner(volume, this);
|
||||||
|
}
|
||||||
|
if (scanner != NULL) {
|
||||||
|
string desiredPath(path.Path());
|
||||||
|
scanner->SetDesiredPath(desiredPath);
|
||||||
|
|
||||||
|
// Select the volume on the menu.
|
||||||
|
fWindow->FindDeviceFor(device, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_ShowVolume(BVolume* volume)
|
||||||
|
{
|
||||||
|
if (volume != NULL) {
|
||||||
|
if (fScanners.find(volume) == fScanners.end())
|
||||||
|
fScanners[volume] = new Scanner(volume, this);
|
||||||
|
|
||||||
|
Scanner* scanner = fScanners[volume];
|
||||||
|
if (scanner->Snapshot() == NULL)
|
||||||
|
scanner->Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
fCurrentVolume = volume;
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_DrawProgressBar(BRect updateRect)
|
||||||
|
{
|
||||||
|
// Show the progress of the scanning operation.
|
||||||
|
|
||||||
|
fMouseOverInfo.clear();
|
||||||
|
|
||||||
|
FillRect(updateRect, B_SOLID_LOW);
|
||||||
|
|
||||||
|
// Draw the progress bar.
|
||||||
|
BRect b = Bounds();
|
||||||
|
float bx = floorf((b.left + b.Width() - kProgBarWidth) / 2.0);
|
||||||
|
float by = floorf((b.top + b.Height() - kProgBarHeight) / 2.0);
|
||||||
|
float ex = bx + kProgBarWidth;
|
||||||
|
float ey = by + kProgBarHeight;
|
||||||
|
SetPenSize(1.0);
|
||||||
|
SetHighColor(tint_color(kWindowColor, B_LIGHTEN_2_TINT));
|
||||||
|
StrokeLine(BPoint(bx, ey), BPoint(ex, ey));
|
||||||
|
StrokeLine(BPoint(ex, by), BPoint(ex, ey));
|
||||||
|
SetHighColor(tint_color(kWindowColor, B_DARKEN_2_TINT));
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(ex, by));
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(bx, ey));
|
||||||
|
|
||||||
|
bx += 1.0; by += 1.0;
|
||||||
|
ex -= 1.0; ey -= 1.0;
|
||||||
|
float mx = bx + floorf((kProgBarWidth - 2.0)
|
||||||
|
* fScanners[fCurrentVolume]->Progress() / 100.0 + 0.5);
|
||||||
|
SetHighColor(tint_color(kWindowColor, B_DARKEN_1_TINT));
|
||||||
|
FillRect(BRect(mx, by, ex, ey));
|
||||||
|
|
||||||
|
SetHighColor(0x00, 0x20, 0x90);
|
||||||
|
StrokeLine(BPoint(bx, ey), BPoint(mx, ey));
|
||||||
|
StrokeLine(BPoint(mx, by), BPoint(mx, ey));
|
||||||
|
SetHighColor(0x00, 0xc0, 0xff);
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(mx, by));
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(bx, ey));
|
||||||
|
|
||||||
|
bx += 1.0; by += 1.0;
|
||||||
|
mx -= 1.0; ey -= 1.0;
|
||||||
|
SetHighColor(0x00, 0x80, 0xf0);
|
||||||
|
FillRect(BRect(bx, by, mx, ey));
|
||||||
|
|
||||||
|
// Tell what we are doing.
|
||||||
|
const char* task = fScanners[fCurrentVolume]->Task();
|
||||||
|
float strWidth = StringWidth(task);
|
||||||
|
bx = (b.left + b.Width() - strWidth) / 2.0;
|
||||||
|
by -= fFontHeight + 2.0 * kSmallVMargin;
|
||||||
|
SetHighColor(0, 0, 0);
|
||||||
|
DrawString(task, BPoint(bx, by));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_DrawPieChart(BRect updateRect)
|
||||||
|
{
|
||||||
|
BRect pieRect = Bounds();
|
||||||
|
pieRect.InsetBy(kPieOuterMargin, kPieOuterMargin);
|
||||||
|
if (!updateRect.Intersects(pieRect))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetHighColor(kPieBGColor);
|
||||||
|
FillRect(updateRect);
|
||||||
|
|
||||||
|
int colorIdx = 0;
|
||||||
|
FileInfo* currentDir = fScanners[fCurrentVolume]->CurrentDir();
|
||||||
|
FileInfo* parent = currentDir;
|
||||||
|
while (parent != NULL) {
|
||||||
|
parent = parent->parent;
|
||||||
|
colorIdx++;
|
||||||
|
}
|
||||||
|
_DrawDirectory(currentDir, 0.0, 0.0, colorIdx % kBasePieColorCount, 0);
|
||||||
|
|
||||||
|
// This is just for the case when the mouse hovers over the view
|
||||||
|
// while the scanning process is running and then does not move
|
||||||
|
// until after the results are shown. In that case the info will
|
||||||
|
// not automatically update. (TODO: fix this and put into MessageReceived()
|
||||||
|
// kScanDone - currently, fileAt() returns NULL (mouseoverinfo...))
|
||||||
|
BPoint where;
|
||||||
|
uint32 ignore;
|
||||||
|
GetMouse(&where, &ignore, false);
|
||||||
|
fWindow->ShowInfo(_FileAt(where));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float
|
||||||
|
PieView::_DrawDirectory(FileInfo* info, float parentSpan, float beginAngle,
|
||||||
|
int colorIdx, int level)
|
||||||
|
{
|
||||||
|
BRect b = Bounds();
|
||||||
|
if (b.Width() < 2.0 * (kPieCenterSize + level * kPieRingSize
|
||||||
|
+ kPieOuterMargin + kPieInnerMargin)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeSnapshot* snapshot = fScanners[fCurrentVolume]->Snapshot();
|
||||||
|
|
||||||
|
float cx = floorf(b.left + b.Width() / 2.0 + 0.5);
|
||||||
|
float cy = floorf(b.top + b.Height() / 2.0 + 0.5);
|
||||||
|
|
||||||
|
float mySpan;
|
||||||
|
|
||||||
|
if (level == 0) {
|
||||||
|
// Make room for mouse over info.
|
||||||
|
fMouseOverInfo.clear();
|
||||||
|
fMouseOverInfo[0] = SegmentList();
|
||||||
|
|
||||||
|
// Draw the center circle.
|
||||||
|
const char* displayName;
|
||||||
|
if (info == NULL) {
|
||||||
|
// NULL represents the entire volume. Show used and free space in
|
||||||
|
// the center circle, with the used segment representing the
|
||||||
|
// volume's root directory.
|
||||||
|
off_t volCapacity = snapshot->capacity;
|
||||||
|
mySpan = 360.0 * (volCapacity - snapshot->freeBytes) / volCapacity;
|
||||||
|
|
||||||
|
SetHighColor(kEmptySpcColor);
|
||||||
|
FillEllipse(BPoint(cx, cy), kPieCenterSize, kPieCenterSize);
|
||||||
|
|
||||||
|
SetHighColor(kBasePieColor[0]);
|
||||||
|
FillArc(BPoint(cx, cy), kPieCenterSize, kPieCenterSize, 0.0, mySpan);
|
||||||
|
|
||||||
|
// Show total volume capacity.
|
||||||
|
char label[B_PATH_NAME_LENGTH];
|
||||||
|
size_to_string(volCapacity, label);
|
||||||
|
SetHighColor(kPieBGColor);
|
||||||
|
SetDrawingMode(B_OP_OVER);
|
||||||
|
DrawString(label, BPoint(cx - StringWidth(label) / 2.0,
|
||||||
|
cy + fFontHeight + kSmallVMargin));
|
||||||
|
SetDrawingMode(B_OP_COPY);
|
||||||
|
|
||||||
|
displayName = snapshot->name.c_str();
|
||||||
|
|
||||||
|
// Record in-use space and free space for use during MouseMoved().
|
||||||
|
info = snapshot->rootDir;
|
||||||
|
fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info));
|
||||||
|
if (mySpan < 360.0 - kMinSegmentSpan) {
|
||||||
|
fMouseOverInfo[0].push_back(Segment(mySpan, 360.0,
|
||||||
|
snapshot->freeSpace));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show a normal directory.
|
||||||
|
SetHighColor(kBasePieColor[colorIdx]);
|
||||||
|
FillEllipse(BRect(cx - kPieCenterSize, cy - kPieCenterSize,
|
||||||
|
cx + kPieCenterSize + 0.5, cy + kPieCenterSize + 0.5));
|
||||||
|
displayName = info->ref.name;
|
||||||
|
mySpan = 360.0;
|
||||||
|
|
||||||
|
// Record the segment for use during MouseMoved().
|
||||||
|
fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info));
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPenSize(1.0);
|
||||||
|
SetHighColor(kOutlineColor);
|
||||||
|
StrokeEllipse(BPoint(cx, cy), kPieCenterSize + 0.5, kPieCenterSize + 0.5);
|
||||||
|
|
||||||
|
// Show the name of the volume or directory.
|
||||||
|
BString label(displayName);
|
||||||
|
BFont font;
|
||||||
|
GetFont(&font);
|
||||||
|
font.TruncateString(&label, B_TRUNCATE_END,
|
||||||
|
2.0 * (kPieCenterSize - kSmallHMargin));
|
||||||
|
float labelWidth = font.StringWidth(label.String());
|
||||||
|
|
||||||
|
SetHighColor(kPieBGColor);
|
||||||
|
SetDrawingMode(B_OP_OVER);
|
||||||
|
DrawString(label.String(), BPoint(cx - labelWidth / 2.0, cy));
|
||||||
|
SetDrawingMode(B_OP_COPY);
|
||||||
|
beginAngle = 0.0;
|
||||||
|
} else {
|
||||||
|
// Draw an exterior segment.
|
||||||
|
float parentSize;
|
||||||
|
if (info->parent == NULL)
|
||||||
|
parentSize = (float)snapshot->capacity;
|
||||||
|
else
|
||||||
|
parentSize = (float)info->parent->size;
|
||||||
|
|
||||||
|
mySpan = parentSpan * (float)info->size / parentSize;
|
||||||
|
if (mySpan >= kMinSegmentSpan) {
|
||||||
|
rgb_color color = kBasePieColor[colorIdx];
|
||||||
|
color.red += kLightenFactor * level;
|
||||||
|
color.green += kLightenFactor * level;
|
||||||
|
color.blue += kLightenFactor * level;
|
||||||
|
SetHighColor(color);
|
||||||
|
SetPenSize(kPieRingSize);
|
||||||
|
float radius = kPieCenterSize + level * kPieRingSize
|
||||||
|
- kPieRingSize / 2.0;
|
||||||
|
StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan);
|
||||||
|
|
||||||
|
SetHighColor(kOutlineColor);
|
||||||
|
SetPenSize(0.0);
|
||||||
|
float segBeginRadius = kPieCenterSize + (level - 1)
|
||||||
|
* kPieRingSize + 0.5;
|
||||||
|
float segLength = kPieRingSize - 0.5;
|
||||||
|
float rad = deg2rad(beginAngle);
|
||||||
|
float bx = cx + segBeginRadius * cos(rad);
|
||||||
|
float by = cy - segBeginRadius * sin(rad);
|
||||||
|
float ex = bx + segLength * cos(rad);
|
||||||
|
float ey = by - segLength * sin(rad);
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(ex, ey));
|
||||||
|
|
||||||
|
rad = deg2rad(beginAngle + mySpan);
|
||||||
|
bx = cx + segBeginRadius * cos(rad);
|
||||||
|
by = cy - segBeginRadius * sin(rad);
|
||||||
|
ex = bx + segLength * cos(rad);
|
||||||
|
ey = by - segLength * sin(rad);
|
||||||
|
StrokeLine(BPoint(bx, by), BPoint(ex, ey));
|
||||||
|
|
||||||
|
SetPenSize(0.0);
|
||||||
|
SetHighColor(kOutlineColor);
|
||||||
|
radius += kPieRingSize / 2.0;
|
||||||
|
StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan);
|
||||||
|
|
||||||
|
// Record the segment for use during MouseMoved().
|
||||||
|
if (fMouseOverInfo.find(level) == fMouseOverInfo.end())
|
||||||
|
fMouseOverInfo[level] = SegmentList();
|
||||||
|
|
||||||
|
fMouseOverInfo[level].push_back(
|
||||||
|
Segment(beginAngle, beginAngle + mySpan, info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw children.
|
||||||
|
vector<FileInfo*>::iterator i = info->children.begin();
|
||||||
|
while (i != info->children.end()) {
|
||||||
|
float childSpan
|
||||||
|
= _DrawDirectory(*i, mySpan, beginAngle, colorIdx, level + 1);
|
||||||
|
if (childSpan >= kMinSegmentSpan) {
|
||||||
|
beginAngle += childSpan;
|
||||||
|
colorIdx = (colorIdx + 1) % kBasePieColorCount;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mySpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileInfo*
|
||||||
|
PieView::_FileAt(const BPoint& where)
|
||||||
|
{
|
||||||
|
BRect b = Bounds();
|
||||||
|
float cx = b.left + b.Width() / 2.0;
|
||||||
|
float cy = b.top + b.Height() / 2.0;
|
||||||
|
float dx = where.x - cx;
|
||||||
|
float dy = where.y - cy;
|
||||||
|
float dist = sqrt(dx*dx + dy*dy);
|
||||||
|
|
||||||
|
int level;
|
||||||
|
if (dist < kPieCenterSize)
|
||||||
|
level = 0;
|
||||||
|
else
|
||||||
|
level = 1 + (int)((dist - kPieCenterSize) / kPieRingSize);
|
||||||
|
|
||||||
|
float angle = rad2deg(atan(dy / dx));
|
||||||
|
angle = ((dx < 0.0) ? 180.0 : (dy < 0.0) ? 0.0 : 360.0) - angle;
|
||||||
|
|
||||||
|
if (fMouseOverInfo.find(level) == fMouseOverInfo.end()) {
|
||||||
|
// No files in this level (ring) of the pie.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SegmentList s = fMouseOverInfo[level];
|
||||||
|
SegmentList::iterator i = s.begin();
|
||||||
|
while (i != s.end() && (angle < (*i).begin || (*i).end < angle))
|
||||||
|
i++;
|
||||||
|
if (i == s.end()) {
|
||||||
|
// Nothing at this angle.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*i).info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_AddAppToList(vector<AppMenuItem*>& list, const char* appSig,
|
||||||
|
int category)
|
||||||
|
{
|
||||||
|
// skip self.
|
||||||
|
if (strcmp(appSig, kAppSignature) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppMenuItem* item = new AppMenuItem(appSig, category);
|
||||||
|
if (item->IsValid()) {
|
||||||
|
vector<AppMenuItem*>::iterator i = list.begin();
|
||||||
|
while (i != list.end()) {
|
||||||
|
if (*item->AppRef() == *(*i)->AppRef()) {
|
||||||
|
// Skip duplicates.
|
||||||
|
delete item;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
list.push_back(item);
|
||||||
|
} else {
|
||||||
|
// Skip items that weren't constructed successfully.
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BMenu*
|
||||||
|
PieView::_BuildOpenWithMenu(FileInfo* info)
|
||||||
|
{
|
||||||
|
vector<AppMenuItem*> appList;
|
||||||
|
|
||||||
|
// Get preferred app.
|
||||||
|
BMimeType* type = info->Type();
|
||||||
|
char appSignature[B_MIME_TYPE_LENGTH];
|
||||||
|
if (type->GetPreferredApp(appSignature) == B_OK)
|
||||||
|
_AddAppToList(appList, appSignature, 1);
|
||||||
|
|
||||||
|
// Get apps that handle this subtype and supertype.
|
||||||
|
BMessage msg;
|
||||||
|
if (type->GetSupportingApps(&msg) == B_OK) {
|
||||||
|
int32 subs, supers, i;
|
||||||
|
msg.FindInt32("be:sub", &subs);
|
||||||
|
msg.FindInt32("be:super", &supers);
|
||||||
|
|
||||||
|
const char* appSig;
|
||||||
|
for (i = 0; i < subs; i++) {
|
||||||
|
msg.FindString("applications", i, &appSig);
|
||||||
|
_AddAppToList(appList, appSig, 2);
|
||||||
|
}
|
||||||
|
int hold = i;
|
||||||
|
for (i = 0; i < supers; i++) {
|
||||||
|
msg.FindString("applications", i + hold, &appSig);
|
||||||
|
_AddAppToList(appList, appSig, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get apps that handle any type.
|
||||||
|
if (BMimeType::GetWildcardApps(&msg) == B_OK) {
|
||||||
|
const char* appSig;
|
||||||
|
for (int32 i = 0; true; i++) {
|
||||||
|
if (msg.FindString("applications", i, &appSig) == B_OK)
|
||||||
|
_AddAppToList(appList, appSig, 4);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete type;
|
||||||
|
|
||||||
|
BMenu* openWith = new BMenu(kMenuOpenWith);
|
||||||
|
|
||||||
|
if (appList.size() == 0) {
|
||||||
|
BMenuItem* item = new BMenuItem(kMenuNoApps, NULL);
|
||||||
|
item->SetEnabled(false);
|
||||||
|
openWith->AddItem(item);
|
||||||
|
} else {
|
||||||
|
vector<AppMenuItem*>::iterator i = appList.begin();
|
||||||
|
int category = (*i)->Category();
|
||||||
|
while (i != appList.end()) {
|
||||||
|
if (category != (*i)->Category()) {
|
||||||
|
openWith->AddSeparatorItem();
|
||||||
|
category = (*i)->Category();
|
||||||
|
}
|
||||||
|
openWith->AddItem(*i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openWith;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_ShowContextMenu(FileInfo* info, BPoint p)
|
||||||
|
{
|
||||||
|
BRect openRect(p.x - 5.0, p.y - 5.0, p.x + 5.0, p.y + 5.0);
|
||||||
|
|
||||||
|
// Display the open-with menu only if the file is still available.
|
||||||
|
BNode node(&info->ref);
|
||||||
|
if (node.InitCheck() == B_OK) {
|
||||||
|
// Add "Open With" submenu.
|
||||||
|
BMenu* openWith = _BuildOpenWithMenu(info);
|
||||||
|
fMouseOverMenu->AddItem(openWith, kIdxOpenWith);
|
||||||
|
|
||||||
|
// Add a "Rescan" option for folders.
|
||||||
|
BMenuItem* rescan = NULL;
|
||||||
|
if (info->children.size() > 0) {
|
||||||
|
rescan = new BMenuItem(kStrRescan, NULL);
|
||||||
|
fMouseOverMenu->AddItem(rescan, kIdxRescan);
|
||||||
|
}
|
||||||
|
|
||||||
|
BMenuItem* item = fMouseOverMenu->Go(p, false, true, openRect);
|
||||||
|
if (item != NULL) {
|
||||||
|
switch (fMouseOverMenu->IndexOf(item)) {
|
||||||
|
case kIdxGetInfo:
|
||||||
|
new InfoWin(p, info, Window());
|
||||||
|
break;
|
||||||
|
case kIdxOpen:
|
||||||
|
_Launch(info);
|
||||||
|
break;
|
||||||
|
case kIdxRescan:
|
||||||
|
fScanners[fCurrentVolume]->Refresh(info);
|
||||||
|
Invalidate();
|
||||||
|
break;
|
||||||
|
default: // must be "Open With" submenu
|
||||||
|
_Launch(info, ((AppMenuItem*)item)->AppRef());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rescan != NULL) {
|
||||||
|
fMouseOverMenu->RemoveItem(rescan);
|
||||||
|
delete rescan;
|
||||||
|
}
|
||||||
|
|
||||||
|
fMouseOverMenu->RemoveItem(openWith);
|
||||||
|
delete openWith;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The file is no longer available.
|
||||||
|
fFileUnavailableMenu->Go(p, false, true, openRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PieView::_Launch(FileInfo* info, const entry_ref* appRef)
|
||||||
|
{
|
||||||
|
BMessage msg(B_REFS_RECEIVED);
|
||||||
|
msg.AddRef("refs", &info->ref);
|
||||||
|
|
||||||
|
if (appRef == NULL) {
|
||||||
|
// Let the registrar pick an app based on the file's MIME type.
|
||||||
|
BMimeType* type = info->Type();
|
||||||
|
be_roster->Launch(type->Type(), &msg);
|
||||||
|
delete type;
|
||||||
|
} else {
|
||||||
|
// Launch a designated app to handle this file.
|
||||||
|
be_roster->Launch(appRef, &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef PIE_VIEW_H
|
||||||
|
#define PIE_VIEW_H
|
||||||
|
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
class AppMenuItem;
|
||||||
|
class BEntry;
|
||||||
|
class BMenu;
|
||||||
|
class BPopUpMenu;
|
||||||
|
class BVolume;
|
||||||
|
struct entry_ref;
|
||||||
|
struct FileInfo;
|
||||||
|
class Scanner;
|
||||||
|
class MainWindow;
|
||||||
|
|
||||||
|
class PieView: public BView {
|
||||||
|
public:
|
||||||
|
PieView(BRect frame, MainWindow* window);
|
||||||
|
virtual ~PieView();
|
||||||
|
|
||||||
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
virtual void MouseDown(BPoint where);
|
||||||
|
virtual void MouseUp(BPoint where);
|
||||||
|
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||||
|
const BMessage* dragMessage);
|
||||||
|
virtual void Draw(BRect updateRect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _HandleArg(const BEntry* entry);
|
||||||
|
void _ShowVolume(BVolume* volume);
|
||||||
|
void _DrawProgressBar(BRect updateRect);
|
||||||
|
void _DrawPieChart(BRect updateRect);
|
||||||
|
float _DrawDirectory(FileInfo* info,
|
||||||
|
float parentSpan, float beginAngle,
|
||||||
|
int colorIdx, int level);
|
||||||
|
FileInfo* _FileAt(const BPoint& where);
|
||||||
|
void _AddAppToList(vector<AppMenuItem*>& list,
|
||||||
|
const char* appSignature, int category);
|
||||||
|
BMenu* _BuildOpenWithMenu(FileInfo* info);
|
||||||
|
void _ShowContextMenu(FileInfo* info, BPoint where);
|
||||||
|
void _Launch(FileInfo* info,
|
||||||
|
const entry_ref* ref = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Segment {
|
||||||
|
Segment()
|
||||||
|
: begin(0.0), end(0.0), info(NULL) { }
|
||||||
|
Segment(float b, float e, FileInfo* i)
|
||||||
|
: begin(b), end(e), info(i) { }
|
||||||
|
Segment(const Segment &s)
|
||||||
|
: begin(s.begin), end(s.end), info(s.info) { }
|
||||||
|
~Segment() { }
|
||||||
|
|
||||||
|
float begin;
|
||||||
|
float end;
|
||||||
|
FileInfo* info;
|
||||||
|
};
|
||||||
|
typedef vector<Segment> SegmentList;
|
||||||
|
typedef map<int, SegmentList> MouseOverInfo;
|
||||||
|
|
||||||
|
typedef map<BVolume*, Scanner*> ScannerMap;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MainWindow* fWindow;
|
||||||
|
ScannerMap fScanners;
|
||||||
|
BVolume* fCurrentVolume;
|
||||||
|
FileInfo* fCurrentDir;
|
||||||
|
MouseOverInfo fMouseOverInfo;
|
||||||
|
BPopUpMenu* fMouseOverMenu;
|
||||||
|
BPopUpMenu* fFileUnavailableMenu;
|
||||||
|
float fFontHeight;
|
||||||
|
bool fClicked;
|
||||||
|
bool fDragging;
|
||||||
|
BPoint fDragStart;
|
||||||
|
FileInfo* fClickedFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PIE_VIEW_H
|
||||||
|
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <[email protected]>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "Scanner.h"
|
||||||
|
|
||||||
|
#include <Directory.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
|
||||||
|
Scanner::Scanner(BVolume *v, BHandler *handler)
|
||||||
|
: BLooper(),
|
||||||
|
fListener(handler),
|
||||||
|
fDoneMessage(kScanDone),
|
||||||
|
fProgressMessage(kScanProgress),
|
||||||
|
|
||||||
|
fVolume(v),
|
||||||
|
fSnapshot(NULL),
|
||||||
|
fDesiredPath(),
|
||||||
|
fTask(),
|
||||||
|
fBusy(false),
|
||||||
|
fQuitRequested(false)
|
||||||
|
{
|
||||||
|
fDoneMessage.AddPointer(kNameVolPtr, fVolume);
|
||||||
|
fProgressMessage.AddPointer(kNameVolPtr, fVolume);
|
||||||
|
|
||||||
|
Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scanner::~Scanner()
|
||||||
|
{
|
||||||
|
delete fSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::MessageReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
switch (message->what) {
|
||||||
|
case kScanRefresh:
|
||||||
|
{
|
||||||
|
FileInfo* startInfo;
|
||||||
|
if (message->FindPointer(kNameFilePtr, (void **)&startInfo) == B_OK)
|
||||||
|
_RunScan(startInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
BLooper::MessageReceived(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::Refresh(FileInfo* startInfo)
|
||||||
|
{
|
||||||
|
if (fBusy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fBusy = true;
|
||||||
|
|
||||||
|
// Remember the current directory, if any, so we can return to it after
|
||||||
|
// the scanning is done.
|
||||||
|
if (fSnapshot != NULL && fSnapshot->currentDir != NULL)
|
||||||
|
fSnapshot->currentDir->GetPath(fDesiredPath);
|
||||||
|
|
||||||
|
fTask.assign(kEmptyStr);
|
||||||
|
|
||||||
|
BMessage msg(kScanRefresh);
|
||||||
|
msg.AddPointer(kNameFilePtr, startInfo);
|
||||||
|
PostMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::SetDesiredPath(string &path)
|
||||||
|
{
|
||||||
|
fDesiredPath = path;
|
||||||
|
|
||||||
|
if (fSnapshot == NULL)
|
||||||
|
Refresh();
|
||||||
|
else
|
||||||
|
_ChangeToDesired();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::RequestQuit()
|
||||||
|
{
|
||||||
|
// If the looper thread is scanning, we won't succeed to grab the lock,
|
||||||
|
// since the thread is scanning from within MessageReceived(). Then by
|
||||||
|
// setting fQuitRequested, the thread will stop scanning and only after
|
||||||
|
// that happened we succeed to grab the lock. So this line of action
|
||||||
|
// should be safe.
|
||||||
|
fQuitRequested = true;
|
||||||
|
Lock();
|
||||||
|
Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - private
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::_RunScan(FileInfo* startInfo)
|
||||||
|
{
|
||||||
|
if (startInfo == NULL || startInfo == fSnapshot->rootDir) {
|
||||||
|
delete fSnapshot;
|
||||||
|
fSnapshot = new VolumeSnapshot(fVolume);
|
||||||
|
fTask = kStrScanningX + fSnapshot->name;
|
||||||
|
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
|
||||||
|
fVolumeBytesScanned = 0;
|
||||||
|
fProgress = 0.0;
|
||||||
|
fLastReport = -100.0;
|
||||||
|
|
||||||
|
BDirectory root;
|
||||||
|
fVolume->GetRootDirectory(&root);
|
||||||
|
fSnapshot->rootDir = _GetFileInfo(&root, NULL);
|
||||||
|
|
||||||
|
FileInfo* freeSpace = new FileInfo;
|
||||||
|
freeSpace->pseudo = true;
|
||||||
|
string s = "Free on " + fSnapshot->name;
|
||||||
|
freeSpace->ref.set_name(s.c_str());
|
||||||
|
freeSpace->size = fSnapshot->freeBytes;
|
||||||
|
fSnapshot->freeSpace = freeSpace;
|
||||||
|
|
||||||
|
fSnapshot->currentDir = NULL;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fSnapshot->capacity = fVolume->Capacity();
|
||||||
|
fSnapshot->freeBytes = fVolume->FreeBytes();
|
||||||
|
fTask = kStrScanningX + string(startInfo->ref.name);
|
||||||
|
fVolumeBytesInUse = fSnapshot->capacity - fSnapshot->freeBytes;
|
||||||
|
fVolumeBytesScanned = fVolumeBytesInUse - startInfo->size; // best guess
|
||||||
|
fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse;
|
||||||
|
fLastReport = -100.0;
|
||||||
|
|
||||||
|
BDirectory startDir(&startInfo->ref);
|
||||||
|
if (startDir.InitCheck() == B_OK) {
|
||||||
|
FileInfo *parent = startInfo->parent;
|
||||||
|
vector<FileInfo *>::iterator i = parent->children.begin();
|
||||||
|
FileInfo* newInfo = _GetFileInfo(&startDir, parent);
|
||||||
|
while (i != parent->children.end() && *i != startInfo)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
int idx = i - parent->children.begin();
|
||||||
|
parent->children[idx] = newInfo;
|
||||||
|
|
||||||
|
// Fixup count and size fields in parent directory.
|
||||||
|
parent->size += newInfo->size - startInfo->size;
|
||||||
|
parent->count += newInfo->count - startInfo->count;
|
||||||
|
|
||||||
|
delete startInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fBusy = false;
|
||||||
|
_ChangeToDesired();
|
||||||
|
fListener.SendMessage(&fDoneMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileInfo*
|
||||||
|
Scanner::_GetFileInfo(BDirectory* dir, FileInfo* parent)
|
||||||
|
{
|
||||||
|
FileInfo* thisDir = new FileInfo;
|
||||||
|
BEntry entry;
|
||||||
|
dir->GetEntry(&entry);
|
||||||
|
entry.GetRef(&thisDir->ref);
|
||||||
|
thisDir->parent = parent;
|
||||||
|
thisDir->count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (fQuitRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (dir->GetNextEntry(&entry) == B_ENTRY_NOT_FOUND)
|
||||||
|
break;
|
||||||
|
if (entry.IsSymLink())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entry.IsFile()) {
|
||||||
|
FileInfo *child = new FileInfo;
|
||||||
|
entry.GetRef(&child->ref);
|
||||||
|
entry.GetSize(&child->size);
|
||||||
|
child->parent = thisDir;
|
||||||
|
thisDir->children.push_back(child);
|
||||||
|
|
||||||
|
// Send a progress report periodically.
|
||||||
|
fVolumeBytesScanned += child->size;
|
||||||
|
fProgress = 100.0 * fVolumeBytesScanned / fVolumeBytesInUse;
|
||||||
|
if (fProgress - fLastReport > kReportInterval) {
|
||||||
|
fLastReport = fProgress;
|
||||||
|
fListener.SendMessage(&fProgressMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (entry.IsDirectory()) {
|
||||||
|
BDirectory childDir(&entry);
|
||||||
|
thisDir->children.push_back(_GetFileInfo(&childDir, thisDir));
|
||||||
|
}
|
||||||
|
thisDir->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<FileInfo *>::iterator i = thisDir->children.begin();
|
||||||
|
while (i != thisDir->children.end()) {
|
||||||
|
thisDir->size += (*i)->size;
|
||||||
|
thisDir->count += (*i)->count;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return thisDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scanner::_ChangeToDesired()
|
||||||
|
{
|
||||||
|
if (fBusy || fDesiredPath.size() <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char* workPath = strdup(fDesiredPath.c_str());
|
||||||
|
char* pathPtr = &workPath[1]; // skip leading '/'
|
||||||
|
char* endOfPath = pathPtr + strlen(pathPtr);
|
||||||
|
|
||||||
|
// Check the name of the root directory. It is a special case because
|
||||||
|
// it has no parent data structure.
|
||||||
|
FileInfo* checkDir = fSnapshot->rootDir;
|
||||||
|
FileInfo* prefDir = NULL;
|
||||||
|
char* sep = strchr(pathPtr, '/');
|
||||||
|
if (sep != NULL)
|
||||||
|
*sep = '\0';
|
||||||
|
|
||||||
|
if (strcmp(pathPtr, checkDir->ref.name) == 0) {
|
||||||
|
// Root directory matches, so follow the remainder of the path as
|
||||||
|
// far as possible.
|
||||||
|
prefDir = checkDir;
|
||||||
|
pathPtr += 1 + strlen(pathPtr);
|
||||||
|
while (pathPtr < endOfPath) {
|
||||||
|
sep = strchr(pathPtr, '/');
|
||||||
|
if (sep != NULL)
|
||||||
|
*sep = '\0';
|
||||||
|
|
||||||
|
checkDir = prefDir->FindChild(pathPtr);
|
||||||
|
if (checkDir == NULL || checkDir->children.size() == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pathPtr += 1 + strlen(pathPtr);
|
||||||
|
prefDir = checkDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the desired path is the volume's root directory, default to the
|
||||||
|
// volume display.
|
||||||
|
if (prefDir == fSnapshot->rootDir)
|
||||||
|
prefDir = NULL;
|
||||||
|
|
||||||
|
ChangeDir(prefDir);
|
||||||
|
|
||||||
|
free(workPath);
|
||||||
|
fDesiredPath.erase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef SCANNER_H
|
||||||
|
#define SCANNER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <Looper.h>
|
||||||
|
#include <Message.h>
|
||||||
|
#include <Messenger.h>
|
||||||
|
#include <Volume.h>
|
||||||
|
|
||||||
|
#include "Snapshot.h"
|
||||||
|
|
||||||
|
|
||||||
|
class BDirectory;
|
||||||
|
|
||||||
|
class Scanner: public BLooper {
|
||||||
|
public:
|
||||||
|
Scanner(BVolume* volume, BHandler* handler);
|
||||||
|
virtual ~Scanner();
|
||||||
|
|
||||||
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
|
VolumeSnapshot* Snapshot() const
|
||||||
|
{ return fBusy ? NULL : fSnapshot; }
|
||||||
|
void Refresh(FileInfo* startInfo = NULL);
|
||||||
|
bool IsBusy() const
|
||||||
|
{ return fBusy; }
|
||||||
|
const char* Task() const
|
||||||
|
{ return fTask.c_str(); }
|
||||||
|
float Progress() const
|
||||||
|
{ return min_c(100.0, fProgress); }
|
||||||
|
FileInfo* CurrentDir() const
|
||||||
|
{ return fBusy ? NULL : fSnapshot->currentDir; }
|
||||||
|
void ChangeDir(FileInfo* info)
|
||||||
|
{ fSnapshot->currentDir = info; }
|
||||||
|
void SetDesiredPath(string &path);
|
||||||
|
dev_t Device() const
|
||||||
|
{ return fVolume->Device(); }
|
||||||
|
void RequestQuit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _RunScan(FileInfo *startInfo);
|
||||||
|
FileInfo* _GetFileInfo(BDirectory* dir, FileInfo* parent);
|
||||||
|
void _ChangeToDesired();
|
||||||
|
|
||||||
|
BMessenger fListener;
|
||||||
|
BMessage fDoneMessage;
|
||||||
|
BMessage fProgressMessage;
|
||||||
|
|
||||||
|
BVolume* fVolume;
|
||||||
|
off_t fVolumeBytesInUse;
|
||||||
|
off_t fVolumeBytesScanned;
|
||||||
|
float fProgress;
|
||||||
|
float fLastReport;
|
||||||
|
VolumeSnapshot* fSnapshot;
|
||||||
|
string fDesiredPath;
|
||||||
|
string fTask;
|
||||||
|
bool fBusy;
|
||||||
|
bool fQuitRequested;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCANNER_H
|
||||||
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "Snapshot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Directory.h>
|
||||||
|
#include <Mime.h>
|
||||||
|
#include <NodeInfo.h>
|
||||||
|
#include <Path.h>
|
||||||
|
#include <Volume.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const char* kFileType = B_FILE_MIME_TYPE;
|
||||||
|
static const char* kDirType = "application/x-vnd.Be-directory";
|
||||||
|
static const char* kVolumeType = "application/x-vnd.Be-volume";
|
||||||
|
|
||||||
|
|
||||||
|
FileInfo::FileInfo()
|
||||||
|
: pseudo(false),
|
||||||
|
size(0),
|
||||||
|
count(0),
|
||||||
|
parent(NULL),
|
||||||
|
children()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileInfo::~FileInfo()
|
||||||
|
{
|
||||||
|
while (children.size() != 0) {
|
||||||
|
FileInfo* child = *children.begin();
|
||||||
|
delete child;
|
||||||
|
children.erase(children.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
FileInfo::GetPath(string& path) const
|
||||||
|
{
|
||||||
|
if (pseudo) {
|
||||||
|
path.assign(ref.name);
|
||||||
|
} else {
|
||||||
|
BEntry entry(&ref, true);
|
||||||
|
BPath pathObj(&entry);
|
||||||
|
path.assign(pathObj.Path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileInfo*
|
||||||
|
FileInfo::FindChild(const char* name) const
|
||||||
|
{
|
||||||
|
vector<FileInfo*>::const_iterator i = children.begin();
|
||||||
|
while (i != children.end()) {
|
||||||
|
if (strcmp((*i)->ref.name, name) == 0)
|
||||||
|
return *i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BMimeType*
|
||||||
|
FileInfo::Type() const
|
||||||
|
{
|
||||||
|
char mimeStr[B_MIME_TYPE_LENGTH] = { '\0' };
|
||||||
|
if (parent == NULL) {
|
||||||
|
// This is the volume's root directory; treat it as a volume type.
|
||||||
|
strcpy(mimeStr, kVolumeType);
|
||||||
|
} else {
|
||||||
|
// Get the MIME type from the registrar.
|
||||||
|
BNode node(&ref);
|
||||||
|
if (node.InitCheck() == B_OK) {
|
||||||
|
BNodeInfo nodeInfo(&node);
|
||||||
|
if (nodeInfo.InitCheck() == B_OK) {
|
||||||
|
status_t s = nodeInfo.GetType(mimeStr);
|
||||||
|
if (s != B_OK && children.size() > 0) {
|
||||||
|
if (s == B_ENTRY_NOT_FOUND) {
|
||||||
|
// This status appears to be returned only for files on
|
||||||
|
// BFS volumes (e.g., CDFS volumes return B_BAD_VALUE).
|
||||||
|
//nodeInfo.SetType(kDirType);
|
||||||
|
}
|
||||||
|
strcpy(mimeStr, kDirType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(mimeStr) == 0)
|
||||||
|
strcpy(mimeStr, kFileType);
|
||||||
|
|
||||||
|
return new BMimeType(mimeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
VolumeSnapshot::VolumeSnapshot(const BVolume* volume)
|
||||||
|
{
|
||||||
|
char nameBuffer[B_FILE_NAME_LENGTH];
|
||||||
|
volume->GetName(nameBuffer);
|
||||||
|
name = nameBuffer;
|
||||||
|
|
||||||
|
capacity = volume->Capacity();
|
||||||
|
freeBytes = volume->FreeBytes();
|
||||||
|
rootDir = NULL;
|
||||||
|
freeSpace = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VolumeSnapshot::~VolumeSnapshot()
|
||||||
|
{
|
||||||
|
delete rootDir;
|
||||||
|
delete freeSpace;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef SNAPSHOT_H
|
||||||
|
#define SNAPSHOT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <Entry.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BMimeType;
|
||||||
|
class BVolume;
|
||||||
|
|
||||||
|
struct FileInfo {
|
||||||
|
FileInfo();
|
||||||
|
~FileInfo();
|
||||||
|
|
||||||
|
void GetPath(string& path) const;
|
||||||
|
FileInfo* FindChild(const char* name) const;
|
||||||
|
BMimeType* Type() const;
|
||||||
|
|
||||||
|
bool pseudo;
|
||||||
|
entry_ref ref;
|
||||||
|
off_t size;
|
||||||
|
int count;
|
||||||
|
FileInfo* parent;
|
||||||
|
vector<FileInfo*> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct VolumeSnapshot {
|
||||||
|
VolumeSnapshot(const BVolume* volume);
|
||||||
|
~VolumeSnapshot();
|
||||||
|
|
||||||
|
string name;
|
||||||
|
off_t capacity;
|
||||||
|
off_t freeBytes;
|
||||||
|
FileInfo* rootDir;
|
||||||
|
FileInfo* freeSpace;
|
||||||
|
FileInfo* currentDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SNAPSHOT_H
|
||||||
|
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#include "StatusView.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Box.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <StringView.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Scanner.h"
|
||||||
|
|
||||||
|
|
||||||
|
StatusView::StatusView(BRect r)
|
||||||
|
: BView(r, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM, B_WILL_DRAW),
|
||||||
|
fCurrentFileInfo(NULL)
|
||||||
|
{
|
||||||
|
SetViewColor(kWindowColor);
|
||||||
|
|
||||||
|
struct font_height fh;
|
||||||
|
be_plain_font->GetHeight(&fh);
|
||||||
|
float plainHeight = ceilf(fh.ascent) + ceilf(fh.descent)
|
||||||
|
+ ceilf(fh.leading);
|
||||||
|
float fixedHeight = ceilf(fh.ascent) + ceilf(fh.descent)
|
||||||
|
+ ceilf(fh.leading);
|
||||||
|
ResizeTo(Bounds().Width(),
|
||||||
|
2.0 * kSmallVMargin + max(plainHeight, fixedHeight));
|
||||||
|
|
||||||
|
BRect r = Bounds();
|
||||||
|
r.top += kSmallVMargin;
|
||||||
|
|
||||||
|
// Size display
|
||||||
|
r.right -= kSmallHMargin;
|
||||||
|
r.left = r.right - StringWidth("9999.99 GB");
|
||||||
|
fSizeView = new BStringView(r, NULL, kEmptyStr,
|
||||||
|
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM);
|
||||||
|
AddChild(fSizeView);
|
||||||
|
|
||||||
|
// Vertical divider
|
||||||
|
r.right = r.left - kSmallHMargin;
|
||||||
|
r.left = r.right - 1.0;
|
||||||
|
AddChild(new BBox(r.InsetByCopy(0, -5), NULL,
|
||||||
|
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM));
|
||||||
|
|
||||||
|
// Count display
|
||||||
|
char testLabel[256];
|
||||||
|
sprintf(testLabel, kManyFiles, 999999);
|
||||||
|
r.right = r.left - kSmallHMargin;
|
||||||
|
r.left = r.right - (StringWidth(testLabel) + kSmallHMargin);
|
||||||
|
fCountView = new BStringView(r, NULL, kEmptyStr,
|
||||||
|
B_FOLLOW_RIGHT|B_FOLLOW_TOP_BOTTOM);
|
||||||
|
AddChild(fCountView);
|
||||||
|
|
||||||
|
// Vertical divider
|
||||||
|
r.right = r.left - kSmallHMargin;
|
||||||
|
r.left = r.right - 1.0;
|
||||||
|
AddChild(new BBox(r.InsetByCopy(0, -5), NULL,
|
||||||
|
B_FOLLOW_RIGHT | B_FOLLOW_TOP_BOTTOM));
|
||||||
|
|
||||||
|
// Path display
|
||||||
|
r.right = r.left - kSmallHMargin;
|
||||||
|
r.left = kSmallHMargin;
|
||||||
|
fPathView = new BStringView(r, NULL, kEmptyStr,
|
||||||
|
B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP_BOTTOM);
|
||||||
|
AddChild(fPathView);
|
||||||
|
|
||||||
|
// Horizontal divider
|
||||||
|
r = Bounds();
|
||||||
|
r.bottom = r.top + 1.0;
|
||||||
|
AddChild(new BBox(r.InsetByCopy(-5, 0), NULL,
|
||||||
|
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM, B_WILL_DRAW));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StatusView::~StatusView()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StatusView::Show(const FileInfo* info)
|
||||||
|
{
|
||||||
|
if (info == fCurrentFileInfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fCurrentFileInfo = info;
|
||||||
|
|
||||||
|
if (info == NULL) {
|
||||||
|
fPathView->SetText(kEmptyStr);
|
||||||
|
fSizeView->SetText(kEmptyStr);
|
||||||
|
fCountView->SetText(kEmptyStr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->pseudo) {
|
||||||
|
BNode node(&info->ref);
|
||||||
|
if (node.InitCheck() != B_OK) {
|
||||||
|
fPathView->SetText(kStrUnavail);
|
||||||
|
fSizeView->SetText(kEmptyStr);
|
||||||
|
fCountView->SetText(kEmptyStr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float viewWidth = fPathView->Bounds().Width();
|
||||||
|
string path;
|
||||||
|
info->GetPath(path);
|
||||||
|
BString pathLabel = path.c_str();
|
||||||
|
be_plain_font->TruncateString(&pathLabel, B_TRUNCATE_BEGINNING, viewWidth);
|
||||||
|
fPathView->SetText(pathLabel.String());
|
||||||
|
|
||||||
|
char label[B_PATH_NAME_LENGTH];
|
||||||
|
size_to_string(info->size, label);
|
||||||
|
fSizeView->SetText(label);
|
||||||
|
|
||||||
|
if (info->count > 0) {
|
||||||
|
char label[256];
|
||||||
|
sprintf(label, (info->count == 1) ? kOneFile : kManyFiles, info->count);
|
||||||
|
fCountView->SetText(label);
|
||||||
|
} else {
|
||||||
|
fCountView->SetText(kEmptyStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT/X11 license.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999 Mike Steed. You are free to use and distribute this software
|
||||||
|
* as long as it is accompanied by it's documentation and this copyright notice.
|
||||||
|
* The software comes with no warranty, etc.
|
||||||
|
*/
|
||||||
|
#ifndef STATUS_VIEW_H
|
||||||
|
#define STATUS_VIEW_H
|
||||||
|
|
||||||
|
#include <View.h>
|
||||||
|
#include <StringView.h>
|
||||||
|
#include <Rect.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct FileInfo;
|
||||||
|
|
||||||
|
class StatusView: public BView {
|
||||||
|
public:
|
||||||
|
StatusView(BRect frame);
|
||||||
|
virtual ~StatusView();
|
||||||
|
|
||||||
|
void Show(const FileInfo* info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BStringView* fPathView;
|
||||||
|
BStringView* fSizeView;
|
||||||
|
BStringView* fCountView;
|
||||||
|
const FileInfo* fCurrentFileInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // STATUS_VIEW_H
|
||||||
|
|
||||||
@@ -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).
|
||||||
|
|
||||||
Reference in New Issue
Block a user