KDirStat – a graphical disk usage utility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kdirtreeview.cpp 44KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947
  1. /*
  2. * File name: kdirtreeview.cpp
  3. * Summary: High level classes for KDirStat
  4. * License: LGPL - See file COPYING.LIB for details.
  5. * Author: Stefan Hundhammer <sh@suse.de>
  6. *
  7. * Updated: 2005-01-07
  8. */
  9. #include <time.h>
  10. #include <stdlib.h>
  11. #include <tqtimer.h>
  12. #include <tqcolor.h>
  13. #include <tqheader.h>
  14. #include <tqpopupmenu.h>
  15. #include <kapp.h>
  16. #include <tdelocale.h>
  17. #include <tdeglobal.h>
  18. #include <tdeglobalsettings.h>
  19. #include <kicontheme.h>
  20. #include <kiconloader.h>
  21. #include "kdirtreeview.h"
  22. #include "kdirtreeiterators.h"
  23. #include "kpacman.h"
  24. #define SEPARATE_READ_JOBS_COL 0
  25. #define VERBOSE_PROGRESS_INFO 0
  26. using namespace KDirStat;
  27. KDirTreeView::KDirTreeView( TQWidget * parent )
  28. : KDirTreeViewParentClass( parent )
  29. {
  30. _tree = 0;
  31. _updateTimer = 0;
  32. _selection = 0;
  33. _openLevel = 1;
  34. _doLazyClone = true;
  35. _doPacManAnimation = false;
  36. _updateInterval = 333; // millisec
  37. _sortCol = -1;
  38. for ( int i=0; i < DEBUG_COUNTERS; i++ )
  39. _debugCount[i] = 0;
  40. setDebugFunc( 1, "KDirTreeViewItem::init()" );
  41. setDebugFunc( 2, "KDirTreeViewItem::updateSummary()" );
  42. setDebugFunc( 3, "KDirTreeViewItem::deferredClone()" );
  43. setDebugFunc( 4, "KDirTreeViewItem::compare()" );
  44. setDebugFunc( 5, "KDirTreeViewItem::paintCell()" );
  45. #if SEPARATE_READ_JOBS_COL
  46. _readJobsCol = -1;
  47. #endif
  48. setRootIsDecorated( false );
  49. int numCol = 0;
  50. addColumn( i18n( "Name" ) ); _nameCol = numCol;
  51. _iconCol = numCol++;
  52. addColumn( i18n( "Subtree Percentage" ) ); _percentBarCol = numCol++;
  53. addColumn( i18n( "Percentage" ) ); _percentNumCol = numCol++;
  54. addColumn( i18n( "Subtree Total" ) ); _totalSizeCol = numCol++;
  55. _workingStatusCol = _totalSizeCol;
  56. addColumn( i18n( "Own Size" ) ); _ownSizeCol = numCol++;
  57. addColumn( i18n( "Items" ) ); _totalItemsCol = numCol++;
  58. addColumn( i18n( "Files" ) ); _totalFilesCol = numCol++;
  59. addColumn( i18n( "Subdirs" ) ); _totalSubDirsCol = numCol++;
  60. addColumn( i18n( "Last Change" ) ); _latestMtimeCol = numCol++;
  61. #if ! SEPARATE_READ_JOBS_COL
  62. _readJobsCol = _percentBarCol;
  63. #endif
  64. setColumnAlignment ( _totalSizeCol, AlignRight );
  65. setColumnAlignment ( _percentNumCol, AlignRight );
  66. setColumnAlignment ( _ownSizeCol, AlignRight );
  67. setColumnAlignment ( _totalItemsCol, AlignRight );
  68. setColumnAlignment ( _totalFilesCol, AlignRight );
  69. setColumnAlignment ( _totalSubDirsCol, AlignRight );
  70. setColumnAlignment ( _readJobsCol, AlignRight );
  71. setSorting( _totalSizeCol );
  72. #define loadIcon(ICON) TDEGlobal::iconLoader()->loadIcon( (ICON), TDEIcon::Small )
  73. _openDirIcon = loadIcon( "folder_open" );
  74. _closedDirIcon = loadIcon( "folder" );
  75. _openDotEntryIcon = loadIcon( "folder_orange_open");
  76. _closedDotEntryIcon = loadIcon( "folder_orange" );
  77. _unreadableDirIcon = loadIcon( "folder_locked" );
  78. _mountPointIcon = loadIcon( "drive-harddisk-mounted" );
  79. _fileIcon = loadIcon( "mime_empty" );
  80. _symLinkIcon = loadIcon( "symlink" ); // The KDE standard link icon is ugly!
  81. _blockDevIcon = loadIcon( "blockdevice" );
  82. _charDevIcon = loadIcon( "chardevice" );
  83. _fifoIcon = loadIcon( "socket" );
  84. _stopIcon = loadIcon( "process-stop" );
  85. _readyIcon = TQPixmap();
  86. #undef loadIcon
  87. setDefaultFillColors();
  88. readConfig();
  89. ensureContrast();
  90. connect( kapp, TQT_SIGNAL( tdedisplayPaletteChanged() ),
  91. this, TQT_SLOT ( paletteChanged() ) );
  92. connect( this, TQT_SIGNAL( selectionChanged ( TQListViewItem * ) ),
  93. this, TQT_SLOT ( selectItem ( TQListViewItem * ) ) );
  94. connect( this, TQT_SIGNAL( rightButtonPressed ( TQListViewItem *, const TQPoint &, int ) ),
  95. this, TQT_SLOT ( popupContextMenu ( TQListViewItem *, const TQPoint &, int ) ) );
  96. connect( header(), TQT_SIGNAL( sizeChange ( int, int, int ) ),
  97. this, TQT_SLOT ( columnResized( int, int, int ) ) );
  98. _contextInfo = new TQPopupMenu;
  99. _idContextInfo = _contextInfo->insertItem ( "dummy" );
  100. }
  101. KDirTreeView::~KDirTreeView()
  102. {
  103. if ( _tree )
  104. delete _tree;
  105. /*
  106. * Don't delete _updateTimer here, it's already automatically deleted by TQt!
  107. * (Since it's derived from TQObject and has a TQObject parent).
  108. */
  109. }
  110. void
  111. KDirTreeView::setDebugFunc( int i, const TQString & functionName )
  112. {
  113. if ( i > 0 && i < DEBUG_COUNTERS )
  114. _debugFunc[i] = functionName;
  115. }
  116. void
  117. KDirTreeView::incDebugCount( int i )
  118. {
  119. if ( i > 0 && i < DEBUG_COUNTERS )
  120. _debugCount[i]++;
  121. }
  122. void
  123. KDirTreeView::busyDisplay()
  124. {
  125. #if SEPARATE_READ_JOBS_COL
  126. if ( _readJobsCol < 0 )
  127. {
  128. _readJobsCol = header()->count();
  129. addColumn( i18n( "Read Jobs" ) );
  130. setColumnAlignment( _readJobsCol, AlignRight );
  131. }
  132. #else
  133. _readJobsCol = _percentBarCol;
  134. #endif
  135. }
  136. void
  137. KDirTreeView::idleDisplay()
  138. {
  139. #if SEPARATE_READ_JOBS_COL
  140. if ( _readJobsCol >= 0 )
  141. {
  142. removeColumn( _readJobsCol );
  143. }
  144. #else
  145. if ( _sortCol == _readJobsCol && _sortCol >= 0 )
  146. {
  147. // A pathological case: The user requested sorting by read jobs, and
  148. // now that everything is read, the items are still in that sort order.
  149. // Not only is that sort order now useless (since all read jobs are
  150. // done), it is contrary to the (now changed) semantics of this
  151. // column. Calling TQListView::sort() might do the trick, but we can
  152. // never know just how clever that TQListView widget tries to be and
  153. // maybe avoid another sorting by the same column - so let's use the
  154. // easy way out and sort by another column that has the same sorting
  155. // semantics like the percentage bar column (that had doubled as the
  156. // read job column while reading) now has.
  157. setSorting( _percentNumCol );
  158. }
  159. #endif
  160. _readJobsCol = -1;
  161. }
  162. void
  163. KDirTreeView::openURL( KURL url )
  164. {
  165. // Clean up any old leftovers
  166. clear();
  167. _currentDir = "";
  168. if ( _tree )
  169. delete _tree;
  170. // Create new (empty) dir tree
  171. _tree = new KDirTree();
  172. // Connect signals
  173. connect( _tree, TQT_SIGNAL( progressInfo ( const TQString & ) ),
  174. this, TQT_SLOT ( sendProgressInfo( const TQString & ) ) );
  175. connect( _tree, TQT_SIGNAL( childAdded( KFileInfo * ) ),
  176. this, TQT_SLOT ( addChild ( KFileInfo * ) ) );
  177. connect( _tree, TQT_SIGNAL( deletingChild( KFileInfo * ) ),
  178. this, TQT_SLOT ( deleteChild ( KFileInfo * ) ) );
  179. connect( _tree, TQT_SIGNAL( startingReading() ),
  180. this, TQT_SLOT ( prepareReading() ) );
  181. connect( _tree, TQT_SIGNAL( finished() ),
  182. this, TQT_SLOT ( slotFinished() ) );
  183. connect( _tree, TQT_SIGNAL( aborted() ),
  184. this, TQT_SLOT ( slotAborted() ) );
  185. connect( _tree, TQT_SIGNAL( finalizeLocal( KDirInfo * ) ),
  186. this, TQT_SLOT ( finalizeLocal( KDirInfo * ) ) );
  187. connect( this, TQT_SIGNAL( selectionChanged( KFileInfo * ) ),
  188. _tree, TQT_SLOT ( selectItem ( KFileInfo * ) ) );
  189. connect( _tree, TQT_SIGNAL( selectionChanged( KFileInfo * ) ),
  190. this, TQT_SLOT ( selectItem ( KFileInfo * ) ) );
  191. // Implicitly calling prepareReading() via the tree's startingReading() signal
  192. _tree->startReading( url );
  193. logActivity( 30 );
  194. }
  195. void
  196. KDirTreeView::prepareReading()
  197. {
  198. // Prepare cyclic update
  199. if ( _updateTimer )
  200. delete _updateTimer;
  201. _updateTimer = new TQTimer( this );
  202. if ( _updateTimer )
  203. {
  204. _updateTimer->changeInterval( _updateInterval );
  205. connect( _updateTimer, TQT_SIGNAL( timeout() ),
  206. this, TQT_SLOT ( updateSummary() ) );
  207. connect( _updateTimer, TQT_SIGNAL( timeout() ),
  208. this, TQT_SLOT ( sendProgressInfo() ) );
  209. }
  210. // Change display to busy state
  211. setSorting( _totalSizeCol );
  212. busyDisplay();
  213. emit startingReading();
  214. // Actually do something
  215. _stopWatch.start();
  216. }
  217. void
  218. KDirTreeView::refreshAll()
  219. {
  220. if ( _tree && _tree->root() )
  221. {
  222. clear();
  223. // Implicitly calling prepareReading() via the tree's startingReading() signal
  224. _tree->refresh( 0 );
  225. }
  226. }
  227. void
  228. KDirTreeView::refreshSelected()
  229. {
  230. if ( _tree && _tree->root() && _selection )
  231. {
  232. // Implicitly calling prepareReading() via the tree's startingReading() signal
  233. _tree->refresh( _selection->orig() );
  234. }
  235. logActivity( 10 );
  236. }
  237. void
  238. KDirTreeView::abortReading()
  239. {
  240. if ( _tree )
  241. _tree->abortReading();
  242. }
  243. void
  244. KDirTreeView::clear()
  245. {
  246. clearSelection();
  247. KDirTreeViewParentClass::clear();
  248. for ( int i=0; i < DEBUG_COUNTERS; i++ )
  249. _debugCount[i] = 0;
  250. }
  251. void
  252. KDirTreeView::addChild( KFileInfo *newChild )
  253. {
  254. if ( newChild->parent() )
  255. {
  256. KDirTreeViewItem *cloneParent = locate( newChild->parent(),
  257. _doLazyClone, // lazy
  258. true ); // doClone
  259. if ( cloneParent )
  260. {
  261. if ( isOpen( cloneParent ) || ! _doLazyClone )
  262. {
  263. // kdDebug() << "Immediately cloning " << newChild << endl;
  264. new KDirTreeViewItem( this, cloneParent, newChild );
  265. }
  266. }
  267. else // Error
  268. {
  269. if ( ! _doLazyClone )
  270. {
  271. kdError() << k_funcinfo << "Can't find parent view item for "
  272. << newChild << endl;
  273. }
  274. }
  275. }
  276. else // No parent - top level item
  277. {
  278. // kdDebug() << "Immediately top level cloning " << newChild << endl;
  279. new KDirTreeViewItem( this, newChild );
  280. }
  281. }
  282. void
  283. KDirTreeView::deleteChild( KFileInfo *child )
  284. {
  285. KDirTreeViewItem *clone = locate( child,
  286. false, // lazy
  287. false ); // doClone
  288. KDirTreeViewItem *nextSelection = 0;
  289. if ( clone )
  290. {
  291. if ( clone == _selection )
  292. {
  293. /**
  294. * The selected item is about to be deleted. Select some other item
  295. * so there is still something selected: Preferably the next item
  296. * or the parent if there is no next. This cannot be done from
  297. * outside because the order of items is not known to the outside;
  298. * it might appear very random if the next item in the KFileInfo
  299. * list would be selected. The order of that list is definitely
  300. * different than the order of this view - which is what the user
  301. * sees. So let's give the user a reasonable next selection so he
  302. * can continue working without having to explicitly select another
  303. * item.
  304. *
  305. * This is very useful if the user just activated a cleanup action
  306. * that deleted an item: It makes sense to implicitly select the
  307. * next item so he can clean up many items in a row.
  308. **/
  309. nextSelection = clone->next() ? clone->next() : clone->parent();
  310. // kdDebug() << k_funcinfo << " Next selection: " << nextSelection << endl;
  311. }
  312. KDirTreeViewItem *parent = clone->parent();
  313. delete clone;
  314. while ( parent )
  315. {
  316. parent->updateSummary();
  317. parent = parent->parent();
  318. }
  319. if ( nextSelection )
  320. selectItem( nextSelection );
  321. }
  322. }
  323. void
  324. KDirTreeView::updateSummary()
  325. {
  326. KDirTreeViewItem *child = firstChild();
  327. while ( child )
  328. {
  329. child->updateSummary();
  330. child = child->next();
  331. }
  332. }
  333. void
  334. KDirTreeView::slotFinished()
  335. {
  336. emit progressInfo( i18n( "Finished. Elapsed time: %1" )
  337. .arg( formatTime( _stopWatch.elapsed(), true ) ) );
  338. if ( _updateTimer )
  339. {
  340. delete _updateTimer;
  341. _updateTimer = 0;
  342. }
  343. idleDisplay();
  344. updateSummary();
  345. logActivity( 30 );
  346. #if 0
  347. for ( int i=0; i < DEBUG_COUNTERS; i++ )
  348. {
  349. kdDebug() << "Debug counter #" << i << ": " << _debugCount[i]
  350. << "\t" << _debugFunc[i]
  351. << endl;
  352. }
  353. kdDebug() << endl;
  354. #endif
  355. emit finished();
  356. }
  357. void
  358. KDirTreeView::slotAborted()
  359. {
  360. emit progressInfo( i18n( "Aborted. Elapsed time: %1" )
  361. .arg( formatTime( _stopWatch.elapsed(), true ) ) );
  362. if ( _updateTimer )
  363. {
  364. delete _updateTimer;
  365. _updateTimer = 0;
  366. }
  367. idleDisplay();
  368. updateSummary();
  369. emit aborted();
  370. }
  371. void
  372. KDirTreeView::finalizeLocal( KDirInfo *dir )
  373. {
  374. if ( dir )
  375. {
  376. KDirTreeViewItem *clone = locate( dir,
  377. false, // lazy
  378. false ); // doClone
  379. if ( clone )
  380. clone->finalizeLocal();
  381. }
  382. }
  383. void
  384. KDirTreeView::sendProgressInfo( const TQString & newCurrentDir )
  385. {
  386. _currentDir = newCurrentDir;
  387. #if VERBOSE_PROGRESS_INFO
  388. emit progressInfo( i18n( "Elapsed time: %1 reading directory %2" )
  389. .arg( formatTime( _stopWatch.elapsed() ) )
  390. .arg( _currentDir ) );
  391. #else
  392. emit progressInfo( i18n( "Elapsed time: %1" )
  393. .arg( formatTime( _stopWatch.elapsed() ) ) );
  394. #endif
  395. }
  396. KDirTreeViewItem *
  397. KDirTreeView::locate( KFileInfo *wanted, bool lazy, bool doClone )
  398. {
  399. KDirTreeViewItem *child = firstChild();
  400. while ( child )
  401. {
  402. KDirTreeViewItem *wantedChild = child->locate( wanted, lazy, doClone, 0 );
  403. if ( wantedChild )
  404. return wantedChild;
  405. else
  406. child = child->next();
  407. }
  408. return 0;
  409. }
  410. int
  411. KDirTreeView::openCount()
  412. {
  413. int count = 0;
  414. KDirTreeViewItem *child = firstChild();
  415. while ( child )
  416. {
  417. count += child->openCount();
  418. child = child->next();
  419. }
  420. return count;
  421. }
  422. void
  423. KDirTreeView::selectItem( TQListViewItem *listViewItem )
  424. {
  425. _selection = dynamic_cast<KDirTreeViewItem *>( listViewItem );
  426. if ( _selection )
  427. {
  428. // kdDebug() << k_funcinfo << " Selecting item " << _selection << endl;
  429. setSelected( _selection, true );
  430. }
  431. else
  432. {
  433. // kdDebug() << k_funcinfo << " Clearing selection" << endl;
  434. clearSelection();
  435. }
  436. emit selectionChanged( _selection );
  437. emit selectionChanged( _selection ? _selection->orig() : (KFileInfo *) 0 );
  438. }
  439. void
  440. KDirTreeView::selectItem( KFileInfo *newSelection )
  441. {
  442. // Short-circuit for the most common case: The signal has been triggered by
  443. // this view, and the KDirTree has sent it right back.
  444. if ( _selection && _selection->orig() == newSelection )
  445. return;
  446. if ( ! newSelection )
  447. clearSelection();
  448. else
  449. {
  450. _selection = locate( newSelection,
  451. false, // lazy
  452. true ); // doClone
  453. if ( _selection )
  454. {
  455. closeAllExcept( _selection );
  456. _selection->setOpen( false );
  457. ensureItemVisible( _selection );
  458. emit selectionChanged( _selection );
  459. setSelected( _selection, true );
  460. }
  461. else
  462. kdError() << "Couldn't clone item " << newSelection << endl;
  463. }
  464. }
  465. void
  466. KDirTreeView::clearSelection()
  467. {
  468. // kdDebug() << k_funcinfo << endl;
  469. _selection = 0;
  470. TQListView::clearSelection();
  471. emit selectionChanged( (KDirTreeViewItem *) 0 );
  472. emit selectionChanged( (KFileInfo *) 0 );
  473. }
  474. void
  475. KDirTreeView::closeAllExcept( KDirTreeViewItem *except )
  476. {
  477. if ( ! except )
  478. {
  479. kdError() << k_funcinfo << ": NULL pointer passed" << endl;
  480. return;
  481. }
  482. except->closeAllExceptThis();
  483. }
  484. const TQColor &
  485. KDirTreeView::fillColor( int level ) const
  486. {
  487. if ( level < 0 )
  488. {
  489. level = 0;
  490. kdWarning() << k_funcinfo << "Invalid argument: " << level << endl;
  491. }
  492. return _fillColor [ level % _usedFillColors ];
  493. }
  494. const TQColor &
  495. KDirTreeView::rawFillColor( int level ) const
  496. {
  497. if ( level < 0 || level > KDirTreeViewMaxFillColor )
  498. {
  499. level = 0;
  500. kdWarning() << k_funcinfo << "Invalid argument: " << level << endl;
  501. }
  502. return _fillColor [ level % KDirTreeViewMaxFillColor ];
  503. }
  504. void
  505. KDirTreeView::setFillColor( int level,
  506. const TQColor & color )
  507. {
  508. if ( level >= 0 && level < KDirTreeViewMaxFillColor )
  509. _fillColor[ level ] = color;
  510. }
  511. void
  512. KDirTreeView::setUsedFillColors( int usedFillColors )
  513. {
  514. if ( usedFillColors < 1 )
  515. {
  516. kdWarning() << k_funcinfo << "Invalid argument: "<< usedFillColors << endl;
  517. usedFillColors = 1;
  518. }
  519. else if ( usedFillColors >= KDirTreeViewMaxFillColor )
  520. {
  521. kdWarning() << k_funcinfo << "Invalid argument: "<< usedFillColors
  522. << " (max: " << KDirTreeViewMaxFillColor-1 << ")" << endl;
  523. usedFillColors = KDirTreeViewMaxFillColor-1;
  524. }
  525. _usedFillColors = usedFillColors;
  526. }
  527. void
  528. KDirTreeView::setDefaultFillColors()
  529. {
  530. int i;
  531. for ( i=0; i < KDirTreeViewMaxFillColor; i++ )
  532. {
  533. _fillColor[i] = blue;
  534. }
  535. i = 0;
  536. _usedFillColors = 4;
  537. setFillColor ( i++, TQColor ( 0, 0, 255 ) );
  538. setFillColor ( i++, TQColor ( 128, 0, 128 ) );
  539. setFillColor ( i++, TQColor ( 231, 147, 43 ) );
  540. setFillColor ( i++, TQColor ( 4, 113, 0 ) );
  541. setFillColor ( i++, TQColor ( 176, 0, 0 ) );
  542. setFillColor ( i++, TQColor ( 204, 187, 0 ) );
  543. setFillColor ( i++, TQColor ( 162, 98, 30 ) );
  544. setFillColor ( i++, TQColor ( 0, 148, 146 ) );
  545. setFillColor ( i++, TQColor ( 217, 94, 0 ) );
  546. setFillColor ( i++, TQColor ( 0, 194, 65 ) );
  547. setFillColor ( i++, TQColor ( 194, 108, 187 ) );
  548. setFillColor ( i++, TQColor ( 0, 179, 255 ) );
  549. }
  550. void
  551. KDirTreeView::setTreeBackground( const TQColor &color )
  552. {
  553. _treeBackground = color;
  554. _percentageBarBackground = _treeBackground.dark( 115 );
  555. TQPalette pal = kapp->palette();
  556. pal.setBrush( TQColorGroup::Base, _treeBackground );
  557. setPalette( pal );
  558. }
  559. void
  560. KDirTreeView::ensureContrast()
  561. {
  562. if ( colorGroup().base() == white ||
  563. colorGroup().base() == black )
  564. {
  565. setTreeBackground( colorGroup().midlight() );
  566. }
  567. else
  568. {
  569. setTreeBackground( colorGroup().base() );
  570. }
  571. }
  572. void
  573. KDirTreeView::paletteChanged()
  574. {
  575. setTreeBackground( TDEGlobalSettings::baseColor() );
  576. ensureContrast();
  577. }
  578. void
  579. KDirTreeView::popupContextMenu( TQListViewItem * listViewItem,
  580. const TQPoint & pos,
  581. int column )
  582. {
  583. KDirTreeViewItem *item = (KDirTreeViewItem *) listViewItem;
  584. if ( ! item )
  585. return;
  586. if ( column == _nameCol ||
  587. column == _percentBarCol ||
  588. column == _percentNumCol )
  589. {
  590. // Make the item the context menu is popping up over the current
  591. // selection - all user operations refer to the current selection.
  592. // Just right-clicking on an item does not make it the current
  593. // item!
  594. selectItem( item );
  595. // Let somebody from outside pop up the context menu, if so desired.
  596. emit contextMenu( item, pos );
  597. }
  598. // If the column is one with a large size in kB/MB/GB, open a
  599. // info popup with the exact number.
  600. if ( column == _ownSizeCol && ! item->orig()->isDotEntry() )
  601. {
  602. KFileInfo * orig = item->orig();
  603. if ( orig->isSparseFile() || ( orig->links() > 1 && orig->isFile() ) )
  604. {
  605. TQString text;
  606. if ( orig->isSparseFile() )
  607. {
  608. text = i18n( "Sparse file: %1 (%2 Bytes) -- allocated: %3 (%4 Bytes)" )
  609. .arg( formatSize( orig->byteSize() ) )
  610. .arg( formatSizeLong( orig->byteSize() ) )
  611. .arg( formatSize( orig->allocatedSize() ) )
  612. .arg( formatSizeLong( orig->allocatedSize() ) );
  613. }
  614. else
  615. {
  616. text = i18n( "%1 (%2 Bytes) with %3 hard links => effective size: %4 (%5 Bytes)" )
  617. .arg( formatSize( orig->byteSize() ) )
  618. .arg( formatSizeLong( orig->byteSize() ) )
  619. .arg( orig->links() )
  620. .arg( formatSize( orig->size() ) )
  621. .arg( formatSizeLong( orig->size() ) );
  622. }
  623. popupContextInfo( pos, text );
  624. }
  625. else
  626. {
  627. popupContextSizeInfo( pos, orig->size() );
  628. }
  629. }
  630. if ( column == _totalSizeCol &&
  631. ( item->orig()->isDir() || item->orig()->isDotEntry() ) )
  632. {
  633. popupContextSizeInfo( pos, item->orig()->totalSize() );
  634. }
  635. // Show alternate time / date format in time / date related columns.
  636. if ( column == _latestMtimeCol )
  637. {
  638. popupContextInfo( pos, formatTimeDate( item->orig()->latestMtime() ) );
  639. }
  640. logActivity( 3 );
  641. }
  642. void
  643. KDirTreeView::popupContextSizeInfo( const TQPoint & pos,
  644. KFileSize size )
  645. {
  646. TQString info;
  647. if ( size < 1024 )
  648. {
  649. info = formatSizeLong( size ) + " " + i18n( "Bytes" );
  650. }
  651. else
  652. {
  653. info = i18n( "%1 (%2 Bytes)" )
  654. .arg( formatSize( size ) )
  655. .arg( formatSizeLong( size ) );
  656. }
  657. popupContextInfo( pos, info );
  658. }
  659. void
  660. KDirTreeView::popupContextInfo( const TQPoint & pos,
  661. const TQString & info )
  662. {
  663. _contextInfo->changeItem( info, _idContextInfo );
  664. _contextInfo->popup( pos );
  665. }
  666. void
  667. KDirTreeView::readConfig()
  668. {
  669. TDEConfig *config = kapp->config();
  670. TDEConfigGroupSaver saver( config, "Tree Colors" );
  671. _usedFillColors = config->readNumEntry( "usedFillColors", -1 );
  672. if ( _usedFillColors < 0 )
  673. {
  674. /*
  675. * No 'usedFillColors' in the config file? Better forget that
  676. * file and use default values. Otherwise, all colors would very
  677. * likely become blue - the default color.
  678. */
  679. setDefaultFillColors();
  680. }
  681. else
  682. {
  683. // Read the rest of the 'Tree Colors' section
  684. TQColor defaultColor( blue );
  685. for ( int i=0; i < KDirTreeViewMaxFillColor; i++ )
  686. {
  687. TQString name;
  688. name.sprintf( "fillColor_%02d", i );
  689. _fillColor [i] = config->readColorEntry( name, &defaultColor );
  690. }
  691. }
  692. if ( isVisible() )
  693. triggerUpdate();
  694. }
  695. void
  696. KDirTreeView::saveConfig() const
  697. {
  698. TDEConfig *config = kapp->config();
  699. TDEConfigGroupSaver saver( config, "Tree Colors" );
  700. config->writeEntry( "usedFillColors", _usedFillColors );
  701. for ( int i=0; i < KDirTreeViewMaxFillColor; i++ )
  702. {
  703. TQString name;
  704. name.sprintf( "fillColor_%02d", i );
  705. config->writeEntry ( name, _fillColor [i] );
  706. }
  707. }
  708. void
  709. KDirTreeView::setSorting( int column, bool increasing )
  710. {
  711. _sortCol = column;
  712. TQListView::setSorting( column, increasing );
  713. }
  714. void
  715. KDirTreeView::logActivity( int points )
  716. {
  717. emit userActivity( points );
  718. }
  719. void
  720. KDirTreeView::columnResized( int column, int oldSize, int newSize )
  721. {
  722. NOT_USED( oldSize );
  723. NOT_USED( newSize );
  724. if ( column == _percentBarCol )
  725. triggerUpdate();
  726. }
  727. void
  728. KDirTreeView::sendMailToOwner()
  729. {
  730. if ( ! _selection )
  731. {
  732. kdError() << k_funcinfo << "Nothing selected!" << endl;
  733. return;
  734. }
  735. TQString owner = KAnyDirReadJob::owner( fixedUrl( _selection->orig()->url() ) );
  736. TQString subject = i18n( "Disk Usage" );
  737. TQString body =
  738. i18n("Please check your disk usage and clean up if you can. Thank you." )
  739. + "\n\n"
  740. + _selection->asciiDump()
  741. + "\n\n"
  742. + i18n( "Disk usage report generated by KDirStat" )
  743. + "\nhttp://kdirstat.sourceforge.net/";
  744. // kdDebug() << "owner: " << owner << endl;
  745. // kdDebug() << "subject: " << subject << endl;
  746. // kdDebug() << "body:\n" << body << endl;
  747. KURL mail;
  748. mail.setProtocol( "mailto" );
  749. mail.setPath( owner );
  750. mail.setQuery( "?subject=" + KURL::encode_string( subject ) +
  751. "&body=" + KURL::encode_string( body ) );
  752. // TODO: Check for maximum command line length.
  753. //
  754. // The hard part with this is how to get this from all that 'autoconf'
  755. // stuff into 'config.h' or some other include file without hardcoding
  756. // anything - this is too system dependent.
  757. kapp->invokeMailer( mail );
  758. logActivity( 10 );
  759. }
  760. KDirTreeViewItem::KDirTreeViewItem( KDirTreeView * view,
  761. KFileInfo * orig )
  762. : TQListViewItem( view )
  763. {
  764. init( view, 0, orig );
  765. }
  766. KDirTreeViewItem::KDirTreeViewItem( KDirTreeView * view,
  767. KDirTreeViewItem * parent,
  768. KFileInfo * orig )
  769. : TQListViewItem( parent )
  770. {
  771. TQ_CHECK_PTR( parent );
  772. init( view, parent, orig );
  773. }
  774. void
  775. KDirTreeViewItem::init( KDirTreeView * view,
  776. KDirTreeViewItem * parent,
  777. KFileInfo * orig )
  778. {
  779. _view = view;
  780. _parent = parent;
  781. _orig = orig;
  782. _percent = 0.0;
  783. _pacMan = 0;
  784. _openCount = 0;
  785. // _view->incDebugCount(1);
  786. // kdDebug() << "new KDirTreeViewItem for " << orig << endl;
  787. if ( _orig->isDotEntry() )
  788. {
  789. setText( view->nameCol(), i18n( "<Files>" ) );
  790. TQListViewItem::setOpen ( false );
  791. }
  792. else
  793. {
  794. setText( view->nameCol(), _orig->name() );
  795. if ( ! _orig->isDevice() )
  796. {
  797. TQString text;
  798. if ( _orig->isFile() && ( _orig->links() > 1 ) ) // Regular file with multiple links
  799. {
  800. if ( _orig->isSparseFile() )
  801. {
  802. text = i18n( "%1 / %2 Links (allocated: %3)" )
  803. .arg( formatSize( _orig->byteSize() ) )
  804. .arg( formatSize( _orig->links() ) )
  805. .arg( formatSize( _orig->allocatedSize() ) );
  806. }
  807. else
  808. {
  809. text = i18n( "%1 / %2 Links" )
  810. .arg( formatSize( _orig->byteSize() ) )
  811. .arg( _orig->links() );
  812. }
  813. }
  814. else // No multiple links or no regular file
  815. {
  816. if ( _orig->isSparseFile() )
  817. {
  818. text = i18n( "%1 (allocated: %2)" )
  819. .arg( formatSize( _orig->byteSize() ) )
  820. .arg( formatSize( _orig->allocatedSize() ) );
  821. }
  822. else
  823. {
  824. text = formatSize( _orig->size() );
  825. }
  826. }
  827. setText( view->ownSizeCol(), text );
  828. }
  829. TQListViewItem::setOpen ( _orig->treeLevel() < _view->openLevel() );
  830. /*
  831. * Don't use KDirTreeViewItem::setOpen() here since this might call
  832. * KDirTreeViewItem::deferredClone() which would confuse bookkeeping
  833. * with addChild() signals that might arrive, too - resulting in double
  834. * dot entries.
  835. */
  836. }
  837. if ( _view->doLazyClone() &&
  838. ( _orig->isDir() || _orig->isDotEntry() ) )
  839. {
  840. /*
  841. * Determine whether or not this item can be opened.
  842. *
  843. * Normally, TQt handles this very well, but when lazy cloning is in
  844. * effect, TQt cannot know whether or not there are children - they may
  845. * only be in the original tree until the user tries to open this
  846. * item. So let's assume there may be children as long as the directory
  847. * is still being read.
  848. */
  849. if ( _orig->readState() == KDirQueued ||
  850. _orig->readState() == KDirReading )
  851. {
  852. setExpandable( true );
  853. }
  854. else // KDirFinished, KDirError, KDirAborted
  855. {
  856. setExpandable( _orig->hasChildren() );
  857. }
  858. }
  859. if ( ! parent || parent->isOpen() )
  860. {
  861. setIcon();
  862. }
  863. _openCount = isOpen() ? 1 : 0;
  864. }
  865. KDirTreeViewItem::~KDirTreeViewItem()
  866. {
  867. if ( _pacMan )
  868. delete _pacMan;
  869. if ( this == _view->selection() )
  870. _view->clearSelection();
  871. }
  872. void
  873. KDirTreeViewItem::setIcon()
  874. {
  875. TQPixmap icon;
  876. if ( _orig->isDotEntry() )
  877. {
  878. icon = isOpen() ? _view->openDotEntryIcon() : _view->closedDotEntryIcon();
  879. }
  880. else if ( _orig->isDir() )
  881. {
  882. if ( _orig->readState() == KDirAborted ) icon = _view->stopIcon();
  883. else if ( _orig->readState() == KDirError )
  884. {
  885. icon = _view->unreadableDirIcon();
  886. setExpandable( false );
  887. }
  888. else
  889. {
  890. if ( _orig->isMountPoint() )
  891. {
  892. icon = _view->mountPointIcon();
  893. }
  894. else
  895. {
  896. icon = isOpen() ? _view->openDirIcon() : _view->closedDirIcon();
  897. }
  898. }
  899. }
  900. else if ( _orig->isFile() ) icon = _view->fileIcon();
  901. else if ( _orig->isSymLink() ) icon = _view->symLinkIcon();
  902. else if ( _orig->isBlockDevice() ) icon = _view->blockDevIcon();
  903. else if ( _orig->isCharDevice() ) icon = _view->charDevIcon();
  904. else if ( _orig->isSpecial() ) icon = _view->fifoIcon();
  905. setPixmap( _view->iconCol(), icon );
  906. }
  907. void
  908. KDirTreeViewItem::updateSummary()
  909. {
  910. // _view->incDebugCount(2);
  911. // Update this item
  912. setIcon();
  913. setText( _view->latestMtimeCol(), " " + localeTimeDate( _orig->latestMtime() ) );
  914. if ( _orig->isDir() || _orig->isDotEntry() )
  915. {
  916. TQString prefix = " ";
  917. if ( _orig->readState() == KDirAborted )
  918. prefix = " >";
  919. setText( _view->totalSizeCol(), prefix + formatSize( _orig->totalSize() ) );
  920. setText( _view->totalItemsCol(), prefix + formatCount( _orig->totalItems() ) );
  921. setText( _view->totalFilesCol(), prefix + formatCount( _orig->totalFiles() ) );
  922. if ( _view->readJobsCol() >= 0 )
  923. {
  924. #if SEPARATE_READ_JOBS_COL
  925. setText( _view->readJobsCol(), " " + formatCount( _orig->pendingReadJobs(), true ) );
  926. #else
  927. int jobs = _orig->pendingReadJobs();
  928. TQString text = "";
  929. if ( jobs > 0 )
  930. text = i18n( "[%1 Read Jobs]" ).arg( formatCount( _orig->pendingReadJobs(), true ) );
  931. setText( _view->readJobsCol(), text );
  932. #endif
  933. }
  934. }
  935. if ( _orig->isDir() )
  936. {
  937. setText( _view->totalSubDirsCol(), " " + formatCount( _orig->totalSubDirs() ) );
  938. }
  939. // Calculate and display percentage
  940. if ( _orig->parent() && // only if there is a parent as calculation base
  941. _orig->parent()->pendingReadJobs() < 1 && // not before subtree is finished reading
  942. _orig->parent()->totalSize() > 0 ) // avoid division by zero
  943. {
  944. _percent = ( 100.0 * _orig->totalSize() ) / (float) _orig->parent()->totalSize();
  945. setText( _view->percentNumCol(), formatPercent ( _percent ) );
  946. }
  947. else
  948. {
  949. _percent = 0.0;
  950. setText( _view->percentNumCol(), "" );
  951. }
  952. if ( _view->doPacManAnimation() && _orig->isBusy() )
  953. {
  954. if ( ! _pacMan )
  955. _pacMan = new KPacManAnimation( _view, height()-4, true );
  956. repaint();
  957. }
  958. if ( ! isOpen() ) // Lazy update: Nobody can see the children
  959. return; // -> don't update them.
  960. // Update all children
  961. KDirTreeViewItem *child = firstChild();
  962. while ( child )
  963. {
  964. child->updateSummary();
  965. child = child->next();
  966. }
  967. }
  968. KDirTreeViewItem *
  969. KDirTreeViewItem::locate( KFileInfo * wanted,
  970. bool lazy,
  971. bool doClone,
  972. int level )
  973. {
  974. if ( lazy && ! isOpen() )
  975. {
  976. /*
  977. * In "lazy" mode, we don't bother searching all the children of this
  978. * item if they are not visible (i.e. the branch is open) anyway. In
  979. * this case, cloning that branch is deferred until the branch is
  980. * actually opened - which in most cases will never happen anyway (most
  981. * users don't manually open each and every subtree). If and when it
  982. * happens, we'll probably be fast enough bringing the view tree in
  983. * sync with the original tree since opening a branch requires manual
  984. * interaction which is a whole lot slower than copying a couple of
  985. * objects.
  986. *
  987. * Note that this mode is _independent_ of lazy cloning in general: The
  988. * caller explicitly specifies if he wants to locate an item at all
  989. * cost, even if that means deferred cloning children whose creation
  990. * has been delayed until now.
  991. */
  992. // kdDebug() << "Too lazy to search for " << wanted << " from " << this << endl;
  993. return 0;
  994. }
  995. if ( _orig == wanted )
  996. {
  997. return this;
  998. }
  999. if ( level < 0 )
  1000. level = _orig->treeLevel();
  1001. if ( wanted->urlPart( level ) == _orig->name() )
  1002. {
  1003. // Search all children
  1004. KDirTreeViewItem *child = firstChild();
  1005. if ( ! child && _orig->hasChildren() && doClone )
  1006. {
  1007. // kdDebug() << "Deferred cloning " << this << " for children search of " << wanted << endl;
  1008. deferredClone();
  1009. child = firstChild();
  1010. }
  1011. while ( child )
  1012. {
  1013. KDirTreeViewItem *foundChild = child->locate( wanted, lazy, doClone, level+1 );
  1014. if ( foundChild )
  1015. return foundChild;
  1016. else
  1017. child = child->next();
  1018. }
  1019. }
  1020. return 0;
  1021. }
  1022. void
  1023. KDirTreeViewItem::deferredClone()
  1024. {
  1025. // _view->incDebugCount(3);
  1026. if ( ! _orig->hasChildren() )
  1027. {
  1028. // kdDebug() << k_funcinfo << "Oops, no children - sorry for bothering you!" << endl;
  1029. setExpandable( false );
  1030. return;
  1031. }
  1032. // Clone all normal children
  1033. int level = _orig->treeLevel();
  1034. bool startingClean = ! firstChild();
  1035. KFileInfo *origChild = _orig->firstChild();
  1036. while ( origChild )
  1037. {
  1038. if ( startingClean ||
  1039. ! locate( origChild,
  1040. false, // lazy
  1041. true, // doClone
  1042. level ) )
  1043. {
  1044. // kdDebug() << "Deferred cloning " << origChild << endl;
  1045. new KDirTreeViewItem( _view, this, origChild );
  1046. }
  1047. origChild = origChild->next();
  1048. }
  1049. // Clone the dot entry
  1050. if ( _orig->dotEntry() &&
  1051. ( startingClean ||
  1052. ! locate( _orig->dotEntry(),
  1053. false, // lazy
  1054. true, // doClone
  1055. level )
  1056. )
  1057. )
  1058. {
  1059. // kdDebug() << "Deferred cloning dot entry for " << _orig << endl;
  1060. new KDirTreeViewItem( _view, this, _orig->dotEntry() );
  1061. }
  1062. }
  1063. void
  1064. KDirTreeViewItem::finalizeLocal()
  1065. {
  1066. // kdDebug() << k_funcinfo << _orig << endl;
  1067. cleanupDotEntries();
  1068. if ( _orig->totalItems() == 0 )
  1069. // _orig->hasChildren() would give a wrong answer here since it counts
  1070. // the dot entry, too - which might be removed a moment later.
  1071. {
  1072. setExpandable( false );
  1073. }
  1074. }
  1075. void
  1076. KDirTreeViewItem::cleanupDotEntries()
  1077. {
  1078. if ( ! _orig->dotEntry() )
  1079. return;
  1080. KDirTreeViewItem *dotEntry = findDotEntry();
  1081. if ( ! dotEntry )
  1082. return;
  1083. // Reparent dot entry children if there are no subdirectories on this level
  1084. if ( ! _orig->firstChild() )
  1085. {
  1086. // kdDebug() << "Removing solo dot entry clone " << _orig << endl;
  1087. KDirTreeViewItem *child = dotEntry->firstChild();
  1088. while ( child )
  1089. {
  1090. KDirTreeViewItem *nextChild = child->next();
  1091. // Reparent this child
  1092. // kdDebug() << "Reparenting clone " << child << endl;
  1093. dotEntry->removeItem( child );
  1094. insertItem( child );
  1095. child = nextChild;
  1096. }
  1097. /*
  1098. * Immediately delete the (now emptied) dot entry. The algorithm for
  1099. * the original tree doesn't quite fit here - there, the dot entry is
  1100. * actually deleted in the step below. But the 'no children' check for
  1101. * this fails here since the original dot entry still _has_ its
  1102. * children - they will be deleted only after all clones have been
  1103. * processed.
  1104. *
  1105. * This had been the cause for a core that took me quite some time to
  1106. * track down.
  1107. */
  1108. delete dotEntry;
  1109. dotEntry = 0;
  1110. }
  1111. // Delete dot entries without any children
  1112. if ( ! _orig->dotEntry()->firstChild() && dotEntry )
  1113. {
  1114. // kdDebug() << "Removing empty dot entry clone " << _orig << endl;
  1115. delete dotEntry;
  1116. }
  1117. }
  1118. KDirTreeViewItem *
  1119. KDirTreeViewItem::findDotEntry() const
  1120. {
  1121. KDirTreeViewItem *child = firstChild();
  1122. while ( child )
  1123. {
  1124. if ( child->orig()->isDotEntry() )
  1125. return child;
  1126. child = child->next();
  1127. }
  1128. return 0;
  1129. }
  1130. void
  1131. KDirTreeViewItem::setOpen( bool open )
  1132. {
  1133. if ( open && _view->doLazyClone() )
  1134. {
  1135. // kdDebug() << "Opening " << this << endl;
  1136. deferredClone();
  1137. }
  1138. if ( isOpen() != open )
  1139. {
  1140. openNotify( open );
  1141. }
  1142. TQListViewItem::setOpen( open );
  1143. setIcon();
  1144. if ( open )
  1145. updateSummary();
  1146. // kdDebug() << _openCount << " open in " << this << endl;
  1147. // _view->logActivity( 1 );
  1148. }
  1149. void
  1150. KDirTreeViewItem::openNotify( bool open )
  1151. {
  1152. if ( open )
  1153. _openCount++;
  1154. else
  1155. _openCount--;
  1156. if ( _parent )
  1157. _parent->openNotify( open );
  1158. }
  1159. void
  1160. KDirTreeViewItem::openSubtree()
  1161. {
  1162. if ( parent() )
  1163. parent()->setOpen( true );
  1164. setOpen( true );
  1165. }
  1166. void
  1167. KDirTreeViewItem::closeSubtree()
  1168. {
  1169. setOpen( false );
  1170. if ( _openCount > 0 )
  1171. {
  1172. KDirTreeViewItem * child = firstChild();
  1173. while ( child )
  1174. {
  1175. child->closeSubtree();
  1176. child = child->next();
  1177. }
  1178. }
  1179. _openCount = 0; // just to be sure
  1180. }
  1181. void
  1182. KDirTreeViewItem::closeAllExceptThis()
  1183. {
  1184. KDirTreeViewItem *sibling = _parent ?
  1185. _parent->firstChild() : _view->firstChild();
  1186. while ( sibling )
  1187. {
  1188. if ( sibling != this )
  1189. sibling->closeSubtree(); // Recurse down
  1190. sibling = sibling->next();
  1191. }
  1192. setOpen( true );
  1193. if ( _parent )
  1194. _parent->closeAllExceptThis(); // Recurse up
  1195. }
  1196. TQString
  1197. KDirTreeViewItem::asciiDump()
  1198. {
  1199. TQString dump;
  1200. dump = TQString("%1 %2\n")
  1201. .arg(formatSize(_orig->totalSize()), 10)
  1202. .arg(_orig->debugUrl().local8Bit());
  1203. if ( isOpen() )
  1204. {
  1205. KDirTreeViewItem *child = firstChild();
  1206. while ( child )
  1207. {
  1208. dump += child->asciiDump();
  1209. child = child->next();
  1210. }
  1211. }
  1212. return dump;
  1213. }
  1214. /**
  1215. * Comparison function used for sorting the list.
  1216. * Returns:
  1217. * -1 if this < other
  1218. * 0 if this == other
  1219. * +1 if this > other
  1220. **/
  1221. int
  1222. KDirTreeViewItem::compare( TQListViewItem * otherListViewItem,
  1223. int column,
  1224. bool ascending ) const
  1225. {
  1226. // _view->incDebugCount(4);
  1227. KDirTreeViewItem * other = dynamic_cast<KDirTreeViewItem *> (otherListViewItem);
  1228. if ( other )
  1229. {
  1230. KFileInfo * otherOrig = other->orig();
  1231. #if ! SEPARATE_READ_JOBS_COL
  1232. if ( column == _view->readJobsCol() ) return - compare( _orig->pendingReadJobs(), otherOrig->pendingReadJobs() );
  1233. else
  1234. #endif
  1235. if ( column == _view->totalSizeCol() ||
  1236. column == _view->percentNumCol() ||
  1237. column == _view->percentBarCol() ) return - compare( _orig->totalSize(), otherOrig->totalSize() );
  1238. else if ( column == _view->ownSizeCol() ) return - compare( _orig->size(), otherOrig->size() );
  1239. else if ( column == _view->totalItemsCol() ) return - compare( _orig->totalItems(), otherOrig->totalItems() );
  1240. else if ( column == _view->totalFilesCol() ) return - compare( _orig->totalFiles(), otherOrig->totalFiles() );
  1241. else if ( column == _view->totalSubDirsCol() ) return - compare( _orig->totalSubDirs(), otherOrig->totalSubDirs() );
  1242. else if ( column == _view->latestMtimeCol() ) return - compare( _orig->latestMtime(), otherOrig->latestMtime() );
  1243. else
  1244. {
  1245. if ( _orig->isDotEntry() ) // make sure dot entries are last in the list
  1246. return 1;
  1247. if ( otherOrig->isDotEntry() )
  1248. return -1;
  1249. }
  1250. }
  1251. return TQListViewItem::compare( otherListViewItem, column, ascending );
  1252. }
  1253. void
  1254. KDirTreeViewItem::paintCell( TQPainter * painter,
  1255. const TQColorGroup & colorGroup,
  1256. int column,
  1257. int width,
  1258. int alignment )
  1259. {
  1260. // _view->incDebugCount(5);
  1261. if ( column == _view->percentBarCol() )
  1262. {
  1263. painter->setBackgroundColor( colorGroup.base() );
  1264. if ( _percent > 0.0 )
  1265. {
  1266. if ( _pacMan )
  1267. {
  1268. delete _pacMan;
  1269. _pacMan = 0;
  1270. }
  1271. int level = _orig->treeLevel();
  1272. paintPercentageBar ( _percent,
  1273. painter,
  1274. _view->treeStepSize() * ( level-1 ),
  1275. width,
  1276. _view->fillColor( level-1 ),
  1277. _view->percentageBarBackground() );
  1278. }
  1279. else
  1280. {
  1281. if ( _pacMan && _orig->isBusy() )
  1282. {
  1283. // kdDebug() << "Animating PacMan for " << _orig << endl;
  1284. // painter->setBackgroundColor( _view->treeBackground() );
  1285. _pacMan->animate( painter, TQRect( 0, 0, width, height() ) );
  1286. }
  1287. else
  1288. {
  1289. if ( _view->percentBarCol() == _view->readJobsCol()
  1290. && ! _pacMan )
  1291. {
  1292. TQListViewItem::paintCell( painter,
  1293. colorGroup,
  1294. column,
  1295. width,
  1296. alignment );
  1297. }
  1298. else
  1299. {
  1300. painter->eraseRect( 0, 0, width, height() );
  1301. }
  1302. }
  1303. }
  1304. }
  1305. else
  1306. {
  1307. /*
  1308. * Call the parent's paintCell() method. We don't want to do
  1309. * all the hassle of drawing strings and pixmaps, regarding
  1310. * alignments etc.
  1311. */
  1312. TQListViewItem::paintCell( painter,
  1313. colorGroup,
  1314. column,
  1315. width,
  1316. alignment );
  1317. }
  1318. }
  1319. void
  1320. KDirTreeViewItem::paintPercentageBar( float percent,
  1321. TQPainter * painter,
  1322. int indent,
  1323. int width,
  1324. const TQColor & fillColor,
  1325. const TQColor & barBackground )
  1326. {
  1327. int penWidth = 2;
  1328. int extraMargin = 3;
  1329. int x = _view->itemMargin();
  1330. int y = extraMargin;
  1331. int w = width - 2 * _view->itemMargin();
  1332. int h = height() - 2 * extraMargin;
  1333. int fillWidth;
  1334. painter->eraseRect( 0, 0, width, height() );
  1335. w -= indent;
  1336. x += indent;
  1337. if ( w > 0 )
  1338. {
  1339. TQPen pen( painter->pen() );
  1340. pen.setWidth( 0 );
  1341. painter->setPen( pen );
  1342. painter->setBrush( NoBrush );
  1343. fillWidth = (int) ( ( w - 2 * penWidth ) * percent / 100.0);
  1344. // Fill bar background.
  1345. painter->fillRect( x + penWidth, y + penWidth,
  1346. w - 2 * penWidth + 1, h - 2 * penWidth + 1,
  1347. barBackground );
  1348. /*
  1349. * Notice: The Xlib XDrawRectangle() function always fills one
  1350. * pixel less than specified. Altough this is very likely just a
  1351. * plain old bug, it is documented that way. Obviously, TQt just
  1352. * maps the fillRect() call directly to XDrawRectangle() so they
  1353. * inherited that bug (although the TQt doc stays silent about
  1354. * it). So it is really necessary to compensate for that missing
  1355. * pixel in each dimension.
  1356. *
  1357. * If you don't believe it, see for yourself.
  1358. * Hint: Try the xmag program to zoom into the drawn pixels.
  1359. **/
  1360. // Fill the desired percentage.
  1361. painter->fillRect( x + penWidth, y + penWidth,
  1362. fillWidth+1, h - 2 * penWidth+1,
  1363. fillColor );
  1364. // Draw 3D shadows.
  1365. pen.setColor( contrastingColor ( TQt::black,
  1366. painter->backgroundColor() ) );
  1367. painter->setPen( pen );
  1368. painter->drawLine( x, y, x+w, y );
  1369. painter->drawLine( x, y, x, y+h );
  1370. pen.setColor( contrastingColor( barBackground.dark(),
  1371. painter->backgroundColor() ) );
  1372. painter->setPen( pen );
  1373. painter->drawLine( x+1, y+1, x+w-1, y+1 );
  1374. painter->drawLine( x+1, y+1, x+1, y+h-1 );
  1375. pen.setColor( contrastingColor( barBackground.light(),
  1376. painter->backgroundColor() ) );
  1377. painter->setPen( pen );
  1378. painter->drawLine( x+1, y+h, x+w, y+h );
  1379. painter->drawLine( x+w, y, x+w, y+h );
  1380. pen.setColor( contrastingColor( TQt::white,
  1381. painter->backgroundColor() ) );
  1382. painter->setPen( pen );
  1383. painter->drawLine( x+2, y+h-1, x+w-1, y+h-1 );
  1384. painter->drawLine( x+w-1, y+1, x+w-1, y+h-1 );
  1385. }
  1386. }
  1387. TQString
  1388. KDirStat::formatSizeLong( KFileSize size )
  1389. {
  1390. TQString sizeText;
  1391. int count = 0;
  1392. while ( size > 0 )
  1393. {
  1394. sizeText = ( ( size % 10 ) + '0' ) + sizeText;
  1395. size /= 10;
  1396. if ( ++count == 3 && size > 0 )
  1397. {
  1398. sizeText = TDEGlobal::locale()->thousandsSeparator() + sizeText;
  1399. count = 0;
  1400. }
  1401. }
  1402. return sizeText;
  1403. }
  1404. TQString
  1405. KDirStat::hexKey( KFileSize size )
  1406. {
  1407. /**
  1408. * This is optimized for performance, not for aesthetics.
  1409. * And every now and then the old C hacker breaks through in most of us...
  1410. * ;-)
  1411. **/
  1412. static const char hexDigits[] = "0123456789ABCDEF";
  1413. char key[ sizeof( KFileSize ) * 2 + 1 ]; // 2 hex digits per byte required
  1414. char *cptr = key + sizeof( key ) - 1; // now points to last char of key
  1415. memset( key, '0', sizeof( key ) - 1 ); // fill with zeroes
  1416. *cptr-- = 0; // terminate string
  1417. while ( size > 0 )
  1418. {
  1419. *cptr-- = hexDigits[ size & 0xF ]; // same as size % 16
  1420. size >>= 4; // same as size /= 16
  1421. }
  1422. return TQString( key );
  1423. }
  1424. TQString
  1425. KDirStat::formatTime( long millisec, bool showMilliSeconds )
  1426. {
  1427. TQString formattedTime;
  1428. int hours;
  1429. int min;
  1430. int sec;
  1431. hours = millisec / 3600000L; // 60*60*1000
  1432. millisec %= 3600000L;
  1433. min = millisec / 60000L; // 60*1000
  1434. millisec %= 60000L;
  1435. sec = millisec / 1000L;
  1436. millisec %= 1000L;
  1437. if ( showMilliSeconds )
  1438. {
  1439. formattedTime.sprintf ( "%02d:%02d:%02d.%03ld",
  1440. hours, min, sec, millisec );
  1441. }
  1442. else
  1443. {
  1444. formattedTime.sprintf ( "%02d:%02d:%02d", hours, min, sec );
  1445. }
  1446. return formattedTime;
  1447. }
  1448. TQString
  1449. KDirStat::formatCount( int count, bool suppressZero )
  1450. {
  1451. if ( suppressZero && count == 0 )
  1452. return "";
  1453. TQString countString;
  1454. countString.setNum( count );
  1455. return countString;
  1456. }
  1457. TQString
  1458. KDirStat::formatPercent( float percent )
  1459. {
  1460. TQString percentString;
  1461. percentString.sprintf( "%.1f%%", percent );
  1462. return percentString;
  1463. }
  1464. TQString
  1465. KDirStat::formatTimeDate( time_t rawTime )
  1466. {
  1467. TQString timeDateString;
  1468. struct tm *t = localtime( &rawTime );
  1469. /*
  1470. * Format this as "yyyy-mm-dd hh:mm:ss".
  1471. *
  1472. * This format may not be POSIX'ly correct, but it is the ONLY of all those
  1473. * brain-dead formats today's computer users are confronted with that makes
  1474. * any sense to the average human.
  1475. *
  1476. * Agreed, it takes some getting used to, too, but once you got that far,
  1477. * you won't want to miss it.
  1478. *
  1479. * Who the hell came up with those weird formats like described in the
  1480. * ctime() man page? Don't those people ever actually use that?
  1481. *
  1482. * What sense makes a format like "Wed Jun 30 21:49:08 1993" ?
  1483. * The weekday (of all things!) first, then a partial month name, then the
  1484. * day of month, then the time and then - at the very end - the year.
  1485. * IMHO this is maximum brain-dead. Not only can't you do any kind of
  1486. * decent sorting or automatic processing with that disinformation
  1487. * hodge-podge, your brain runs in circles trying to make sense of it.
  1488. *
  1489. * I could put up with crap like that if the Americans and Brits like it
  1490. * that way, but unfortunately I as a German am confronted with that
  1491. * bullshit, too, on a daily basis - either because some localization stuff
  1492. * didn't work out right (again) or because some jerk decided to emulate
  1493. * this stuff in the German translation, too. I am sick and tired with
  1494. * that, and since this is MY program I am going to use a format that makes
  1495. * sense to ME.
  1496. *
  1497. * No, no exceptions for Americans or Brits. I had to put up with their
  1498. * crap long enough, now it's time for them to put up with mine.
  1499. * Payback time - though luck, folks.
  1500. * ;-)
  1501. *
  1502. * Stefan Hundhammer <sh@suse.de> 2001-05-28
  1503. * (in quite some fit of frustration)
  1504. */
  1505. timeDateString.sprintf( "%4d-%02d-%02d %02d:%02d:%02d",
  1506. t->tm_year + 1900,
  1507. t->tm_mon + 1, // another brain-dead common pitfall - 0..11
  1508. t->tm_mday,
  1509. t->tm_hour, t->tm_min, t->tm_sec );
  1510. return timeDateString;
  1511. }
  1512. TQString
  1513. KDirStat::localeTimeDate( time_t rawTime )
  1514. {
  1515. TQDateTime timeDate;
  1516. timeDate.setTime_t( rawTime );
  1517. TQString timeDateString =
  1518. TDEGlobal::locale()->formatDate( timeDate.date(), true ) + " " + // short format
  1519. TDEGlobal::locale()->formatTime( timeDate.time(), true ); // include seconds
  1520. return timeDateString;
  1521. }
  1522. TQColor
  1523. KDirStat::contrastingColor( const TQColor &desiredColor,
  1524. const TQColor &contrastColor )
  1525. {
  1526. if ( desiredColor != contrastColor )
  1527. {
  1528. return desiredColor;
  1529. }
  1530. if ( contrastColor != contrastColor.light() )
  1531. {
  1532. // try a little lighter
  1533. return contrastColor.light();
  1534. }
  1535. else
  1536. {
  1537. // try a little darker
  1538. return contrastColor.dark();
  1539. }
  1540. }
  1541. #include "kdirtreeview.moc"
  1542. // EOF