1 /*
2 * File name: LocateFileTypeWindow.cpp
3 * Summary: QDirStat "locate files by type" window
4 * License: GPL V2 - See file LICENSE for details.
5 *
6 * Author: Stefan Hundhammer <Stefan.Hundhammer@gmx.de>
7 */
8
9
10 #include <algorithm>
11
12 #include "LocateFileTypeWindow.h"
13 #include "QDirStatApp.h" // SelectionModel
14 #include "DirTree.h"
15 #include "DotEntry.h"
16 #include "SelectionModel.h"
17 #include "SettingsHelpers.h"
18 #include "HeaderTweaker.h"
19 #include "FormatUtil.h"
20 #include "Logger.h"
21 #include "Exception.h"
22
23 using namespace QDirStat;
24
25
26 LocateFileTypeWindow::LocateFileTypeWindow( QWidget * parent ):
27 QDialog( parent ),
28 _ui( new Ui::LocateFileTypeWindow )
xdebug_superglobals_dump_dtor(void * user,void * ptr)29 {
30 // logDebug() << "init" << endl;
31
32 CHECK_NEW( _ui );
33 _ui->setupUi( this );
34 initWidgets();
35 readWindowSettings( this, "LocateFileTypeWindow" );
36
37 connect( _ui->refreshButton, SIGNAL( clicked() ),
38 this, SLOT ( refresh() ) );
39
40 connect( _ui->treeWidget, SIGNAL( currentItemChanged( QTreeWidgetItem *,
41 QTreeWidgetItem * ) ),
42 this, SLOT ( selectResult ( QTreeWidgetItem * ) ) );
43 }
44
45
46 LocateFileTypeWindow::~LocateFileTypeWindow()
47 {
48 // logDebug() << "destroying" << endl;
49 writeWindowSettings( this, "LocateFileTypeWindow" );
50 delete _ui;
51 }
52
53
54 void LocateFileTypeWindow::clear()
55 {
56 _searchSuffix.clear();
57 _ui->treeWidget->clear();
58 }
59
60
61 void LocateFileTypeWindow::refresh()
62 {
63 populate( searchSuffix(), _subtree() );
64 }
65
66
67 void LocateFileTypeWindow::initWidgets()
68 {
69 QFont font = _ui->heading->font();
70 font.setBold( true );
71 _ui->heading->setFont( font );
72
73 _ui->treeWidget->setColumnCount( SSR_ColumnCount );
74 _ui->treeWidget->setHeaderLabels( QStringList()
75 << tr( "Number" )
76 << tr( "Total Size" )
77 << tr( "Directory" ) );
78 _ui->treeWidget->header()->setStretchLastSection( false );
79 HeaderTweaker::resizeToContents( _ui->treeWidget->header() );
80 }
dump_hash_elem_va(zval * pDest,zend_ulong index_key,zend_string * hash_key,const char * name,int html,xdebug_str * str)81
82
83 void LocateFileTypeWindow::reject()
84 {
85 deleteLater();
86 }
87
88
89 QString LocateFileTypeWindow::searchSuffix() const
90 {
91 return QString( "*" ) + _searchSuffix;
dump_hash(xdebug_llist * l,const char * name,int name_len,int html,xdebug_str * str)92 }
93
94
95 void LocateFileTypeWindow::populate( const QString & suffix, FileInfo * newSubtree )
96 {
97 clear();
98
99 _searchSuffix = suffix;
100 _subtree = newSubtree;
101
102 if ( _searchSuffix.startsWith( '*' ) )
103 _searchSuffix.remove( 0, 1 ); // Remove the leading '*'
104
105 if ( ! _searchSuffix.startsWith( '.' ) )
106 _searchSuffix.prepend( '.' );
107
108 _ui->heading->setText( tr( "Directories with %1 Files below %2" )
109 .arg( searchSuffix() )
110 .arg( _subtree.url() ) );
111
112 logDebug() << "Locating all files ending with \""
113 << _searchSuffix << "\" below " << _subtree.url() << endl;
114
115 // For better Performance: Disable sorting while inserting many items
116 _ui->treeWidget->setSortingEnabled( false );
117
118 populateRecursive( newSubtree ? newSubtree : _subtree() );
119
120 _ui->treeWidget->setSortingEnabled( true );
121 _ui->treeWidget->sortByColumn( SSR_PathCol, Qt::AscendingOrder );
122 logDebug() << _ui->treeWidget->topLevelItemCount() << " directories" << endl;
123
124 // Make sure something is selected, even if this window is not the active
125 // one (for example because the user just clicked on another suffix in the
126 // file type stats window). When the window is activated, the tree widget
127 // automatically uses the topmost item as the current item, and in the
128 // default selection mode, this item is also selected. When the window is
129 // not active, this does not happen yet - until the window is activated.
130 //
131 // In the context of QDirStat, this means that this is also signaled to the
132 // SelectionModel, the corresponding branch in the main window's dir tree
133 // is opened, and the matching files are selected in the dir tree and in
134 // the treemap.
135 //
136 // It is very irritating if this only happens sometimes - when the "locate
137 // files" window is created, but not when it is just populated with new
138 // content from the outside (from the file type stats window).
139 //
140 // So let's make sure the topmost item is always selected.
141
142 _ui->treeWidget->setCurrentItem( _ui->treeWidget->topLevelItem( 0 ) );
143 }
144
145
146 void LocateFileTypeWindow::populateRecursive( FileInfo * dir )
xdebug_get_printable_superglobals(int html)147 {
148 if ( ! dir )
149 return;
150
151 FileInfoSet matches = matchingFiles( dir );
152
153 if ( ! matches.isEmpty() )
154 {
155 // Create a search result for this path
156
157 FileSize totalSize = 0LL;
158
159 foreach ( FileInfo * file, matches )
160 totalSize += file->size();
161
162 SuffixSearchResultItem * searchResultItem =
163 new SuffixSearchResultItem( dir->url(), matches.size(), totalSize );
164 CHECK_NEW( searchResultItem );
165
166 _ui->treeWidget->addTopLevelItem( searchResultItem );
167 }
168
169
170 // Recurse through any subdirectories
171
172 FileInfo * child = dir->firstChild();
173
174 while ( child )
175 {
176 if ( child->isDir() )
177 populateRecursive( child );
178
179 child = child->next();
180 }
181
182 // Notice that unlike in FileTypeStats, there is no need to recurse through
183 // any dot entries: They are handled in matchingFiles() already.
184 }
185
186
187 FileInfoSet LocateFileTypeWindow::matchingFiles( FileInfo * item )
188 {
PHP_FUNCTION(xdebug_dump_superglobals)189 FileInfoSet result;
190
191 if ( ! item || ! item->isDirInfo() )
192 return result;
193
194 DirInfo * dir = item->toDirInfo();
195
196 if ( dir->dotEntry() )
197 dir = dir->dotEntry();
198
199 FileInfo * child = dir->firstChild();
200
201 while ( child )
202 {
203 if ( child->isFile() &&
204 child->name().endsWith( _searchSuffix, Qt::CaseInsensitive ) )
205 {
206 result << child;
207 }
208
209 child = child->next();
210 }
211
212 return result;
213 }
214
215
216 void LocateFileTypeWindow::selectResult( QTreeWidgetItem * item )
217 {
218 if ( ! item )
219 return;
220
221 SuffixSearchResultItem * searchResult =
222 dynamic_cast<SuffixSearchResultItem *>( item );
223 CHECK_DYNAMIC_CAST( searchResult, "SuffixSearchResultItem" );
224 CHECK_PTR( _subtree.tree() );
225
226 FileInfo * dir = _subtree.tree()->locate( searchResult->path() );
227 FileInfoSet matches = matchingFiles( dir );
228
229 // logDebug() << "Selecting " << searchResult->path() << " with " << matches.size() << " matches" << endl;
230
231 if ( ! matches.isEmpty() )
232 app()->selectionModel()->setCurrentItem( matches.first(), true );
233
234 app()->selectionModel()->setSelectedItems( matches );
235 }
236
237
238
239
240
241
242 SuffixSearchResultItem::SuffixSearchResultItem( const QString & path,
243 int count,
244 FileSize totalSize ):
245 QTreeWidgetItem( QTreeWidgetItem::UserType ),
246 _path( path ),
247 _count( count ),
248 _totalSize( totalSize )
249 {
250 setText( SSR_CountCol, QString( "%1" ).arg( count ) );
251 setText( SSR_TotalSizeCol, formatSize( totalSize ) );
252 setText( SSR_PathCol, path );
253
254 setTextAlignment( SSR_CountCol, Qt::AlignRight );
255 setTextAlignment( SSR_TotalSizeCol, Qt::AlignRight );
256 setTextAlignment( SSR_PathCol, Qt::AlignLeft );
257 }
258
259
260 bool SuffixSearchResultItem::operator<( const QTreeWidgetItem & rawOther ) const
261 {
262 // Since this is a reference, the dynamic_cast will throw a std::bad_cast
263 // exception if it fails. Not catching this here since this is a genuine
264 // error which should not be silently ignored.
265 const SuffixSearchResultItem & other = dynamic_cast<const SuffixSearchResultItem &>( rawOther );
266
267 int col = treeWidget() ? treeWidget()->sortColumn() : SSR_PathCol;
268
269 switch ( col )
270 {
271 case SSR_PathCol: return path() < other.path();
272 case SSR_CountCol: return count() < other.count();
273 case SSR_TotalSizeCol: return totalSize() < other.totalSize();
274 default: return QTreeWidgetItem::operator<( rawOther );
275 }
276 }
277
278