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