GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
find-files-dialog.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2013-2022 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <QCheckBox>
31#include <QComboBox>
32#include <QDialogButtonBox>
33#include <QDirIterator>
34#include <QFileDialog>
35#include <QFileInfo>
36#include <QGridLayout>
37#include <QGroupBox>
38#include <QHeaderView>
39#include <QIcon>
40#include <QLabel>
41#include <QLineEdit>
42#include <QPushButton>
43#include <QStatusBar>
44#include <QTableView>
45#include <QTextStream>
46#include <QTimer>
47
48#include "find-files-dialog.h"
49#include "find-files-model.h"
51#include "gui-preferences-ff.h"
52#include "octave-qobject.h"
53
54namespace octave
55{
57 : QDialog (p), m_octave_qobj (oct_qobj)
58 {
60
61 setWindowTitle (tr ("Find Files"));
62 setWindowIcon (rmgr.icon ("edit-find"));
63
64 m_dir_iterator = nullptr;
65
66 m_timer = new QTimer (this);
67 connect (m_timer, &QTimer::timeout,
69
71
72 QLabel *file_name_label = new QLabel (tr ("Named:"));
74 m_file_name_edit->setToolTip (tr ("Enter the filename search expression"));
75
76 m_file_name_edit->setText (settings->value (ff_file_name).toString ());
77 file_name_label->setBuddy (m_file_name_edit);
78
79 QLabel *start_dir_label = new QLabel (tr ("Start in:"));
80
82 m_start_dir_edit->setText (settings->value (ff_start_dir.key,
83 QDir::currentPath ()).toString ());
84 m_start_dir_edit->setToolTip (tr ("Enter the start directory"));
85 start_dir_label->setBuddy (m_start_dir_edit);
86
87 m_browse_button = new QPushButton (tr ("Browse..."));
88 m_browse_button->setToolTip (tr ("Browse for start directory"));
89 connect (m_browse_button, &QPushButton::clicked,
91
92 m_recurse_dirs_check = new QCheckBox (tr ("Search subdirectories"));
93 m_recurse_dirs_check->setChecked (settings->value (ff_recurse_dirs).toBool ());
94 m_recurse_dirs_check->setToolTip (tr ("Search recursively through directories for matching files"));
95
96 m_include_dirs_check = new QCheckBox (tr ("Include directory names"));
97 m_include_dirs_check->setChecked (settings->value (ff_include_dirs).toBool ());
98 m_include_dirs_check->setToolTip (tr ("Include matching directories in search results"));
99
100 m_name_case_check = new QCheckBox (tr ("Name case insensitive"));
101 m_name_case_check->setChecked (settings->value (ff_name_case).toBool ());
102 m_name_case_check->setToolTip (tr ("Set matching name is case insensitive"));
103
104 m_contains_text_check = new QCheckBox (tr ("Contains text:"));
105 m_contains_text_check->setToolTip (tr ("Enter the file content search expression"));
106 m_contains_text_check->setChecked (settings->value (ff_check_text).toBool ());
107
109 m_contains_text_edit->setToolTip (tr ("Text to match"));
110 m_contains_text_edit->setText (settings->value (ff_contains_text).toString ());
111
112 m_content_case_check = new QCheckBox (tr ("Text case insensitive"));
113 m_content_case_check->setChecked (settings->value (ff_content_case).toBool ());
114 m_content_case_check->setToolTip (tr ("Set text content is case insensitive"));
115
116 find_files_model *model = new find_files_model (this);
117
119 m_file_list->setWordWrap (false);
120 m_file_list->setModel (model);
121 m_file_list->setShowGrid (false);
122 m_file_list->setSelectionBehavior (QAbstractItemView::SelectRows);
123 m_file_list->setSelectionMode (QAbstractItemView::SingleSelection);
124 m_file_list->setAlternatingRowColors (true);
125 m_file_list->setToolTip (tr ("Search results"));
126 m_file_list->setSortingEnabled (true);
127 m_file_list->horizontalHeader ()->restoreState (settings->value (ff_column_state.key).toByteArray ());
128 m_file_list->horizontalHeader ()->setSortIndicatorShown (true);
129 m_file_list->horizontalHeader ()->setSectionsClickable (true);
130 m_file_list->horizontalHeader ()->setStretchLastSection (true);
131 m_file_list->sortByColumn (settings->value (ff_sort_files_by_column).toInt (),
132 static_cast<Qt::SortOrder>
133 (settings->value (ff_sort_files_by_order).toUInt ()));
134 // FIXME: use value<Qt::SortOrder> instead of static cast after
135 // dropping support of Qt 5.4
136
137 connect (m_file_list, &QTableView::doubleClicked,
139
140 m_status_bar = new QStatusBar;
141 m_status_bar->showMessage (tr ("Idle."));
142
143 m_find_button = new QPushButton (tr ("Find"));
144 m_find_button->setToolTip (tr ("Start search for matching files"));
145 connect (m_find_button, &QPushButton::clicked,
147
148 m_stop_button = new QPushButton (tr ("Stop"));
149 m_stop_button->setToolTip (tr ("Stop searching"));
150 m_stop_button->setEnabled (false);
151 connect (m_stop_button, &QPushButton::clicked,
153
154 // layout everything
155 QDialogButtonBox *button_box = new QDialogButtonBox (Qt::Vertical);
156 button_box->addButton (m_find_button, QDialogButtonBox::ActionRole);
157 button_box->addButton (m_stop_button, QDialogButtonBox::ActionRole);
158
159 // add dialog close button
160 m_close_button = button_box->addButton (QDialogButtonBox::Close);
161 connect (button_box, &QDialogButtonBox::rejected,
162 this, &find_files_dialog::close);
163
164 // name options
165 QGroupBox *name_group = new QGroupBox (tr ("Filename/location"));
166 QGridLayout *name_layout = new QGridLayout;
167 name_group->setLayout (name_layout);
168
169 name_layout->addWidget (file_name_label, 1, 1, 1, 1);
170 name_layout->addWidget (m_file_name_edit, 1, 2, 1, -1);
171
172 name_layout->addWidget (start_dir_label, 2, 1);
173 name_layout->addWidget (m_start_dir_edit, 2, 2, 1, 3);
174 name_layout->addWidget (m_browse_button, 2, 5);
175 name_layout->setColumnStretch (2, 1);
176
177 name_layout->addWidget (m_recurse_dirs_check, 3, 1);
178 name_layout->addWidget (m_include_dirs_check, 3, 2);
179 name_layout->addWidget (m_name_case_check, 3, 3);
180
181 // content options
182 QGroupBox *content_group = new QGroupBox (tr ("File contents"));
183 QGridLayout *content_layout = new QGridLayout;
184 content_group->setLayout (content_layout);
185 content_layout->addWidget (m_contains_text_check, 4, 1);
186 content_layout->addWidget (m_contains_text_edit, 4, 2, 1, 3);
187 content_layout->setColumnStretch (2, 1);
188 content_layout->addWidget (m_content_case_check, 5, 1);
189
190 QGridLayout *main_layout = new QGridLayout;
191 main_layout->setSizeConstraint (QLayout::SetFixedSize);
192 main_layout->addWidget (name_group, 0, 0);
193 main_layout->addWidget (content_group, 1, 0);
194 main_layout->addWidget (button_box, 0, 1, 3, 1);
195 main_layout->addWidget (m_file_list, 2, 0);
196 main_layout->setRowStretch (2, 1);
197 main_layout->addWidget (m_status_bar, 3, 0, 1, -1);
198
199 setLayout (main_layout);
200
201 connect (this, &find_files_dialog::finished,
203 }
204
206 {
207 delete m_dir_iterator;
208 }
209
211 {
214
215 if (! settings)
216 return;
217
218 int sort_column = m_file_list->horizontalHeader ()->sortIndicatorSection ();
219 Qt::SortOrder sort_order
220 = m_file_list->horizontalHeader ()->sortIndicatorOrder ();
221 settings->setValue (ff_sort_files_by_column.key, sort_column);
222 settings->setValue (ff_sort_files_by_order.key, sort_order);
223 settings->setValue (ff_column_state.key, m_file_list->horizontalHeader ()->saveState ());
224
225 settings->setValue (ff_file_name.key, m_file_name_edit->text ());
226
227 settings->setValue (ff_start_dir.key, m_start_dir_edit->text ());
228
229 settings->setValue (ff_recurse_dirs.key, m_recurse_dirs_check->text ());
230 settings->setValue (ff_include_dirs.key, m_include_dirs_check->text ());
231 settings->setValue (ff_name_case.key, m_name_case_check->text ());
232
234 settings->setValue (ff_check_text.key, m_contains_text_check->isChecked ());
235 settings->setValue (ff_content_case.key, m_content_case_check->isChecked ());
236
237 settings->sync ();
238 }
239
240 void find_files_dialog::set_search_dir (const QString& dir)
241 {
242 stop_find ();
243 m_start_dir_edit->setText (dir);
244 }
245
247 {
248 stop_find ();
249
250 find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
251 m->clear ();
252
253 QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
254 if (m_recurse_dirs_check->isChecked ())
255 flags |= QDirIterator::Subdirectories;
256
257 QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files;
258 if (! m_name_case_check->isChecked ())
259 filters |= QDir::CaseSensitive;
260
261 QStringList nameFilters;
262 nameFilters.append (m_file_name_edit->text ());
263
264 if (m_dir_iterator)
265 delete m_dir_iterator;
266
267 m_dir_iterator = new QDirIterator (m_start_dir_edit->text (), nameFilters,
268 filters, flags);
269
270 // enable/disable widgets
271 m_find_button->setEnabled (false);
272 m_stop_button->setEnabled (true);
273 m_close_button->setEnabled (false);
274 m_browse_button->setEnabled (false);
275 m_start_dir_edit->setEnabled (false);
276 m_file_name_edit->setEnabled (false);
277 m_recurse_dirs_check->setEnabled (false);
278 m_include_dirs_check->setEnabled (false);
279 m_name_case_check->setEnabled (false);
280 m_contains_text_check->setEnabled (false);
281 m_content_case_check->setEnabled (false);
282 m_contains_text_edit->setEnabled (false);
283
284 m_status_bar->showMessage (tr ("Searching..."));
285 m_timer->start (0);
286 }
287
289 {
290 m_timer->stop ();
291
292 m_find_button->setEnabled (true);
293 m_stop_button->setEnabled (false);
294 m_close_button->setEnabled (true);
295 m_browse_button->setEnabled (true);
296 m_start_dir_edit->setEnabled (true);
297 m_file_name_edit->setEnabled (true);
298 m_recurse_dirs_check->setEnabled (true);
299 m_include_dirs_check->setEnabled (true);
300 m_name_case_check->setEnabled (true);
301 m_contains_text_check->setEnabled (true);
302 m_content_case_check->setEnabled (true);
303 m_contains_text_edit->setEnabled (true);
304
305 find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
306 QString res_str = QString (tr ("%1 match (es)")).arg (m->rowCount ());
307
308 m_status_bar->showMessage (res_str);
309 }
310
312 {
313 int opts = 0; // No options by default.
314 // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
317 if (! settings->value (global_use_native_dialogs).toBool ())
318 opts = QFileDialog::DontUseNativeDialog;
319
320 QString dir =
321 QFileDialog::getExistingDirectory (this, tr ("Set search directory"),
322 m_start_dir_edit->text (),
323 QFileDialog::Option (opts));
324
325 if (! dir.isEmpty ())
326 m_start_dir_edit->setText (dir);
327 }
328
329 void find_files_dialog::item_double_clicked (const QModelIndex& idx)
330 {
331 find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
332
333 QFileInfo info = m->fileInfo (idx);
334
335 if (idx.column () == 1)
336 {
337 // clicked in directory part
338 emit dir_selected (info.absolutePath ());
339 }
340 else
341 {
342 // clicked in filename part
343 if (info.isDir ())
344 emit dir_selected (info.absoluteFilePath ());
345 else
346 emit file_selected (info.absoluteFilePath ());
347 }
348 }
349
351 {
352 if (m_dir_iterator && m_dir_iterator->hasNext ())
353 {
354 QFileInfo info (m_dir_iterator->next ());
355
357 = static_cast<find_files_model *> (m_file_list->model ());
358
359 if (is_match (info))
360 m->addFile (info);
361 }
362 else
363 {
364 stop_find ();
365 }
366 }
367
369 {
370 // make sure we stopped processing
371 stop_find ();
372 }
373
374 bool find_files_dialog::is_match (const QFileInfo& info)
375 {
376 bool match = true;
377 if (info.isDir ())
378 {
379 if (! m_include_dirs_check->isChecked ()) match = false;
380 if (m_contains_text_check->isChecked ()) match = false;
381 }
382 else
383 {
384 // a file
385 if (m_contains_text_check->isChecked ())
386 {
387 match = false;
388
389 QFile file (info.absoluteFilePath ());
390 if (file.open (QIODevice::ReadOnly))
391 {
392 QTextStream stream (&file);
393
394 QString line;
395 QString match_str = m_contains_text_edit->text ();
396
397 Qt::CaseSensitivity cs = m_content_case_check->isChecked ()
398 ? Qt::CaseInsensitive
399 : Qt::CaseSensitive;
400
401 do
402 {
403 line = stream.readLine ();
404 match = line.contains (match_str, cs);
405 }
406 while (! line.isNull () && match == false);
407 }
408
409 }
410 }
411
412 return match;
413 }
414}
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
void file_selected(const QString &fileName)
void set_search_dir(const QString &dir)
void dir_selected(const QString &fileName)
void item_double_clicked(const QModelIndex &)
bool is_match(const QFileInfo &info)
find_files_dialog(QWidget *parent, base_qobject &oct_qobj)
QFileInfo fileInfo(const QModelIndex &p) const
void addFile(const QFileInfo &info)
int rowCount(const QModelIndex &p=QModelIndex()) const
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool fallback=true)
const gui_pref ff_include_dirs("findfiles/include_dirs", QVariant(false))
const gui_pref ff_start_dir("findfiles/start_dir", QVariant(""))
const gui_pref ff_check_text("findfiles/check_text", QVariant(false))
const gui_pref ff_file_name("findfiles/file_name", QVariant("*"))
const gui_pref ff_recurse_dirs("findfiles/recurse_dirs", QVariant(false))
const gui_pref ff_content_case("findfiles/content_case", QVariant(false))
const gui_pref ff_sort_files_by_order("findfiles/sort_files_by_order", QVariant(Qt::AscendingOrder))
const gui_pref ff_column_state("findfiles/column_state", QVariant())
const gui_pref ff_sort_files_by_column("findfiles/sort_files_by_column", QVariant(0))
const gui_pref ff_name_case("findfiles/name_case", QVariant(false))
const gui_pref ff_contains_text("findfiles/contains_text", QVariant(""))
const gui_pref global_use_native_dialogs("use_native_file_dialogs", QVariant(true))
const QString key