GNU Octave  6.2.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-2021 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"
50 #include "gui-preferences-global.h"
51 #include "gui-preferences-ff.h"
52 #include "octave-qobject.h"
53 
54 namespace 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, SIGNAL (timeout (void)),
68  this, SLOT (look_for_files (void)));
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, SIGNAL (clicked (void)),
90  this, SLOT (browse_folders (void)));
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 
118  m_file_list = new QTableView;
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 #if defined (HAVE_QHEADERVIEW_SETSECTIONSCLICKABLE)
130  m_file_list->horizontalHeader ()->setSectionsClickable (true);
131 #else
132  m_file_list->horizontalHeader ()->setClickable (true);
133 #endif
134  m_file_list->horizontalHeader ()->setStretchLastSection (true);
135  m_file_list->sortByColumn (settings->value (ff_sort_files_by_column).toInt (),
136  static_cast<Qt::SortOrder>
137  (settings->value (ff_sort_files_by_order).toUInt ()));
138  // FIXME: use value<Qt::SortOrder> instead of static cast after
139  // dropping support of Qt 5.4
140 
141  connect (m_file_list, SIGNAL (doubleClicked (const QModelIndex&)),
142  this, SLOT (item_double_clicked (const QModelIndex &)));
143 
144  m_status_bar = new QStatusBar;
145  m_status_bar->showMessage (tr ("Idle."));
146 
147  m_find_button = new QPushButton (tr ("Find"));
148  m_find_button->setToolTip (tr ("Start search for matching files"));
149  connect (m_find_button, SIGNAL (clicked (void)),
150  this, SLOT (start_find (void)));
151 
152  m_stop_button = new QPushButton (tr ("Stop"));
153  m_stop_button->setToolTip (tr ("Stop searching"));
154  m_stop_button->setEnabled (false);
155  connect (m_stop_button, SIGNAL (clicked (void)),
156  this, SLOT (stop_find (void)));
157 
158  // layout everything
159  QDialogButtonBox *button_box = new QDialogButtonBox (Qt::Vertical);
160  button_box->addButton (m_find_button, QDialogButtonBox::ActionRole);
161  button_box->addButton (m_stop_button, QDialogButtonBox::ActionRole);
162 
163  // add dialog close button
164  m_close_button = button_box->addButton (QDialogButtonBox::Close);
165  connect (button_box, SIGNAL (rejected (void)), this, SLOT (close (void)));
166 
167  // name options
168  QGroupBox *name_group = new QGroupBox (tr ("Filename/location"));
169  QGridLayout *name_layout = new QGridLayout;
170  name_group->setLayout (name_layout);
171 
172  name_layout->addWidget (file_name_label,1,1, 1,1);
173  name_layout->addWidget (m_file_name_edit,1,2, 1,-1);
174 
175  name_layout->addWidget (start_dir_label,2,1);
176  name_layout->addWidget (m_start_dir_edit,2,2,1,3);
177  name_layout->addWidget (m_browse_button,2,5);
178  name_layout->setColumnStretch (2,1);
179 
180  name_layout->addWidget (m_recurse_dirs_check,3,1);
181  name_layout->addWidget (m_include_dirs_check,3,2);
182  name_layout->addWidget (m_name_case_check,3,3);
183 
184  // content options
185  QGroupBox *content_group = new QGroupBox (tr ("File contents"));
186  QGridLayout *content_layout = new QGridLayout;
187  content_group->setLayout (content_layout);
188  content_layout->addWidget (m_contains_text_check,4,1);
189  content_layout->addWidget (m_contains_text_edit,4,2,1,3);
190  content_layout->setColumnStretch (2,1);
191  content_layout->addWidget (m_content_case_check,5,1);
192 
193  QGridLayout *main_layout = new QGridLayout;
194  main_layout->setSizeConstraint (QLayout::SetFixedSize);
195  main_layout->addWidget (name_group, 0, 0);
196  main_layout->addWidget (content_group, 1, 0);
197  main_layout->addWidget (button_box, 0, 1,3,1);
198  main_layout->addWidget (m_file_list,2,0);
199  main_layout->setRowStretch (2,1);
200  main_layout->addWidget (m_status_bar,3,0,1,-1);
201 
202  setLayout (main_layout);
203 
204  connect (this, SIGNAL (finished (int)), this, SLOT (handle_done (int)));
205  }
206 
208  {
209  delete m_dir_iterator;
210  }
211 
213  {
216 
217  if (! settings)
218  return;
219 
220  int sort_column = m_file_list->horizontalHeader ()->sortIndicatorSection ();
221  Qt::SortOrder sort_order
222  = m_file_list->horizontalHeader ()->sortIndicatorOrder ();
223  settings->setValue (ff_sort_files_by_column.key, sort_column);
224  settings->setValue (ff_sort_files_by_order.key, sort_order);
225  settings->setValue (ff_column_state.key, m_file_list->horizontalHeader ()->saveState ());
226 
227  settings->setValue (ff_file_name.key, m_file_name_edit->text ());
228 
229  settings->setValue (ff_start_dir.key, m_start_dir_edit->text ());
230 
231  settings->setValue (ff_recurse_dirs.key, m_recurse_dirs_check->text ());
232  settings->setValue (ff_include_dirs.key, m_include_dirs_check->text ());
233  settings->setValue (ff_name_case.key, m_name_case_check->text ());
234 
235  settings->setValue (ff_contains_text.key, m_contains_text_edit->text ());
236  settings->setValue (ff_check_text.key, m_contains_text_check->isChecked ());
237  settings->setValue (ff_content_case.key, m_content_case_check->isChecked ());
238 
239  settings->sync ();
240  }
241 
242  void find_files_dialog::set_search_dir (const QString& dir)
243  {
244  stop_find ();
245  m_start_dir_edit->setText (dir);
246  }
247 
249  {
250  stop_find ();
251 
252  find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
253  m->clear ();
254 
255  QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
256  if (m_recurse_dirs_check->isChecked ())
257  flags |= QDirIterator::Subdirectories;
258 
259  QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files;
260  if (! m_name_case_check->isChecked ())
261  filters |= QDir::CaseSensitive;
262 
263  QStringList nameFilters;
264  nameFilters.append (m_file_name_edit->text ());
265 
266  if (m_dir_iterator)
267  delete m_dir_iterator;
268 
269  m_dir_iterator = new QDirIterator (m_start_dir_edit->text (), nameFilters,
270  filters, flags);
271 
272  // enable/disable widgets
273  m_find_button->setEnabled (false);
274  m_stop_button->setEnabled (true);
275  m_close_button->setEnabled (false);
276  m_browse_button->setEnabled (false);
277  m_start_dir_edit->setEnabled (false);
278  m_file_name_edit->setEnabled (false);
279  m_recurse_dirs_check->setEnabled (false);
280  m_include_dirs_check->setEnabled (false);
281  m_name_case_check->setEnabled (false);
282  m_contains_text_check->setEnabled (false);
283  m_content_case_check->setEnabled (false);
284  m_contains_text_edit->setEnabled (false);
285 
286  m_status_bar->showMessage (tr ("Searching..."));
287  m_timer->start (0);
288  }
289 
291  {
292  m_timer->stop ();
293 
294  m_find_button->setEnabled (true);
295  m_stop_button->setEnabled (false);
296  m_close_button->setEnabled (true);
297  m_browse_button->setEnabled (true);
298  m_start_dir_edit->setEnabled (true);
299  m_file_name_edit->setEnabled (true);
300  m_recurse_dirs_check->setEnabled (true);
301  m_include_dirs_check->setEnabled (true);
302  m_name_case_check->setEnabled (true);
303  m_contains_text_check->setEnabled (true);
304  m_content_case_check->setEnabled (true);
305  m_contains_text_edit->setEnabled (true);
306 
307  find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
308  QString res_str = QString (tr ("%1 match (es)")).arg (m->rowCount ());
309 
310  m_status_bar->showMessage (res_str);
311  }
312 
314  {
315  int opts = 0; // No options by default.
316  // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
319  if (! settings->value (global_use_native_dialogs).toBool ())
320  opts = QFileDialog::DontUseNativeDialog;
321 
322  QString dir =
323  QFileDialog::getExistingDirectory (this, tr ("Set search directory"),
324  m_start_dir_edit->text (),
325  QFileDialog::Option (opts));
326 
327  if (! dir.isEmpty ())
328  m_start_dir_edit->setText (dir);
329  }
330 
331  void find_files_dialog::item_double_clicked (const QModelIndex& idx)
332  {
333  find_files_model *m = static_cast<find_files_model *> (m_file_list->model ());
334 
335  QFileInfo info = m->fileInfo (idx);
336 
337  if (idx.column () == 1)
338  {
339  // clicked in directory part
340  emit dir_selected (info.absolutePath ());
341  }
342  else
343  {
344  // clicked in filename part
345  if (info.isDir ())
346  emit dir_selected (info.absoluteFilePath ());
347  else
348  emit file_selected (info.absoluteFilePath ());
349  }
350  }
351 
353  {
354  if (m_dir_iterator && m_dir_iterator->hasNext ())
355  {
356  QFileInfo info (m_dir_iterator->next ());
357 
359  = static_cast<find_files_model *> (m_file_list->model ());
360 
361  if (is_match (info))
362  m->addFile (info);
363  }
364  else
365  {
366  stop_find ();
367  }
368  }
369 
371  {
372  // make sure we stopped processing
373  stop_find ();
374  }
375 
376  bool find_files_dialog::is_match (const QFileInfo& info)
377  {
378  bool match = true;
379  if (info.isDir ())
380  {
381  if (! m_include_dirs_check->isChecked ()) match = false;
382  if (m_contains_text_check->isChecked ()) match = false;
383  }
384  else
385  {
386  // a file
387  if (m_contains_text_check->isChecked ())
388  {
389  match = false;
390 
391  QFile file (info.absoluteFilePath ());
392  if (file.open (QIODevice::ReadOnly))
393  {
394  QTextStream stream (&file);
395 
396  QString line;
397  QString match_str = m_contains_text_edit->text ();
398 
399  Qt::CaseSensitivity cs = m_content_case_check->isChecked ()
400  ? Qt::CaseInsensitive
401  : Qt::CaseSensitive;
402 
403  do
404  {
405  line = stream.readLine ();
406  match = line.contains (match_str, cs);
407  }
408  while (! line.isNull () && match == false);
409  }
410 
411  }
412  }
413 
414  return match;
415  }
416 }
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)
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))
T octave_idx_type m
Definition: mx-inlines.cc:773
const QString key