From 3c7b870f367df150ea60eb9d6bb2fd41646545d7 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 3 Feb 2010 01:26:04 +0000 Subject: Added abandoned Filelight application git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/filelight@1084392 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/part/Config.cpp | 62 ++++ src/part/Config.h | 41 +++ src/part/Makefile.am | 19 ++ src/part/debug.h | 36 +++ src/part/dialog.ui | 574 ++++++++++++++++++++++++++++++++++++ src/part/fileTree.cpp | 67 +++++ src/part/fileTree.h | 238 +++++++++++++++ src/part/localLister.cpp | 333 +++++++++++++++++++++ src/part/localLister.h | 35 +++ src/part/part.cpp | 254 ++++++++++++++++ src/part/part.h | 71 +++++ src/part/progressBox.cpp | 65 ++++ src/part/progressBox.h | 32 ++ src/part/radialMap/Makefile.am | 4 + src/part/radialMap/builder.cpp | 141 +++++++++ src/part/radialMap/builder.h | 38 +++ src/part/radialMap/labels.cpp | 327 ++++++++++++++++++++ src/part/radialMap/map.cpp | 442 +++++++++++++++++++++++++++ src/part/radialMap/radialMap.h | 72 +++++ src/part/radialMap/segmentTip.cpp | 186 ++++++++++++ src/part/radialMap/segmentTip.h | 34 +++ src/part/radialMap/sincos.h | 25 ++ src/part/radialMap/widget.cpp | 187 ++++++++++++ src/part/radialMap/widget.h | 114 +++++++ src/part/radialMap/widgetEvents.cpp | 275 +++++++++++++++++ src/part/remoteLister.cpp | 160 ++++++++++ src/part/remoteLister.h | 28 ++ src/part/scan.cpp | 204 +++++++++++++ src/part/scan.h | 52 ++++ src/part/settingsDialog.cpp | 214 ++++++++++++++ src/part/settingsDialog.h | 48 +++ src/part/summaryWidget.cpp | 236 +++++++++++++++ src/part/summaryWidget.h | 25 ++ 33 files changed, 4639 insertions(+) create mode 100644 src/part/Config.cpp create mode 100644 src/part/Config.h create mode 100644 src/part/Makefile.am create mode 100644 src/part/debug.h create mode 100644 src/part/dialog.ui create mode 100644 src/part/fileTree.cpp create mode 100644 src/part/fileTree.h create mode 100644 src/part/localLister.cpp create mode 100644 src/part/localLister.h create mode 100644 src/part/part.cpp create mode 100644 src/part/part.h create mode 100644 src/part/progressBox.cpp create mode 100644 src/part/progressBox.h create mode 100644 src/part/radialMap/Makefile.am create mode 100644 src/part/radialMap/builder.cpp create mode 100644 src/part/radialMap/builder.h create mode 100644 src/part/radialMap/labels.cpp create mode 100644 src/part/radialMap/map.cpp create mode 100644 src/part/radialMap/radialMap.h create mode 100644 src/part/radialMap/segmentTip.cpp create mode 100644 src/part/radialMap/segmentTip.h create mode 100644 src/part/radialMap/sincos.h create mode 100644 src/part/radialMap/widget.cpp create mode 100644 src/part/radialMap/widget.h create mode 100644 src/part/radialMap/widgetEvents.cpp create mode 100644 src/part/remoteLister.cpp create mode 100644 src/part/remoteLister.h create mode 100644 src/part/scan.cpp create mode 100644 src/part/scan.h create mode 100644 src/part/settingsDialog.cpp create mode 100644 src/part/settingsDialog.h create mode 100644 src/part/summaryWidget.cpp create mode 100644 src/part/summaryWidget.h (limited to 'src/part') diff --git a/src/part/Config.cpp b/src/part/Config.cpp new file mode 100644 index 0000000..8d2f6b8 --- /dev/null +++ b/src/part/Config.cpp @@ -0,0 +1,62 @@ + +#include "Config.h" +#include +#include + + +bool Config::scanAcrossMounts; +bool Config::scanRemoteMounts; +bool Config::scanRemovableMedia; +bool Config::varyLabelFontSizes; +bool Config::showSmallFiles; +uint Config::contrast; +uint Config::antiAliasFactor; +uint Config::minFontPitch; +uint Config::defaultRingDepth; +Filelight::MapScheme Config::scheme; +QStringList Config::skipList; + + +inline KConfig& +Filelight::Config::kconfig() +{ + KConfig *config = KGlobal::config(); + config->setGroup( "filelight_part" ); + return *config; +} + +void +Filelight::Config::read() +{ + const KConfig &config = kconfig(); + + scanAcrossMounts = config.readBoolEntry( "scanAcrossMounts", false ); + scanRemoteMounts = config.readBoolEntry( "scanRemoteMounts", false ); + scanRemovableMedia = config.readBoolEntry( "scanRemovableMedia", false ); + varyLabelFontSizes = config.readBoolEntry( "varyLabelFontSizes", true ); + showSmallFiles = config.readBoolEntry( "showSmallFiles", false ); + contrast = config.readNumEntry( "contrast", 75 ); + antiAliasFactor = config.readNumEntry( "antiAliasFactor", 2 ); + minFontPitch = config.readNumEntry( "minFontPitch", QFont().pointSize() - 3); + scheme = (MapScheme) config.readNumEntry( "scheme", 0 ); + skipList = config.readPathListEntry( "skipList" ); + + defaultRingDepth = 4; +} + +void +Filelight::Config::write() +{ + KConfig &config = kconfig(); + + config.writeEntry( "scanAcrossMounts", scanAcrossMounts ); + config.writeEntry( "scanRemoteMounts", scanRemoteMounts ); + config.writeEntry( "scanRemovableMedia", scanRemovableMedia ); + config.writeEntry( "varyLabelFontSizes", varyLabelFontSizes ); + config.writeEntry( "showSmallFiles", showSmallFiles); + config.writeEntry( "contrast", contrast ); + config.writeEntry( "antiAliasFactor", antiAliasFactor ); + config.writeEntry( "minFontPitch", minFontPitch ); + config.writeEntry( "scheme", scheme ); + config.writePathEntry( "skipList", skipList ); +} diff --git a/src/part/Config.h b/src/part/Config.h new file mode 100644 index 0000000..dffaa95 --- /dev/null +++ b/src/part/Config.h @@ -0,0 +1,41 @@ + +#ifndef Config_H +#define Config_H + +#include + +class KConfig; + + +namespace Filelight +{ + enum MapScheme { Rainbow, HighContrast, KDE, FileDensity, ModTime }; + + class Config + { + static KConfig& kconfig(); + + public: + static void read(); + static void write(); + + //keep everything positive, avoid using DON'T, NOT or NO + + static bool scanAcrossMounts; + static bool scanRemoteMounts; + static bool scanRemovableMedia; + static bool varyLabelFontSizes; + static bool showSmallFiles; + static uint contrast; + static uint antiAliasFactor; + static uint minFontPitch; + static uint defaultRingDepth; + + static MapScheme scheme; + static QStringList skipList; + }; +} + +using Filelight::Config; + +#endif diff --git a/src/part/Makefile.am b/src/part/Makefile.am new file mode 100644 index 0000000..2ff41f8 --- /dev/null +++ b/src/part/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = radialMap +INCLUDES = $(all_includes) -I$(top_srcdir)/src +METASOURCES = AUTO + +#Part +kde_module_LTLIBRARIES = libfilelight.la +libfilelight_la_LIBADD = ./radialMap/libradialmap.la $(LIB_KFILE) $(LIB_KPARTS) $(LIB_KDEUI) $(LIB_QT) +libfilelight_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) +libfilelight_la_SOURCES = \ + dialog.ui \ + part.cpp \ + scan.cpp \ + progressBox.cpp \ + Config.cpp \ + settingsDialog.cpp \ + fileTree.cpp \ + localLister.cpp \ + remoteLister.cpp \ + summaryWidget.cpp diff --git a/src/part/debug.h b/src/part/debug.h new file mode 100644 index 0000000..e5e680b --- /dev/null +++ b/src/part/debug.h @@ -0,0 +1,36 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef DEBUG_H +#define DEBUG_H + +/** Fancy debug header + * @author Max Howell + * + * Define DEBUG_PREFIX as a string before you include this to insert a fancy debug prefix + * Debug::debug(), can be used as debug() and is just kdDebug() + * use Debug::indent() and Debug::unindent() + */ + +#include + +#ifdef NDEBUG +static inline kndbgstream debug() { return kndbgstream(); } +#else +static inline kdbgstream debug() +{ + return kdbgstream( + #ifdef DEBUG_PREFIX + "[" DEBUG_PREFIX "] ", + #endif + 0, 0 ); +} +#endif + +#define error kdError +#define fatal kdFatal +#define warning kdWarning + +#define DEBUG_ANNOUNCE debug() << ">> " << __PRETTY_FUNCTION__ << endl; + +#endif diff --git a/src/part/dialog.ui b/src/part/dialog.ui new file mode 100644 index 0000000..2c1d788 --- /dev/null +++ b/src/part/dialog.ui @@ -0,0 +1,574 @@ + +Dialog + + + Dialog + + + + 0 + 0 + 415 + 351 + + + + Settings - Filelight + + + + unnamed + + + + tabWidget + + + false + + + + Widget2 + + + Scannin&g + + + + unnamed + + + + textLabel1 + + + + 5 + 4 + 0 + 0 + + + + Do &not scan these directories: + + + PlainText + + + m_listBox + + + + + m_listBox + + + + + + Filelight will not scan these directories unless you specifically request them. + + + + + layout4 + + + + unnamed + + + + spacer2 + + + Horizontal + + + Expanding + + + + 180 + 21 + + + + + + m_removeButton + + + true + + + R&emove + + + false + + + + + m_addButton + + + &Add... + + + false + + + false + + + + + + + line1 + + + + 0 + 15 + + + + HLine + + + Sunken + + + 1 + + + 1 + + + Horizontal + + + + + layout7 + + + + unnamed + + + + spacer3 + + + Horizontal + + + Fixed + + + + 16 + 50 + + + + + + dontScanRemoteMounts + + + false + + + + 0 + 0 + + + + + 32767 + 32767 + + + + false + + + Exclude remote files&ystems + + + + + + Prevents scanning of filesystems that are not on this computer, e.g. NFS or Samba mounts. + + + + + scanAcrossMounts + + + Scan across filesystem &boundaries + + + Allows scans to enter directories that are part of other filesystems. For example, when unchecked, this will usually prevent the contents of <b>/mnt</b> from being scanned if you scan <b>/</b>. + + + + + dontScanRemovableMedia + + + false + + + E&xclude removable media + + + + + + Prevents Filelight from scanning removable media (eg. CD-ROMs). + + + + + + + + + Widget3 + + + &Appearance + + + + unnamed + + + + groupBox1 + + + Scheme + + + + unnamed + + + + colourSchemeGroup + + + + 5 + 7 + 0 + 0 + + + + + + layout10 + + + + unnamed + + + + textLabel3 + + + Co&ntrast + + + PlainText + + + contrastSlider + + + + + contrastSlider + + + 1 + + + 100 + + + Horizontal + + + Here you can vary the contrast of the filemap in realtime. + + + + + + + + + useAntialiasing + + + &Use anti-aliasing + + + Anti-aliasing the filemap makes it clearer and prettier, unfortunately it also makes rendering very slow. + + + + + layout10 + + + + unnamed + + + 0 + + + + varyLabelFontSizes + + + true + + + Var&y label font sizes + + + The font size of exploded labels can be varied relative to the depth of the directories they represent. This helps you spot the important labels more easily. Set a sensible minimum font size. + + + + + layout9 + + + + unnamed + + + + spacer4 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + textLabel2 + + + true + + + Minimum font si&ze: + + + PlainText + + + minFontPitch + + + The smallest font size Filelight can use to render labels. + + + + + minFontPitch + + + true + + + + 3 + 0 + 0 + 0 + + + + + 80 + 0 + + + + 20 + + + 1 + + + 9 + + + + + + + + + showSmallFiles + + + Show small files + + + Some files are too small to be rendered on the filemap. Selecting this option makes these files visible by merging them all into a single "multi-segment". + + + + + + + + layout6 + + + + unnamed + + + + m_resetButton + + + &Reset + + + false + + + Reset any changes you have made since you opened this dialog. + + + + + spacer1 + + + Horizontal + + + Expanding + + + + 120 + 30 + + + + + + m_closeButton + + + &Close + + + true + + + + + + + + + QVButtonGroup +
qvbuttongroup.h
+ + -1 + -1 + + 0 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000002ff49444154388db59531681c4714863f992dde820cb370815b50600f54e8ca0ba43970712a8fb838438a3895634813d238a5ab80e314ae4d0a812060a4226017c27221c8a9da6b8c4fe0e00d28b0571cec82043b85611f78c12966efa4bb8bc085f29a6567df7cef9f7fdeccaec571cc2cbaddee47ae21e2385e5b9b815f1ebcfcd8de6a63ad25cb338af7c52741acb5a4a729a3d723a82ec6bd99d267bf3f23fc1c4cab2442d14a915986e792fdfa59569766573049417784f1b12e8267954dab24b78714450a28beaf941f847c2a14e70a0841035a2d45d641eb027213c210c69756320767794684d6508bef0befde1a860796e4c402333542b4256c0f0cdd1e50b97191458be6e0e27d81563a87c643d8fb2d7793d685d696413cc8a6cae46f65f7d79c7c62b87b4f2e15fd0fb0d302be0fefde4a0d557a5f35e90f84e0334014d590f855c9de4ecee17e4eb319d1ff3a00ec02f8c67299f283307c61e7d06fbf1782d082588a33e1cf1705fd81cf773f3601e1f9bec59e2f4b5c7ef5209f0ac95f16630cfd818067c103b586dd274a726229cee0fe8380d191cb4d1267d3d58aa1de7d258ceae5d7d0a78fdd269a86f0c52d414c49bbe3762c9b686de41560d7a72e41c4795a6486a78f95e4c4151481d686efbe7b3398ac58b1a23868b8c474aaa8068c8e714a8dd06c1af2a9e5d1c38c641c909dba6e08237f19b358a7ac5cf3479bc2e41f257e55d2ffc6a73833746f09e186cfa387904f2cbffc90a2aa9886d0e99464d3c5965b512cebd01f1800f67672e2a392fb0f023a3d883a053ffddcc2340dd65ab452b6074dc2cd15c1cbceb863daed413e353cdfcfd97d92333a12da6d0181ec3443cf753ef3cdd092de0e116ff1a02cdc157338ca9d7b8269461cfee1ba2139b9286e1a427f10110f2d561555b076d18a39383d4d99a4c0cd0b787f20747b214962c8266e3cdcf0e97c59126ec2f6edd089f40a92f115e0d1eb11ba238461dd6a15f32b53666de841965bb203575a3cc15a48c64a965fe57105e3635db8fa96dcffc431172b5d715d7103dc3fea7f015f373c8ee3b57f0135105a0fae7717960000000049454e44ae426082 + + + + + scanAcrossMounts + toggled(bool) + Dialog + toggleScanAcrossMounts(bool) + + + dontScanRemoteMounts + toggled(bool) + Dialog + toggleDontScanRemoteMounts(bool) + + + dontScanRemovableMedia + toggled(bool) + Dialog + toggleDontScanRemovableMedia(bool) + + + + tabWidget + colourSchemeGroup + contrastSlider + useAntialiasing + varyLabelFontSizes + minFontPitch + m_resetButton + m_closeButton + m_listBox + m_removeButton + m_addButton + scanAcrossMounts + dontScanRemoteMounts + dontScanRemovableMedia + + + toggleDontScanRemovableMedia(bool) + toggleDontScanRemoteMounts(bool) + toggleScanAcrossMounts(bool) + + + + qvbuttongroup.h + knuminput.h + +
diff --git a/src/part/fileTree.cpp b/src/part/fileTree.cpp new file mode 100644 index 0000000..f459d50 --- /dev/null +++ b/src/part/fileTree.cpp @@ -0,0 +1,67 @@ +//Author: Max Howell , (C) 2004 +//Copyright: See COPYING file that comes with this distribution + +#include "fileTree.h" +#include +#include +#include + + +//static definitions +const uint File::DENOMINATOR[4] = { 1<<0, 1<<10, 1<<20, 1<<30 }; +static const char PREFIX[4] = { 'K', 'M', 'G', 'T' }; + + +QString +File::fullPath( const Directory *root /*= 0*/ ) const +{ + QString path; + + if( root == this ) + root = 0; //prevent returning empty string when there is something we could return + + for( const Directory *d = (Directory*)this; d != root && d; d = d->parent() ) + path.prepend( d->name() ); + + return path; +} + +QString +File::humanReadableSize( UnitPrefix key /*= mega*/ ) const //FIXME inline +{ + return humanReadableSize( m_size, key ); +} + +QString +File::humanReadableSize( uint size, UnitPrefix key /*= mega*/ ) //static +{ + if( size == 0 ) + return "0 B"; + + QString s; + double prettySize = (double)size / (double)DENOMINATOR[key]; + const KLocale &locale = *KGlobal::locale(); + + if( prettySize >= 0.01 ) + { + //use three significant figures + if( prettySize < 1 ) s = locale.formatNumber( prettySize, 2 ); + else if( prettySize < 100 ) s = locale.formatNumber( prettySize, 1 ); + else s = locale.formatNumber( prettySize, 0 ); + + s += ' '; + s += PREFIX[key]; + s += 'B'; + } + + if( prettySize < 0.1 ) + { + s += " ("; + s += locale.formatNumber( size / DENOMINATOR[key - 1], 0 ); + s += ' '; + s += PREFIX[key - 1]; + s += "B)"; + } + + return s; +} diff --git a/src/part/fileTree.h b/src/part/fileTree.h new file mode 100644 index 0000000..25ac6fb --- /dev/null +++ b/src/part/fileTree.h @@ -0,0 +1,238 @@ +//Author: Max Howell , (C) 2004 +//Copyright: See COPYING file that comes with this distribution + +#ifndef FILETREE_H +#define FILETREE_H + +#include //qstrdup +#include //decodeName() +#include + + +//TODO these are pointlessly general purpose now, make them incredibly specific + + + +typedef unsigned long int FileSize; +typedef unsigned long int Dirsize; //**** currently unused + +template class Iterator; +template class ConstIterator; +template class Chain; + +template +class Link +{ +public: + Link( T* const t ) : prev( this ), next( this ), data( t ) {} + Link() : prev( this ), next( this ), data( 0 ) {} + +//TODO unlinking is slow and you don't use it very much in this context. +// ** Perhaps you can make a faster deletion system that doesn't bother tidying up first +// ** and then you MUST call some kind of detach() function when you remove elements otherwise + ~Link() { delete data; unlink(); } + + friend class Iterator; + friend class ConstIterator; + friend class Chain; + +private: + void unlink() { prev->next = next; next->prev = prev; prev = next = this; } + + Link* prev; + Link* next; + + T* data; //ensure only iterators have access to this +}; + + +template +class Iterator +{ +public: + Iterator() : link( 0 ) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be derefenced + Iterator( Link *p ) : link( p ) { } + + bool operator==( const Iterator& it ) const { return link == it.link; } + bool operator!=( const Iterator& it ) const { return link != it.link; } + bool operator!=( const Link *p ) const { return p != link; } + + //here we have a choice, really I should make two classes one const the other not + const T* operator*() const { return link->data; } + T* operator*() { return link->data; } + + Iterator& operator++() { link = link->next; return *this; } //**** does it waste time returning in places where we don't use the retval? + + bool isNull() const { return (link == 0); } //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible + + void transferTo( Chain &chain ) + { + chain.append( remove() ); + } + + T* const remove() //remove from list, delete Link, data is returned NOT deleted + { + T* const d = link->data; + Link* const p = link->prev; + + link->data = 0; + delete link; + link = p; //make iterator point to previous element, YOU must check this points to an element + + return d; + } + +private: + Link *link; +}; + + +template +class ConstIterator +{ +public: + ConstIterator( Link *p ) : link( p ) { } + + bool operator==( const Iterator& it ) const { return link == it.link; } + bool operator!=( const Iterator& it ) const { return link != it.link; } + bool operator!=( const Link *p ) const { return p != link; } + + const T* operator*() const { return link->data; } + + ConstIterator& operator++() { link = link->next; return *this; } + +private: + const Link *link; +}; + + +template +class Chain +{ +public: + virtual ~Chain() { empty(); } + + void append( T* const data ) + { + Link* const link = new Link( data ); + + link->prev = head.prev; + link->next = &head; + + head.prev->next = link; + head.prev = link; + } + + void transferTo( Chain &c ) + { + if( isEmpty() ) return; + + Link* const first = head.next; + Link* const last = head.prev; + + head.unlink(); + + first->prev = c.head.prev; + c.head.prev->next = first; + + last->next = &c.head; + c.head.prev = last; + } + + void empty() { while( head.next != &head ) { delete head.next; } } + + Iterator iterator() const { return Iterator( head.next ); } + ConstIterator constIterator() const { return ConstIterator( head.next ); } + const Link *end() const { return &head; } + bool isEmpty() const { return head.next == &head; } + +private: + Link head; + void operator=( const Chain& ); +}; + + +class Directory; +class QString; + +class File +{ +public: + friend class Directory; + + enum UnitPrefix { kilo, mega, giga, tera }; + + static const uint DENOMINATOR[4]; + +public: + File( const char *name, FileSize size ) : m_parent( 0 ), m_name( qstrdup( name ) ), m_size( size ) {} + virtual ~File() { delete [] m_name; } + + const Directory *parent() const { return m_parent; } + const char *name8Bit() const { return m_name; } + const FileSize size() const { return m_size; } + QString name() const { return QFile::decodeName( m_name ); } + + virtual bool isDirectory() const { return false; } + + QString fullPath( const Directory* = 0 ) const; + QString humanReadableSize( UnitPrefix key = mega ) const; + +public: + static QString humanReadableSize( uint size, UnitPrefix Key = mega ); + +protected: + File( const char *name, FileSize size, Directory *parent ) : m_parent( parent ), m_name( qstrdup( name ) ), m_size( size ) {} + + Directory *m_parent; //0 if this is treeRoot + char *m_name; + FileSize m_size; //in units of KiB + +private: + File( const File& ); + void operator=( const File& ); +}; + + +class Directory : public Chain, public File +{ +public: + Directory( const char *name ) : File( name, 0 ), m_children( 0 ) {} //DON'T pass the full path! + + uint children() const { return m_children; } + virtual bool isDirectory() const { return true; } + + ///appends a Directory + void append( Directory *d, const char *name=0 ) + { + if( name ) { + delete [] d->m_name; + d->m_name = qstrdup( name ); } //directories that had a fullpath copy just their names this way + + m_children += d->children(); //doesn't include the dir itself + d->m_parent = this; + append( (File*)d ); //will add 1 to filecount for the dir itself + } + + ///appends a File + void append( const char *name, FileSize size ) + { + append( new File( name, size, this ) ); + } + +private: + void append( File *p ) + { + m_children++; + m_size += p->size(); + Chain::append( p ); + } + + uint m_children; + +private: + Directory( const Directory& ); //undefined + void operator=( const Directory& ); //undefined +}; + +#endif diff --git a/src/part/localLister.cpp b/src/part/localLister.cpp new file mode 100644 index 0000000..6bf7945 --- /dev/null +++ b/src/part/localLister.cpp @@ -0,0 +1,333 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "Config.h" +#include "debug.h" +#include +#include "fileTree.h" +#include +#include "localLister.h" +#ifdef HAVE_MNTENT_H +#include +#endif +#include //postEvent() +#include +#include "scan.h" +#include +#include +#include + +namespace Filelight +{ + QStringList LocalLister::s_remoteMounts; + QStringList LocalLister::s_localMounts; + + LocalLister::LocalLister( const QString &path, Chain *cachedTrees, QObject *parent ) + : QThread() + , m_path( path ) + , m_trees( cachedTrees ) + , m_parent( parent ) + { + //add empty directories for any mount points that are in the path + //TODO empty directories is not ideal as adds to fileCount incorrectly + + QStringList list( Config::skipList ); + if( !Config::scanAcrossMounts ) list += s_localMounts; + if( !Config::scanRemoteMounts ) list += s_remoteMounts; + + for( QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) + if( (*it).startsWith( path ) ) + //prevent scanning of these directories + m_trees->append( new Directory( (*it).local8Bit() ) ); + + start(); + } + + void + LocalLister::run() + { + //recursively scan the requested path + const QCString path = QFile::encodeName( m_path ); + Directory *tree = scan( path, path ); + + //delete the list of trees useful for this scan, + //in a sucessful scan the contents would now be transfered to 'tree' + delete m_trees; + + if( ScanManager::s_abort ) //scan was cancelled + { + debug() << "Scan succesfully aborted\n"; + delete tree; + tree = 0; + } + + QCustomEvent *e = new QCustomEvent( 1000 ); + e->setData( tree ); + QApplication::postEvent( m_parent, e ); + } + + // from system.h in GNU coreutils package + /* Extract or fake data from a `struct stat'. + ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. + ST_NBLOCKS: Number of blocks in the file, including indirect blocks. + ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ + #ifndef HAVE_STRUCT_STAT_ST_BLOCKS + #define ST_BLKSIZE(statbuf) DEV_BSIZE + #if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */ + #define ST_NBLOCKS(statbuf) ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0)) + #else /* !_POSIX_SOURCE && BSIZE */ + #define ST_NBLOCKS(statbuf) (S_ISREG ((statbuf).st_mode) || S_ISDIR ((statbuf).st_mode) ? st_blocks ((statbuf).st_size) : 0) + #endif /* !_POSIX_SOURCE && BSIZE */ + #else /* HAVE_STRUCT_STAT_ST_BLOCKS */ + /* Some systems, like Sequents, return st_blksize of 0 on pipes. + Also, when running `rsh hpux11-system cat any-file', cat would + determine that the output stream had an st_blksize of 2147421096. + So here we arbitrarily limit the `optimal' block size to 4MB. + If anyone knows of a system for which the legitimate value for + st_blksize can exceed 4MB, please report it as a bug in this code. */ + #define ST_BLKSIZE(statbuf) ((0 < (statbuf).st_blksize && (statbuf).st_blksize <= (1 << 22)) /* 4MiB */ ? (statbuf).st_blksize : DEV_BSIZE) + #if defined hpux || defined __hpux__ || defined __hpux + /* HP-UX counts st_blocks in 1024-byte units. + This loses when mixing HP-UX and BSD filesystems with NFS. */ + #define ST_NBLOCKSIZE 1024 + #else /* !hpux */ + #if defined _AIX && defined _I386 + /* AIX PS/2 counts st_blocks in 4K units. */ + #define ST_NBLOCKSIZE (4 * 1024) + #else /* not AIX PS/2 */ + #if defined _CRAY + #define ST_NBLOCKS(statbuf) (S_ISREG ((statbuf).st_mode) || S_ISDIR ((statbuf).st_mode) ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) + #endif /* _CRAY */ + #endif /* not AIX PS/2 */ + #endif /* !hpux */ + #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ + + #ifndef ST_NBLOCKS + #define ST_NBLOCKS(statbuf) ((statbuf).st_blocks) + #endif + + #ifndef ST_NBLOCKSIZE + #define ST_NBLOCKSIZE 512 + #endif + +//some GNU systems don't support big files for some reason +#ifdef __USE_LARGEFILE64 //see dirent.h + #define dirent dirent64 + #define scandir scandir64 + #define stat stat64 + #define statstruct stat64 + #define lstat lstat64 + #define readdir readdir64 +#endif + +#ifndef NULL +#define NULL 0 +#endif + + + #include + static void + outputError( QCString path ) + { + ///show error message that stat or opendir may give + + #define out( s ) error() << s ": " << path << endl; break + + switch( errno ) { + case EACCES: + out( "Inadequate access permisions" ); + case EMFILE: + out( "Too many file descriptors in use by Filelight" ); + case ENFILE: + out( "Too many files are currently open in the system" ); + case ENOENT: + out( "A component of the path does not exist, or the path is an empty string" ); + case ENOMEM: + out( "Insufficient memory to complete the operation" ); + case ENOTDIR: + out( "A component of the path is not a directory" ); + case EBADF: + out( "Bad file descriptor" ); + case EFAULT: + out( "Bad address" ); + case ELOOP: //NOTE shouldn't ever happen + out( "Too many symbolic links encountered while traversing the path" ); + case ENAMETOOLONG: + out( "File name too long" ); + } + + #undef out + } + + Directory* + LocalLister::scan( const QCString &path, const QCString &dirname ) + { + Directory *cwd = new Directory( dirname ); + DIR *dir = opendir( path ); + + if( !dir ) { + outputError( path ); + return cwd; + } + + struct stat statbuf; + dirent *ent; + while ((ent = readdir( dir ))) + { + if( ScanManager::s_abort ) + return cwd; + + if( qstrcmp( ent->d_name, "." ) == 0 || qstrcmp( ent->d_name, ".." ) == 0 ) + continue; + + QCString new_path = path; new_path += ent->d_name; + + //get file information + if( lstat( new_path, &statbuf ) == -1 ) { + outputError( new_path ); + continue; + } + + if( S_ISLNK( statbuf.st_mode ) || + S_ISCHR( statbuf.st_mode ) || + S_ISBLK( statbuf.st_mode ) || + S_ISFIFO( statbuf.st_mode ) || + S_ISSOCK( statbuf.st_mode ) ) + { + continue; + } + + if( S_ISREG( statbuf.st_mode ) ) //file + //using units of KiB as 32bit max is 4GiB and 64bit ints are expensive + cwd->append( ent->d_name, (ST_NBLOCKS( statbuf ) * ST_NBLOCKSIZE) / 1024 ); + + else if( S_ISDIR( statbuf.st_mode ) ) //directory + { + Directory *d = 0; + QCString new_dirname = ent->d_name; + new_dirname += '/'; + new_path += '/'; + + //check to see if we've scanned this section already + + for( Iterator it = m_trees->iterator(); it != m_trees->end(); ++it ) + { + if( new_path == (*it)->name8Bit() ) + { + debug() << "Tree pre-completed: " << (*it)->name() << "\n"; + d = it.remove(); + ScanManager::s_files += d->children(); + //**** ideally don't have this redundant extra somehow + cwd->append( d, new_dirname ); + } + } + + if( !d ) //then scan + if ((d = scan( new_path, new_dirname ))) //then scan was successful + cwd->append( d ); + } + + ++ScanManager::s_files; + } + + closedir( dir ); + + return cwd; + } + + bool + LocalLister::readMounts() + { + #define INFO_PARTITIONS "/proc/partitions" + #define INFO_MOUNTED_PARTITIONS "/etc/mtab" /* on Linux... */ + + //**** SHAMBLES + // ** mtab should have priority as mount points don't have to follow fstab + // ** no removable media detection + // ** no updates if mounts change + // ** you want a KDE extension that handles this for you really + + struct fstab *fstab_ent; +#ifdef HAVE_MNTENT_H + struct mntent *mnt_ent; +#endif + QString str; + + +#ifdef HAVE_MNTENT_H + FILE *fp; + if( setfsent() == 0 || !( fp = setmntent( INFO_MOUNTED_PARTITIONS, "r" ) ) ) +#else + if( setfsent() == 0 ) +#endif + return false; + + #define FS_NAME fstab_ent->fs_spec // device-name + #define FS_FILE fstab_ent->fs_file // mount-point + #define FS_TYPE fstab_ent->fs_vfstype // fs-type + #define FS_MNTOPS fstab_ent->fs_mntops // mount-options + + QStringList remoteFsTypes; + remoteFsTypes << "smbfs" ; +#ifdef MNTTYPE_NFS + remoteFsTypes << MNTTYPE_NFS; +#else + remoteFsTypes << "nfs"; +#endif + // What about afs? + + while( (fstab_ent = getfsent()) != NULL ) + { + str = QString( FS_FILE ); + if( str == "/" ) continue; + str += '/'; + + if( remoteFsTypes.contains( FS_TYPE ) ) + s_remoteMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!! + + else + s_localMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!! + + kdDebug() << "FSTAB: " << FS_TYPE << "\n"; + } + + endfsent(); /* close fstab.. */ + + #undef FS_NAME + #undef FS_FILE + #undef FS_TYPE + #undef FS_MNTOPS + + #define FS_NAME mnt_ent->mnt_fsname // device-name + #define FS_FILE mnt_ent->mnt_dir // mount-point + #define FS_TYPE mnt_ent->mnt_type // fs-type + #define FS_MNTOPS mnt_ent->mnt_opts // mount-options + + //scan mtab, **** mtab should take priority, but currently it isn't + +#ifdef HAVE_MNTENT_H + while( ( mnt_ent = getmntent( fp ) ) != NULL ) + { + bool b = false; + + str = QString( FS_FILE ); + if( str == "/" ) continue; + str += "/"; + + if( remoteFsTypes.contains( FS_TYPE ) ) + if( b = !s_remoteMounts.contains( str ) ) + s_remoteMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!! + + else if( b = !s_localMounts.contains( str ) ) + s_localMounts.append( str ); //**** NO! can't be sure won't have trailing slash, need to do a check first dummy!! + + if( b ) kdDebug() << "MTAB: " << FS_TYPE << "\n"; + } + + endmntent( fp ); /* close mtab.. */ +#endif + + + return true; + } +} diff --git a/src/part/localLister.h b/src/part/localLister.h new file mode 100644 index 0000000..5070e14 --- /dev/null +++ b/src/part/localLister.h @@ -0,0 +1,35 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef LOCALLISTER_H +#define LOCALLISTER_H + +#include + +class Directory; +template class Chain; + +namespace Filelight +{ + class LocalLister : public QThread + { + public: + LocalLister( const QString &path, Chain *cachedTrees, QObject *parent ); + + static bool readMounts(); + + private: + QString m_path; + Chain *m_trees; + QObject *m_parent; + + private: + virtual void run(); + Directory *scan( const QCString&, const QCString& ); + + private: + static QStringList s_localMounts, s_remoteMounts; //TODO namespace + }; +} + +#endif diff --git a/src/part/part.cpp b/src/part/part.cpp new file mode 100644 index 0000000..9a4742a --- /dev/null +++ b/src/part/part.cpp @@ -0,0 +1,254 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "Config.h" +#include "debug.h" +#include "define.h" +#include "fileTree.h" +#include "part.h" +#include "progressBox.h" +#include "radialMap/widget.h" +#include "scan.h" +#include "settingsDialog.h" +#include "summaryWidget.h" + +#include //::createAboutData() +#include +#include +#include //::start() +//#include +#include +#include +#include +#include //encodeName() +#include //postInit() hack +#include +#include //access() + + +namespace Filelight { + + +typedef KParts::GenericFactory Factory; +K_EXPORT_COMPONENT_FACTORY( libfilelight, Filelight::Factory ) + + +BrowserExtension::BrowserExtension( Part *parent, const char *name ) + : KParts::BrowserExtension( parent, name ) +{} + + +Part::Part( QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const QStringList& ) + : ReadOnlyPart( parent, name ) + , m_ext( new BrowserExtension( this ) ) + , m_statusbar( new StatusBarExtension( this ) ) + , m_map( 0 ) + , m_manager( new ScanManager( this ) ) + , m_started( false ) +{ + QPixmap::setDefaultOptimization( QPixmap::BestOptim ); + + Config::read(); + + setInstance( Factory::instance() ); + setWidget( new QVBox( parentWidget, widgetName ) ); + setXMLFile( "filelight_partui.rc" ); + + m_map = new RadialMap::Widget( widget() ); + m_map->hide(); + + KStdAction::zoomIn( m_map, SLOT(zoomIn()), actionCollection() ); + KStdAction::zoomOut( m_map, SLOT(zoomOut()), actionCollection() ); + KStdAction::preferences( this, SLOT(configFilelight()), actionCollection(), "configure_filelight" )->setText( i18n( "Configure Filelight..." ) ); + + connect( m_map, SIGNAL(created( const Directory* )), SIGNAL(completed()) ); + connect( m_map, SIGNAL(created( const Directory* )), SLOT(mapChanged( const Directory* )) ); + connect( m_map, SIGNAL(activated( const KURL& )), SLOT(updateURL( const KURL& )) ); + + // TODO make better system + connect( m_map, SIGNAL(giveMeTreeFor( const KURL& )), SLOT(updateURL( const KURL& )) ); + connect( m_map, SIGNAL(giveMeTreeFor( const KURL& )), SLOT(openURL( const KURL& )) ); + + connect( m_manager, SIGNAL(completed( Directory* )), SLOT(scanCompleted( Directory* )) ); + connect( m_manager, SIGNAL(aboutToEmptyCache()), m_map, SLOT(invalidate()) ); + + QTimer::singleShot( 0, this, SLOT(postInit()) ); +} + +void +Part::postInit() +{ + if( m_url.isEmpty() ) //if url is not empty openURL() has been called immediately after ctor, which happens + { + QWidget *summary = new SummaryWidget( widget(), "summaryWidget" ); + connect( summary, SIGNAL(activated( const KURL& )), SLOT(openURL( const KURL& )) ); + summary->show(); + + //FIXME KXMLGUI is b0rked, it should allow us to set this + //BEFORE createGUI is called but it doesn't + stateChanged( "scan_failed" ); + } +} + +bool +Part::openURL( const KURL &u ) +{ + //we don't want to be using the summary screen anymore + delete widget()->child( "summaryWidget" ); + m_map->show(); + + //TODO everyone hates dialogs, instead render the text in big fonts on the Map + //TODO should have an empty KURL until scan is confirmed successful + //TODO probably should set caption to QString::null while map is unusable + + #define KMSG( s ) KMessageBox::information( widget(), s ) + + KURL url = u; + url.cleanPath( true ); + const QString path = url.path( 1 ); + const QCString path8bit = QFile::encodeName( path ); + const bool isLocal = url.protocol() == "file"; + + if( url.isEmpty() ) + { + //do nothing, chances are the user accidently pressed ENTER + } + else if( !url.isValid() ) + { + KMSG( i18n( "The entered URL cannot be parsed; it is invalid." ) ); + } + else if( path[0] != '/' ) + { + KMSG( i18n( "Filelight only accepts absolute paths, eg. /%1" ).arg( path ) ); + } + else if( isLocal && access( path8bit, F_OK ) != 0 ) //stat( path, &statbuf ) == 0 + { + KMSG( i18n( "Directory not found: %1" ).arg( path ) ); + } + else if( isLocal && access( path8bit, R_OK | X_OK ) != 0 ) + { + KMSG( i18n( "Unable to enter: %1\nYou do not have access rights to this location." ).arg( path ) ); + } + else + { + if( url == m_url ) + m_manager->emptyCache(); //same as rescan() + + return start( url ); + } + + return false; +} + +bool +Part::closeURL() +{ + if( m_manager->abort() ) + statusBar()->message( i18n( "Aborting Scan..." ) ); + + m_url = KURL(); + + return true; +} + +void +Part::updateURL( const KURL &u ) +{ + //the map has changed internally, update the interface to reflect this + emit m_ext->openURLNotify(); //must be done first + emit m_ext->setLocationBarURL( u.prettyURL() ); + + //do this last, or it breaks Konqi location bar + m_url = u; +} + +void +Part::configFilelight() +{ + QWidget *dialog = new SettingsDialog( widget(), "settings_dialog" ); + + connect( dialog, SIGNAL(canvasIsDirty( int )), m_map, SLOT(refresh( int )) ); + connect( dialog, SIGNAL(mapIsInvalid()), m_manager, SLOT(emptyCache()) ); + + dialog->show(); //deletes itself +} + +KAboutData* +Part::createAboutData() +{ + return new KAboutData( APP_NAME, I18N_NOOP( APP_PRETTYNAME ), APP_VERSION ); +} + +bool +Part::start( const KURL &url ) +{ + if( !m_started ) { + m_statusbar->addStatusBarItem( new ProgressBox( statusBar(), this ), 0, true ); + connect( m_map, SIGNAL(mouseHover( const QString& )), statusBar(), SLOT(message( const QString& )) ); + connect( m_map, SIGNAL(created( const Directory* )), statusBar(), SLOT(clear()) ); + m_started = true; + } + + if( m_manager->start( url ) ) { + m_url = url; + + const QString s = i18n( "Scanning: %1" ).arg( prettyURL() ); + stateChanged( "scan_started" ); + emit started( 0 ); //as a Part, we have to do this + emit setWindowCaption( s ); + statusBar()->message( s ); + m_map->invalidate(); //to maintain ui consistency + + return true; + } + + return false; +} + +void +Part::rescan() +{ + //FIXME we have to empty the cache because otherwise rescan picks up the old tree.. + m_manager->emptyCache(); //causes canvas to invalidate + start( m_url ); +} + +void +Part::scanCompleted( Directory *tree ) +{ + if( tree ) { + statusBar()->message( i18n( "Scan completed, generating map..." ) ); + + m_map->create( tree ); + + //do after creating map + stateChanged( "scan_complete" ); + } + else { + stateChanged( "scan_failed" ); + emit canceled( i18n( "Scan failed: %1" ).arg( prettyURL() ) ); + emit setWindowCaption( QString::null ); + + statusBar()->clear(); +// QTimer::singleShot( 2000, statusBar(), SLOT(clear()) ); + + m_url = KURL(); + } +} + +void +Part::mapChanged( const Directory *tree ) +{ + //IMPORTANT -> m_url has already been set + + emit setWindowCaption( prettyURL() ); + + ProgressBox *progress = static_cast(statusBar()->child( "ProgressBox" )); + + if( progress ) + progress->setText( tree->children() ); +} + +} //namespace Filelight + +#include "part.moc" diff --git a/src/part/part.h b/src/part/part.h new file mode 100644 index 0000000..348b22b --- /dev/null +++ b/src/part/part.h @@ -0,0 +1,71 @@ +// Author: Max Howell , (C) 2003-4 +// Copyright: See COPYING file that comes with this distribution + +#ifndef FILELIGHTPART_H +#define FILELIGHTPART_H + +#include +#include +#include +#include + +class KAboutData; +using KParts::StatusBarExtension; +namespace RadialMap { class Widget; } +class Directory; + + +namespace Filelight +{ + class Part; + + class BrowserExtension : public KParts::BrowserExtension + { + public: + BrowserExtension( Part*, const char * = 0 ); + }; + + + class Part : public KParts::ReadOnlyPart + { + Q_OBJECT + + public: + Part( QWidget *, const char *, QObject *, const char *, const QStringList& ); + + virtual bool openFile() { return false; } //pure virtual in base class + virtual bool closeURL(); + + QString prettyURL() const { return m_url.protocol() == "file" ? m_url.path() : m_url.prettyURL(); } + + static KAboutData *createAboutData(); + + public slots: + virtual bool openURL( const KURL& ); + void configFilelight(); + void rescan(); + + private slots: + void postInit(); + void scanCompleted( Directory* ); + void mapChanged( const Directory* ); + + private: + KStatusBar *statusBar() { return m_statusbar->statusBar(); } + + BrowserExtension *m_ext; + StatusBarExtension *m_statusbar; + RadialMap::Widget *m_map; + class ScanManager *m_manager; + + bool m_started; + + private: + bool start( const KURL& ); + + private slots: + void updateURL( const KURL & ); + }; +} + +#endif diff --git a/src/part/progressBox.cpp b/src/part/progressBox.cpp new file mode 100644 index 0000000..5bf205a --- /dev/null +++ b/src/part/progressBox.cpp @@ -0,0 +1,65 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include +#include +#include +#include + +#include "scan.h" +#include "progressBox.h" + + +ProgressBox::ProgressBox( QWidget *parent, QObject *part ) + : QLabel( parent, "ProgressBox" ) +{ + hide(); + + setAlignment( Qt::AlignCenter ); + setFont( KGlobalSettings::fixedFont() ); + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); + + setText( 999999 ); + setMinimumWidth( sizeHint().width() ); + + connect( &m_timer, SIGNAL(timeout()), SLOT(report()) ); + connect( part, SIGNAL(started( KIO::Job* )), SLOT(start()) ); + connect( part, SIGNAL(completed()), SLOT(stop()) ); + connect( part, SIGNAL(canceled( const QString& )), SLOT(halt()) ); +} + +void +ProgressBox::start() //slot +{ + m_timer.start( 50 ); //20 times per second - very smooth + report(); + show(); +} + +void +ProgressBox::report() //slot +{ + setText( Filelight::ScanManager::files() ); +} + +void +ProgressBox::stop() +{ + m_timer.stop(); +} + +void +ProgressBox::halt() +{ + // canceled by stop button + m_timer.stop(); + QTimer::singleShot( 2000, this, SLOT(hide()) ); +} + +void +ProgressBox::setText( int files ) +{ + QLabel::setText( i18n("%n File", "%n Files", files) ); +} + +#include "progressBox.moc" diff --git a/src/part/progressBox.h b/src/part/progressBox.h new file mode 100644 index 0000000..17cc2a6 --- /dev/null +++ b/src/part/progressBox.h @@ -0,0 +1,32 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef PROGRESSBOX_H +#define PROGRESSBOX_H + +#include +#include + +namespace KIO { class Job; } + + +class ProgressBox : public QLabel +{ +Q_OBJECT + +public: + ProgressBox( QWidget*, QObject* ); + + void setText( int ); + +public slots: + void start(); + void report(); + void stop(); + void halt(); + +private: + QTimer m_timer; +}; + +#endif diff --git a/src/part/radialMap/Makefile.am b/src/part/radialMap/Makefile.am new file mode 100644 index 0000000..989cbe1 --- /dev/null +++ b/src/part/radialMap/Makefile.am @@ -0,0 +1,4 @@ +INCLUDES = -I$(top_srcdir)/src/part $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libradialmap.la +libradialmap_la_SOURCES = widget.cpp builder.cpp map.cpp widgetEvents.cpp labels.cpp segmentTip.cpp diff --git a/src/part/radialMap/builder.cpp b/src/part/radialMap/builder.cpp new file mode 100644 index 0000000..68dc382 --- /dev/null +++ b/src/part/radialMap/builder.cpp @@ -0,0 +1,141 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include "builder.h" +#include "Config.h" +#include "fileTree.h" +#include //locale object +#include +#include "widget.h" + + +//**** REMOVE NEED FOR the +1 with MAX_RING_DEPTH uses +//**** add some angle bounds checking (possibly in Segment ctor? can I delete in a ctor?) +//**** this class is a mess + +RadialMap::Builder::Builder( RadialMap::Map *m, const Directory* const d, bool fast ) + : m_map( m ) + , m_root( d ) + , m_minSize( static_cast((d->size() * 3) / (PI * m->height() - m->MAP_2MARGIN )) ) + , m_depth( &m->m_visibleDepth ) +{ + m_signature = new Chain [*m_depth + 1]; + + if( !fast )//|| *m_depth == 0 ) //depth 0 is special case usability-wise //**** WHY?! + { + //determine depth rather than use old one + findVisibleDepth( d ); //sets m_depth + } + + m_map->setRingBreadth(); + setLimits( m_map->m_ringBreadth ); + build( d ); + + m_map->m_signature = m_signature; + + delete [] m_limits; +} + + +void +RadialMap::Builder::findVisibleDepth( const Directory* const dir, const unsigned int depth ) +{ + //**** because I don't use the same minimumSize criteria as in the visual function + // this can lead to incorrect visual representation + //**** BUT, you can't set those limits until you know m_depth! + + //**** also this function doesn't check to see if anything is actually visible + // it just assumes that when it reaches a new level everything in it is visible + // automatically. This isn't right especially as there might be no files in the + // dir provided to this function! + + static uint stopDepth = 0; + + if( dir == m_root ) + { + stopDepth = *m_depth; + *m_depth = 0; + } + + if( *m_depth < depth ) *m_depth = depth; + if( *m_depth >= stopDepth ) return; + + for( ConstIterator it = dir->constIterator(); it != dir->end(); ++it ) + if( (*it)->isDirectory() && (*it)->size() > m_minSize ) + findVisibleDepth( (Directory *)*it, depth + 1 ); //if no files greater than min size the depth is still recorded +} + +void +RadialMap::Builder::setLimits( const uint &b ) //b = breadth? +{ + double size3 = m_root->size() * 3; + double pi2B = PI * 2 * b; + + m_limits = new uint [*m_depth + 1]; //FIXME delete! + + for( unsigned int d = 0; d <= *m_depth; ++d ) + m_limits[d] = (uint)(size3 / (double)(pi2B * (d + 1))); //min is angle that gives 3px outer diameter for that depth +} + + +//**** segments currently overlap at edges (i.e. end of first is start of next) +bool +RadialMap::Builder::build( const Directory* const dir, const unsigned int depth, unsigned int a_start, const unsigned int a_end ) +{ + //first iteration: dir == m_root + + if( dir->children() == 0 ) //we do fileCount rather than size to avoid chance of divide by zero later + return false; + + uint hiddenSize = 0, hiddenFileCount = 0; + + for( ConstIterator it = dir->constIterator(); it != dir->end(); ++it ) + { + if( (*it)->size() > m_limits[depth] ) + { + unsigned int a_len = (unsigned int)(5760 * ((double)(*it)->size() / (double)m_root->size())); + + Segment *s = new Segment( *it, a_start, a_len ); + + (m_signature + depth)->append( s ); + + if( (*it)->isDirectory() ) + { + if( depth != *m_depth ) + { + //recurse + s->m_hasHiddenChildren = build( (Directory*)*it, depth + 1, a_start, a_start + a_len ); + } + else s->m_hasHiddenChildren = true; + } + + a_start += a_len; //**** should we add 1? + + } else { + + hiddenSize += (*it)->size(); + + if( (*it)->isDirectory() ) //**** considered virtual, but dir wouldn't count itself! + hiddenFileCount += static_cast(*it)->children(); //need to add one to count the dir as well + + ++hiddenFileCount; + } + } + + if( hiddenFileCount == dir->children() && !Config::showSmallFiles ) + return true; + + else if( (Config::showSmallFiles && hiddenSize > m_limits[depth]) || (depth == 0 && (hiddenSize > dir->size()/8)) /*|| > size() * 0.75*/ ) + { + //append a segment for unrepresented space - a "fake" segment + + // I dunno how to i18n this + const QString s = i18n( "There can't ever be only 1 file", "%1 files, each about %2" ) + .arg( hiddenFileCount ) + .arg( File::humanReadableSize( hiddenSize/hiddenFileCount ) ); + + (m_signature + depth)->append( new Segment( new File( s.local8Bit(), hiddenSize ), a_start, a_end - a_start, true ) ); + } + + return false; +} diff --git a/src/part/radialMap/builder.h b/src/part/radialMap/builder.h new file mode 100644 index 0000000..819813a --- /dev/null +++ b/src/part/radialMap/builder.h @@ -0,0 +1,38 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#ifndef BUILDER_H +#define BUILDER_H + +#include "radialMap.h" //Segment, defines + +template class Chain; +class Directory; + + +namespace RadialMap +{ + class Map; + + //temporary class that builds the Map signature + + class Builder + { + public: + Builder( Map*, const Directory* const, bool fast=false ); + + private: + void findVisibleDepth( const Directory* const dir, const uint=0 ); + void setLimits( const uint& ); + bool build( const Directory* const, const uint=0, uint=0, const uint=5760 ); + + Map *m_map; + const Directory* const m_root; + const uint m_minSize; + uint *m_depth; + Chain *m_signature; + uint *m_limits; + }; +} + +#endif diff --git a/src/part/radialMap/labels.cpp b/src/part/radialMap/labels.cpp new file mode 100644 index 0000000..73a7ba8 --- /dev/null +++ b/src/part/radialMap/labels.cpp @@ -0,0 +1,327 @@ +//Author: Max Howell , (C) 2003-4 +//Copyright: See COPYING file that comes with this distribution + +#include +#include +#include +#include +#include + +#include "Config.h" +#include "fileTree.h" +#include "radialMap.h" +#include "sincos.h" +#include "widget.h" + + + +namespace RadialMap +{ + struct Label + { + Label( const RadialMap::Segment *s, int l ) : segment( s ), lvl( l ), a( segment->start() + (segment->length() / 2) ) { } + + bool tooClose( const int &aa ) const { return ( a > aa - LABEL_ANGLE_MARGIN && a < aa + LABEL_ANGLE_MARGIN ); } + + const RadialMap::Segment *segment; + const unsigned int lvl; + const int a; + + int x1, y1, x2, y2, x3; + int tx, ty; + + QString qs; + }; + + class LabelList : public QPtrList