GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
files-dock-widget.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2023 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 <QApplication>
31 #include <QClipboard>
32 #include <QCompleter>
33 #include <QDebug>
34 #include <QDesktopServices>
35 #include <QFileDialog>
36 #include <QFileInfo>
37 #include <QHeaderView>
38 #include <QInputDialog>
39 #include <QLineEdit>
40 #include <QMenu>
41 #include <QMessageBox>
42 #include <QProcess>
43 #include <QSizePolicy>
44 #include <QStyledItemDelegate>
45 #include <QTimer>
46 #include <QToolButton>
47 #include <QUrl>
48 
49 #include "files-dock-widget.h"
50 #include "gui-preferences-fb.h"
51 #include "gui-preferences-global.h"
52 #include "octave-qobject.h"
53 #include "octave-qtutils.h"
54 #include "qt-interpreter-events.h"
55 
56 #include "oct-env.h"
57 
59 
60 class FileTreeViewer : public QTreeView
61 {
62 public:
63 
65 
66  ~FileTreeViewer (void) = default;
67 
68  void mousePressEvent (QMouseEvent *e)
69  {
70  if (e->button () != Qt::RightButton)
71  QTreeView::mousePressEvent (e);
72  }
73 };
74 
75 // to have file renamed in the file tree, it has to be renamed in
76 // QFileSystemModel::setData.
77 // For the editor to behave correctly, some signals must be sent before
78 // and after the rename
80 {
81 public:
83 
84  ~file_system_model () = default;
85 
86  bool setData (const QModelIndex &idx, const QVariant &value,
87  int role) override
88  {
89  if (!idx.isValid () || idx.column () != 0 || role != Qt::EditRole
90  || (flags (idx) & Qt::ItemIsEditable) == 0)
91  {
92  return false;
93  }
94 
95  QString new_name = value.toString ();
96  QString old_name = idx.data ().toString ();
97  if (new_name == old_name)
98  return true;
99  if (new_name.isEmpty ()
100  || QDir::toNativeSeparators (new_name).contains (QDir::separator ()))
101  {
102  display_rename_failed_message (old_name, new_name);
103  return false;
104  }
105 
106  auto parent_dir = QDir(filePath (parent (idx)));
107 
108  files_dock_widget *fdw = static_cast<files_dock_widget*>(parent());
109 
110  fdw->file_remove_signal(parent_dir.filePath(old_name), parent_dir.filePath(new_name));
111 
112  if (!parent_dir.rename (old_name, new_name))
113  {
114  display_rename_failed_message (old_name, new_name);
115  fdw->file_renamed_signal(false);
116  return false;
117  }
118 
119  fdw->file_renamed_signal(true);
120 
121  emit fileRenamed(parent_dir.absolutePath(), old_name, new_name);
122  revert();
123 
124  return true;
125  }
126 
127 private:
128  void display_rename_failed_message (const QString &old_name,
129  const QString &new_name)
130  {
131  const QString message =
132 
133  files_dock_widget::tr ("Could not rename file \"%1\" to \"%2\".")
134  .arg (old_name)
135  .arg (new_name);
136  QMessageBox::information (static_cast<QWidget *> (parent ()),
137  QFileSystemModel::tr ("Invalid filename"),
138  message, QMessageBox::Ok);
139  }
140 };
141 
142 // Delegate to improve ergonomy of file renaming by pre-selecting the text
143 // before the extension.
145 {
146 public:
147  RenameItemDelegate (QObject *parent = nullptr)
148  : QStyledItemDelegate{ parent }
149  {
150  }
151 
152  void setEditorData (QWidget *editor,
153  const QModelIndex &index) const override
154  {
155  QLineEdit *line_edit = qobject_cast<QLineEdit *> (editor);
156 
157  if (!line_edit)
158  {
159  QStyledItemDelegate::setEditorData (editor, index);
160  return;
161  }
162 
163  QString filename = index.data (Qt::EditRole).toString ();
164 
165  int select_len = filename.indexOf (QChar ('.'));
166  if (select_len == -1)
167  select_len = filename.size ();
168 
169  line_edit->setText (filename);
170 
171  // Qt calls QLineEdit::selectAll after this function is called, so to
172  // actually restrict the selection, we have to post the modification at
173  // the end of the event loop.
174  // QTimer allows this easily with 0 as timeout.
175  QTimer::singleShot (0, [=] () {
176  line_edit->setSelection (0, select_len);
177  });
178  }
179 };
180 
182  : octave_dock_widget ("FilesDockWidget", p, oct_qobj)
183 {
184  set_title (tr ("File Browser"));
185  setToolTip (tr ("Browse your files"));
186 
187  m_sig_mapper = nullptr;
188 
189  m_columns_shown = QStringList ();
190  m_columns_shown.append (tr ("File size"));
191  m_columns_shown.append (tr ("File type"));
192  m_columns_shown.append (tr ("Date modified"));
193  m_columns_shown.append (tr ("Show hidden"));
194  m_columns_shown.append (tr ("Alternating row colors"));
195 
196  m_columns_shown_keys = QStringList ();
202 
209 
210  QWidget *container = new QWidget (this);
211 
212  setWidget (container);
213 
214  // Create a toolbar
215  m_navigation_tool_bar = new QToolBar ("", container);
216  m_navigation_tool_bar->setAllowedAreas (Qt::TopToolBarArea);
217  m_navigation_tool_bar->setMovable (false);
218 
219  m_current_directory = new QComboBox (m_navigation_tool_bar);
220  m_current_directory->setToolTip (tr ("Enter the path or filename"));
221  m_current_directory->setEditable (true);
222  m_current_directory->setMaxCount (MaxMRUDirs);
223  m_current_directory->setInsertPolicy (QComboBox::NoInsert);
224  m_current_directory->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
225  QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
226  m_current_directory->setSizePolicy (sizePol);
227 
229 
230  QAction *directory_up_action
231  = new QAction (rmgr.icon ("folder-up", false, "go-up"), "", m_navigation_tool_bar);
232  directory_up_action->setToolTip (tr ("One directory up"));
233 
235  = new QAction (rmgr.icon ("go-first"), tr ("Show Octave directory"),
237  m_sync_browser_directory_action->setToolTip (tr ("Go to current Octave directory"));
238  m_sync_browser_directory_action->setEnabled (false);
239 
241  = new QAction (rmgr.icon ("go-last"), tr ("Set Octave directory"),
243  m_sync_octave_directory_action->setToolTip (tr ("Set Octave directory to current browser directory"));
244  m_sync_octave_directory_action->setEnabled (false);
245 
246  QToolButton *popdown_button = new QToolButton ();
247  popdown_button->setToolTip (tr ("Actions on current directory"));
248  QMenu *popdown_menu = new QMenu ();
249  popdown_menu->addAction (rmgr.icon ("user-home"),
250  tr ("Show Home Directory"), this,
251  SLOT (popdownmenu_home (bool)));
252  popdown_menu->addAction (m_sync_browser_directory_action);
253  popdown_menu->addAction (m_sync_octave_directory_action);
254  popdown_button->setMenu (popdown_menu);
255  popdown_button->setPopupMode (QToolButton::InstantPopup);
256  popdown_button->setDefaultAction (
257  new QAction (rmgr.icon ("folder-settings", false, "applications-system"),
258  "", m_navigation_tool_bar));
259 
260  popdown_menu->addSeparator ();
261  popdown_menu->addAction (rmgr.icon ("folder"),
262  tr ("Set Browser Directory..."),
264  popdown_menu->addSeparator ();
265  popdown_menu->addAction (rmgr.icon ("edit-find"),
266  tr ("Find Files..."),
268  popdown_menu->addSeparator ();
269  popdown_menu->addAction (rmgr.icon ("document-new"),
270  tr ("New File..."),
272  popdown_menu->addAction (rmgr.icon ("folder-new"),
273  tr ("New Directory..."),
275 
277  m_navigation_tool_bar->addAction (directory_up_action);
278  m_navigation_tool_bar->addWidget (popdown_button);
279 
280  connect (directory_up_action, &QAction::triggered,
282  connect (m_sync_octave_directory_action, &QAction::triggered,
284  connect (m_sync_browser_directory_action, &QAction::triggered,
286 
288  // FIXME: what should happen if settings is 0?
289 
290  // Create the QFileSystemModel starting in the desired directory
291  QDir startup_dir; // take current dir
292 
293  if (settings->value (fb_restore_last_dir).toBool ())
294  {
295  // restore last dir from previous session
296  QStringList last_dirs
297  = settings->value (fb_mru_list.key).toStringList ();
298  if (last_dirs.length () > 0)
299  startup_dir = QDir (last_dirs.at (0)); // last dir in previous session
300  }
301  else if (! settings->value (fb_startup_dir).toString ().isEmpty ())
302  {
303  // do not restore but there is a startup dir configured
304  startup_dir = QDir (settings->value (fb_startup_dir.key).toString ());
305  }
306 
307  if (! startup_dir.exists ())
308  {
309  // the configured startup dir does not exist, take actual one
310  startup_dir = QDir ();
311  }
312 
314  m_file_system_model->setResolveSymlinks (false);
315  m_file_system_model->setFilter (
316  QDir::System | QDir::NoDotAndDotDot | QDir::AllEntries);
317  QModelIndex rootPathIndex
318  = m_file_system_model->setRootPath (startup_dir.absolutePath ());
319 
320  // Attach the model to the QTreeView and set the root index
321  m_file_tree_view = new FileTreeViewer (container);
322  m_file_tree_view->setSelectionMode (QAbstractItemView::ExtendedSelection);
324  m_file_tree_view->setRootIndex (rootPathIndex);
325  m_file_tree_view->setSortingEnabled (true);
326  m_file_tree_view->setAlternatingRowColors (true);
327  m_file_tree_view->setAnimated (true);
328  m_file_tree_view->setToolTip (tr ("Double-click to open file/folder, right click for alternatives"));
329 
330  // allow renaming directly in the tree view with
331  // m_file_tree_view->edit(index)
332  m_file_system_model->setReadOnly (false);
333  // delegate to improve rename ergonomy by pre-selecting text up to the
334  // extension
335  auto *rename_delegate = new RenameItemDelegate (this);
336  m_file_tree_view->setItemDelegateForColumn (0, rename_delegate);
337  // prevent the tree view to override Octave's double-click behavior
338  m_file_tree_view->setEditTriggers (QAbstractItemView::NoEditTriggers);
339  // create the rename action (that will be added to context menu)
340  // and associate to F2 key shortcut
341  m_rename_action = new QAction (tr ("Rename..."), this);
342  m_rename_action->setShortcut (Qt::Key_F2);
343  m_rename_action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
344  connect (m_rename_action, &QAction::triggered, this,
346  addAction(m_rename_action);
347 
348  // get sort column and order as well as column state (order and width)
349 
350  m_file_tree_view->sortByColumn
351  (settings->value (fb_sort_column).toInt (),
352  static_cast<Qt::SortOrder> (settings->value (fb_sort_order).toUInt ()));
353  // FIXME: use value<Qt::SortOrder> instead of static cast after
354  // dropping support of Qt 5.4
355 
356  if (settings->contains (fb_column_state.key))
357  m_file_tree_view->header ()->restoreState
358  (settings->value (fb_column_state.key).toByteArray ());
359 
360  // Set header properties for sorting
361  m_file_tree_view->header ()->setSectionsClickable (true);
362  m_file_tree_view->header ()->setSectionsMovable (true);
363  m_file_tree_view->header ()->setSortIndicatorShown (true);
364 
365  QStringList mru_dirs =
366  settings->value (fb_mru_list.key).toStringList ();
367  m_current_directory->addItems (mru_dirs);
368 
369  m_current_directory->setEditText
370  (m_file_system_model->fileInfo (rootPathIndex). absoluteFilePath ());
371 
372  connect (m_file_tree_view, &FileTreeViewer::activated,
374 
375  // add context menu to tree_view
376  m_file_tree_view->setContextMenuPolicy (Qt::CustomContextMenu);
377  connect (m_file_tree_view, &FileTreeViewer::customContextMenuRequested,
379 
380  m_file_tree_view->header ()->setContextMenuPolicy (Qt::CustomContextMenu);
381  connect (m_file_tree_view->header (),
382  &QHeaderView::customContextMenuRequested,
384 
385  // Layout the widgets vertically with the toolbar on top
386  QVBoxLayout *vbox_layout = new QVBoxLayout ();
387  vbox_layout->setSpacing (0);
388  vbox_layout->addWidget (m_navigation_tool_bar);
389  vbox_layout->addWidget (m_file_tree_view);
390  vbox_layout->setMargin (1);
391 
392  container->setLayout (vbox_layout);
393 
394  // FIXME: Add right-click contextual menus for copying, pasting,
395  // deleting files (and others).
396 
397  connect (m_current_directory->lineEdit (), &QLineEdit::returnPressed,
399 
400  // FIXME: We could use
401  //
402  // connect (m_current_directory,
403  // QOverload<const QString&>::of (&QComboBox::activated),
404  // this, &files_dock_widget::set_current_directory);
405  //
406  // but referring to QComboBox::activated will generate deprecated
407  // function warnings from GCC. We could also use
408  //
409  // connect (m_current_directory, &QComboBox::textActivated,
410  // this, &files_dock_widget::set_current_directory);
411  //
412  // but the function textActivated was not introduced until Qt 5.14
413  // so we'll need a feature test.
414 
415  connect (m_current_directory, SIGNAL (activated (const QString&)),
416  this, SLOT (set_current_directory (const QString&)));
417 
418  QCompleter *completer = new QCompleter (m_file_system_model, this);
419  m_current_directory->setCompleter (completer);
420 
421  setFocusProxy (m_current_directory);
422 
423  m_sync_octave_dir = true; // default, overwritten with notice_settings ()
424  m_octave_dir = "";
425 
426  if (! p)
427  make_window ();
428 }
429 
431 {
434 
435  if (! settings)
436  return;
437 
438  int sort_column = m_file_tree_view->header ()->sortIndicatorSection ();
439  Qt::SortOrder sort_order = m_file_tree_view->header ()->sortIndicatorOrder ();
440  settings->setValue (fb_sort_column.key, sort_column);
441  settings->setValue (fb_sort_order.key, sort_order);
442  settings->setValue (fb_column_state.key,
443  m_file_tree_view->header ()->saveState ());
444 
445  QStringList dirs;
446  for (int i=0; i< m_current_directory->count (); i++)
447  {
448  dirs.append (m_current_directory->itemText (i));
449  }
450  settings->setValue (fb_mru_list.key, dirs);
451 
452  settings->sync ();
453 
455 
456  if (m_sig_mapper)
457  delete m_sig_mapper;
458 }
459 
460 void files_dock_widget::item_double_clicked (const QModelIndex& index)
461 {
462  // Retrieve the file info associated with the model index.
463  QFileInfo fileInfo = m_file_system_model->fileInfo (index);
464  set_current_directory (fileInfo.absoluteFilePath ());
465 }
466 
468 {
469  display_directory (dir);
470 }
471 
473 {
474  display_directory (m_current_directory->currentText ());
475 }
476 
478 {
479  QDir dir
480  = QDir (m_file_system_model->filePath (m_file_tree_view->rootIndex ()));
481 
482  dir.cdUp ();
483  display_directory (dir.absolutePath ());
484 }
485 
487 {
488  QDir dir
489  = QDir (m_file_system_model->filePath (m_file_tree_view->rootIndex ()));
490 
491  emit displayed_directory_changed (dir.absolutePath ());
492 }
493 
495 {
496  display_directory (m_octave_dir, false); // false: no sync of octave dir
497 }
498 
500 {
501  m_octave_dir = dir;
502  if (m_sync_octave_dir)
503  display_directory (m_octave_dir, false); // false: no sync of octave dir
504 }
505 
506 void files_dock_widget::display_directory (const QString& dir,
507  bool set_octave_dir)
508 {
509  QFileInfo fileInfo (dir);
510  if (fileInfo.exists ())
511  {
512  if (fileInfo.isDir ())
513  {
514  m_file_tree_view->setRootIndex (m_file_system_model->
515  index (fileInfo.absoluteFilePath ()));
516  m_file_system_model->setRootPath (fileInfo.absoluteFilePath ());
517  if (m_sync_octave_dir && set_octave_dir)
518  process_set_current_dir (fileInfo.absoluteFilePath ());
519 
520  // see if it's in the list, and if it is,
521  // remove it and then put at top of the list
522  int index
523  = m_current_directory->findText (fileInfo.absoluteFilePath ());
524  if (index != -1)
525  {
526  m_current_directory->removeItem (index);
527  }
528  m_current_directory->insertItem (0, fileInfo.absoluteFilePath ());
529  m_current_directory->setCurrentIndex (0);
530  }
531  else
532  {
533  QString abs_fname = fileInfo.absoluteFilePath ();
534 
535  QString suffix = fileInfo.suffix ().toLower ();
538  QString ext = settings->value (fb_txt_file_ext).toString ();
539 #if defined (HAVE_QT_SPLITBEHAVIOR_ENUM)
540  QStringList extensions = ext.split (";", Qt::SkipEmptyParts);
541 #else
542  QStringList extensions = ext.split (";", QString::SkipEmptyParts);
543 #endif
544  if (QFile::exists (abs_fname))
545  {
546  if (extensions.contains (suffix))
547  emit open_file (fileInfo.absoluteFilePath ());
548  else
549  emit open_any_signal (abs_fname);
550  }
551  }
552  }
553 }
554 
555 void files_dock_widget::open_item_in_app (const QModelIndex& index)
556 {
557  // Retrieve the file info associated with the model index.
558  QFileInfo fileInfo = m_file_system_model->fileInfo (index);
559 
560  QString file = fileInfo.absoluteFilePath ();
561 
562  QDesktopServices::openUrl (QUrl::fromLocalFile (file));
563 }
564 
566 {
569 
570  QString key = m_columns_shown_keys.at (col);
571  bool shown = settings->value (key, false).toBool ();
572  settings->setValue (key, ! shown);
573  settings->sync ();
574 
575  switch (col)
576  {
577  case 0:
578  case 1:
579  case 2:
580  // toggle column visibility
581  m_file_tree_view->setColumnHidden (col + 1, shown);
582  break;
583  case 3:
584  case 4:
585  // other actions depending on new settings
587  break;
588  }
589 }
590 
592 {
593  QMenu menu (this);
594 
595  if (m_sig_mapper)
596  delete m_sig_mapper;
597  m_sig_mapper = new QSignalMapper (this);
598 
601 
602  for (int i = 0; i < m_columns_shown.size (); i++)
603  {
604  QAction *action = menu.addAction (m_columns_shown.at (i),
605  m_sig_mapper, SLOT (map ()));
606  m_sig_mapper->setMapping (action, i);
607  action->setCheckable (true);
608  action->setChecked
609  (settings->value (m_columns_shown_keys.at (i),
610  m_columns_shown_defs.at (i)).toBool ());
611  }
612 
613  // FIXME: We could use
614  //
615  // connect (&m_sig_mapper, QOverload<int>::of (&QSignalMapper::mapped),
616  // this, &workspace_view::toggle_header);
617  //
618  // but referring to QSignalMapper::mapped will generate deprecated
619  // function warnings from GCC. We could also use
620  //
621  // connect (&m_sig_mapper, &QSignalMapper::mappedInt,
622  // this, &workspace_view::toggle_header);
623  //
624  // but the function mappedInt was not introduced until Qt 5.15 so
625  // we'll need a feature test.
626 
627  connect (m_sig_mapper, SIGNAL (mapped (int)),
628  this, SLOT (toggle_header (int)));
629 
630  menu.exec (m_file_tree_view->mapToGlobal (mpos));
631 }
632 
634 {
635 
636  QMenu menu (this);
637 
638  QModelIndex index = m_file_tree_view->indexAt (mpos);
639 
640  if (index.isValid ())
641  {
642  QFileInfo info = m_file_system_model->fileInfo (index);
643 
644  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
645  QModelIndexList sel = m->selectedRows ();
646 
647  // check if item at mouse position is seleccted
648  if (! sel.contains (index))
649  {
650  // is not selected -> clear actual selection and select this item
651  m->setCurrentIndex (index,
652  QItemSelectionModel::Clear
653  | QItemSelectionModel::Select
654  | QItemSelectionModel::Rows);
655  }
656 
658 
659  // construct the context menu depending on item
660  menu.addAction (rmgr.icon ("document-open"), tr ("Open"),
662 
663  if (info.isDir ())
664  {
665  menu.addAction (tr ("Open in System File Explorer"),
667  }
668 
669  if (info.isFile ())
670  menu.addAction (tr ("Open in Text Editor"),
672 
673  menu.addAction (tr ("Copy Selection to Clipboard"),
675 
676  if (info.isFile () && info.suffix () == "m")
677  menu.addAction (rmgr.icon ("media-playback-start"), tr ("Run"),
679 
680  if (info.isFile ())
681  menu.addAction (tr ("Load Data"),
683 
684  if (info.isDir ())
685  {
686  menu.addSeparator ();
687  menu.addAction (rmgr.icon ("go-first"), tr ("Set Current Directory"),
689 
690  QMenu *add_path_menu = menu.addMenu (tr ("Add to Path"));
691 
692  add_path_menu->addAction (tr ("Selected Directories"),
693  this, [=] (bool checked) { contextmenu_add_to_path (checked); });
694  add_path_menu->addAction (tr ("Selected Directories and Subdirectories"),
696 
697  QMenu *rm_path_menu = menu.addMenu (tr ("Remove from Path"));
698 
699  rm_path_menu->addAction (tr ("Selected Directories"),
701  rm_path_menu->addAction (tr ("Selected Directories and Subdirectories"),
703 
704  menu.addSeparator ();
705 
706  menu.addAction (rmgr.icon ("edit-find"), tr ("Find Files..."),
708  }
709 
710  menu.addSeparator ();
711  menu.addAction (m_rename_action);
712  menu.addAction (rmgr.icon ("edit-delete"), tr ("Delete..."),
714 
715  if (info.isDir ())
716  {
717  menu.addSeparator ();
718  menu.addAction (rmgr.icon ("document-new"), tr ("New File..."),
720  menu.addAction (rmgr.icon ("folder-new"), tr ("New Directory..."),
722  }
723 
724  // show the menu
725  menu.exec (m_file_tree_view->mapToGlobal (mpos));
726 
727  }
728 }
729 
731 {
732 
733  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
734  QModelIndexList rows = m->selectedRows ();
735 
736  for (auto it = rows.begin (); it != rows.end (); it++)
737  {
738  QFileInfo file = m_file_system_model->fileInfo (*it);
739  if (file.exists ())
740  display_directory (file.absoluteFilePath ());
741  }
742 }
743 
745 {
746 
747  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
748  QModelIndexList rows = m->selectedRows ();
749 
750  for (auto it = rows.begin (); it != rows.end (); it++)
751  {
752  QFileInfo file = m_file_system_model->fileInfo (*it);
753  if (file.exists ())
754  emit open_file (file.absoluteFilePath ());
755  }
756 }
757 
759 {
760  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
761  QModelIndexList rows = m->selectedRows ();
762 
763  for (auto it = rows.begin (); it != rows.end (); it++)
764  open_item_in_app (*it);
765 }
766 
768 {
769  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
770  QModelIndexList rows = m->selectedRows ();
771 
772  QStringList selection;
773 
774  for (auto it = rows.begin (); it != rows.end (); it++)
775  {
776  QFileInfo info = m_file_system_model->fileInfo (*it);
777 
778  selection << info.fileName ();
779  }
780 
781  QClipboard *clipboard = QApplication::clipboard ();
782 
783  clipboard->setText (selection.join ("\n"));
784 }
785 
787 {
788  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
789  QModelIndexList rows = m->selectedRows ();
790 
791  if (rows.size () > 0)
792  {
793  QModelIndex index = rows[0];
794 
795  QFileInfo info = m_file_system_model->fileInfo (index);
796 
797  emit load_file_signal (info.fileName ());
798  }
799 }
800 
802 {
803  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
804  QModelIndexList rows = m->selectedRows ();
805 
806  if (rows.size () > 0)
807  {
808  QModelIndex index = rows[0];
809 
810  QFileInfo info = m_file_system_model->fileInfo (index);
811  emit run_file_signal (info);
812  }
813 }
814 
816 {
817  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
818  QModelIndexList rows = m->selectedRows ();
819  if (rows.size () > 0)
820  {
821  QModelIndex index = rows[0];
822  m_file_tree_view->edit(index);
823  }
824 }
825 
827 {
828  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
829  QModelIndexList rows = m->selectedRows ();
830 
831  int file_cnt = rows.size ();
832  bool multiple_files = (file_cnt > 1);
833 
834  for (auto it = rows.begin (); it != rows.end (); it++)
835  {
836  QModelIndex index = *it;
837 
838  QFileInfo info = m_file_system_model->fileInfo (index);
839 
840  QMessageBox::StandardButton dlg_answer;
841  if (multiple_files)
842  if (it == rows.begin ())
843  {
844  dlg_answer = QMessageBox::question (this,
845  tr ("Delete file/directory"),
846  tr ("Are you sure you want to delete all %1 selected files?\n").arg (file_cnt),
847  QMessageBox::Yes | QMessageBox::No);
848  if (dlg_answer != QMessageBox::Yes)
849  return;
850  }
851  else
852  dlg_answer = QMessageBox::Yes;
853  else
854  {
855  dlg_answer = QMessageBox::question (this,
856  tr ("Delete file/directory"),
857  tr ("Are you sure you want to delete\n")
858  + info.filePath (),
859  QMessageBox::Yes | QMessageBox::No);
860  }
861 
862  if (dlg_answer == QMessageBox::Yes)
863  {
864  if (info.isDir ())
865  {
866  // see if directory is empty
867  QDir path (info.absoluteFilePath ());
868  QList<QFileInfo> fileLst = path.entryInfoList (
869  QDir::Hidden | QDir::AllEntries |
870  QDir::NoDotAndDotDot | QDir::System);
871 
872  if (fileLst.count () != 0)
873  QMessageBox::warning (this, tr ("Delete file/directory"),
874  tr ("Can not delete a directory that is not empty"));
875  else
876  m_file_system_model->rmdir (index);
877  }
878  else
879  {
880  // Close the file in the editor if open
881  emit file_remove_signal (info.filePath (), QString ());
882  // Remove the file.
883  bool st = m_file_system_model->remove (index);
884  if (! st)
885  {
886  QMessageBox::warning (this, tr ("Deletion error"),
887  tr ("Could not delete file \"%1\".").
888  arg (info.filePath ()));
889  // Reload the old file
890  }
891  emit file_renamed_signal (st);
892  }
893 
894  m_file_system_model->revert ();
895 
896  }
897  }
898 }
899 
900 // Get the currently selected files/dirs and return their file info
901 // in a list.
903 {
904  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
905  QModelIndexList rows = m->selectedRows ();
906 
907  QList<QFileInfo> infos;
908 
909  for (auto it = rows.begin (); it != rows.end (); it++)
910  {
911  QModelIndex index = *it;
912 
913  QFileInfo info = m_file_system_model->fileInfo (index);
914 
915  if (info.exists () &&
916  ((dir & info.isDir ()) || (! dir && info.isFile ())))
917  infos.append (info);
918  }
919 
920  return infos;
921 }
922 
924 {
925  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
926  QModelIndexList rows = m->selectedRows ();
927 
928  if (rows.size () > 0)
929  {
930  QModelIndex index = rows[0];
931 
932  QFileInfo info = m_file_system_model->fileInfo (index);
933  QString parent_dir = info.filePath ();
934 
935  process_new_file (parent_dir);
936  }
937 }
938 
940 {
941  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
942  QModelIndexList rows = m->selectedRows ();
943 
944  if (rows.size () > 0)
945  {
946  QModelIndex index = rows[0];
947 
948  QFileInfo info = m_file_system_model->fileInfo (index);
949  QString parent_dir = info.filePath ();
950 
951  process_new_dir (parent_dir);
952  }
953 }
954 
956 {
958 
959  if (infos.length () > 0 && infos.first ().isDir ())
960  process_set_current_dir (infos.first ().absoluteFilePath ());
961 }
962 
963 void files_dock_widget::contextmenu_add_to_path (bool, bool rm, bool subdirs)
964 {
966 
967  QStringList dir_list;
968 
969  for (int i = 0; i < infos.length (); i++)
970  dir_list.append (infos.at (i).absoluteFilePath ());
971 
972  if (infos.length () > 0)
973  emit modify_path_signal (dir_list, rm, subdirs);
974 }
975 
977 {
978  contextmenu_add_to_path (true, false, true);
979 }
980 
982 {
983  contextmenu_add_to_path (true, true, false);
984 }
985 
987 {
988  contextmenu_add_to_path (true, true, true);
989 }
990 
992 {
993  QItemSelectionModel *m = m_file_tree_view->selectionModel ();
994  QModelIndexList rows = m->selectedRows ();
995 
996  if (rows.size () > 0)
997  {
998  QModelIndex index = rows[0];
999 
1000  QFileInfo info = m_file_system_model->fileInfo (index);
1001 
1002  if (info.isDir ())
1003  {
1004  process_find_files (info.absoluteFilePath ());
1005  }
1006  }
1007 }
1008 
1010 {
1011  // QSettings pointer is checked before emitting.
1012 
1013  int size_idx = settings->value (global_icon_size).toInt ();
1014  size_idx = (size_idx > 0) - (size_idx < 0) + 1; // Make valid index from 0 to 2
1015 
1016  QStyle *st = style ();
1017  int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
1018  m_navigation_tool_bar->setIconSize (QSize (icon_size, icon_size));
1019 
1020  // filenames are always shown, other columns can be hidden by settings
1021  for (int i = 0; i < 3; i++)
1022  m_file_tree_view->setColumnHidden (i + 1,
1023  ! settings->value (m_columns_shown_keys.at (i),false).toBool ());
1024 
1025  QDir::Filters current_filter = m_file_system_model->filter ();
1026  if (settings->value (m_columns_shown_keys.at (3), false).toBool ())
1027  m_file_system_model->setFilter (current_filter | QDir::Hidden);
1028  else
1029  m_file_system_model->setFilter (current_filter & (~QDir::Hidden));
1030 
1031  m_file_tree_view->setAlternatingRowColors
1032  (settings->value (m_columns_shown_keys.at (4),true).toBool ());
1034 
1035  // enable the buttons to sync octave/browser dir
1036  // only if this is not done by default
1038  = settings->value (fb_sync_octdir).toBool ();
1041 
1042  // If m_sync_octave_dir is enabled, then we want the file browser to
1043  // update to match the current working directory of the
1044  // interpreter. We don't want to queue any signal to change the
1045  // interpreter's current working directory. In this case, we just
1046  // want the GUI to match the state of the interpreter.
1047 
1048  if (m_sync_octave_dir)
1050 }
1051 
1053 {
1054  QString dir = QString::fromStdString (sys::env::get_home_directory ());
1055 
1056  if (dir.isEmpty ())
1057  dir = QDir::homePath ();
1058 
1059  set_current_directory (dir);
1060 }
1061 
1063 {
1064  // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
1065  int opts = QFileDialog::ShowDirsOnly;
1067  gui_settings *settings = rmgr.get_settings ();
1068  if (! settings->value (global_use_native_dialogs).toBool ())
1069  opts |= QFileDialog::DontUseNativeDialog;
1070 
1071  QString dir = QFileDialog::getExistingDirectory (this,
1072  tr ("Set directory of file browser"),
1073  m_file_system_model->rootPath (),
1074  QFileDialog::Option (opts));
1075  set_current_directory (dir);
1076 }
1077 
1079 {
1080  process_find_files (m_file_system_model->rootPath ());
1081 }
1082 
1084 {
1085  process_new_dir (m_file_system_model->rootPath ());
1086 }
1087 
1089 {
1090  process_new_file (m_file_system_model->rootPath ());
1091 }
1092 
1093 void files_dock_widget::process_new_file (const QString& parent_dir)
1094 {
1095  bool ok;
1096 
1097  QString name = QInputDialog::getText (this, tr ("Create File"),
1098  tr ("Create file in\n", "String ends with \\n!") + parent_dir,
1099  QLineEdit::Normal,
1100  tr ("New File.txt"), &ok);
1101  if (ok && name.length () > 0)
1102  {
1103  name = parent_dir + '/' + name;
1104 
1105  QFile file (name);
1106  file.open (QIODevice::WriteOnly);
1107  m_file_system_model->revert ();
1108  }
1109 }
1110 
1111 void files_dock_widget::process_new_dir (const QString& parent_dir)
1112 {
1113  bool ok;
1114 
1115  QString name = QInputDialog::getText (this, tr ("Create Directory"),
1116  tr ("Create folder in\n", "String ends with \\n!") + parent_dir,
1117  QLineEdit::Normal,
1118  tr ("New Directory"), &ok);
1119  if (ok && name.length () > 0)
1120  {
1121  QDir dir (parent_dir);
1122  dir.mkdir (name);
1123  m_file_system_model->revert ();
1124  }
1125 }
1126 
1128 {
1129  emit displayed_directory_changed (dir);
1130 }
1131 
1132 void files_dock_widget::process_find_files (const QString& dir)
1133 {
1134  emit find_files_signal (dir);
1135 }
1136 
1138 {
1139  if (m_file_tree_view->hasFocus ())
1141  if (m_current_directory->hasFocus ())
1142  {
1143  QClipboard *clipboard = QApplication::clipboard ();
1144 
1145  QLineEdit *edit = m_current_directory->lineEdit ();
1146  if (edit && edit->hasSelectedText ())
1147  {
1148  clipboard->setText (edit->selectedText ());
1149  }
1150  }
1151 }
1152 
1154 {
1155  if (m_current_directory->hasFocus ())
1156  {
1157  QClipboard *clipboard = QApplication::clipboard ();
1158  QString str = clipboard->text ();
1159  QLineEdit *edit = m_current_directory->lineEdit ();
1160  if (edit && str.length () > 0)
1161  edit->insert (str);
1162  }
1163 }
1164 
1166 {
1167  if (m_file_tree_view->hasFocus ())
1168  m_file_tree_view->selectAll ();
1169  if (m_current_directory->hasFocus ())
1170  {
1171  QLineEdit *edit = m_current_directory->lineEdit ();
1172  if (edit)
1173  {
1174  edit->selectAll ();
1175  }
1176  }
1177 }
1178 
OCTAVE_END_NAMESPACE(octave)
~FileTreeViewer(void)=default
void mousePressEvent(QMouseEvent *e)
FileTreeViewer(QWidget *p)
void setEditorData(QWidget *editor, const QModelIndex &index) const override
RenameItemDelegate(QObject *parent=nullptr)
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
bool setData(const QModelIndex &idx, const QVariant &value, int role) override
file_system_model(files_dock_widget *p)
~file_system_model()=default
void display_rename_failed_message(const QString &old_name, const QString &new_name)
Dock widget to display files in the current directory.
void open_file(const QString &fileName)
Emitted, whenever the user requested to open a file.
void contextmenu_open_in_app(bool)
Context menu actions.
void item_double_clicked(const QModelIndex &index)
Slot for handling a change in directory via double click.
void process_new_file(const QString &parent_name)
Process new file/directory actions.
QAction * m_sync_browser_directory_action
void contextmenu_load(bool)
Context menu actions.
void popdownmenu_findfiles(bool)
Popdown menu options.
QList< QVariant > m_columns_shown_defs
void process_find_files(const QString &dir_name)
void set_current_directory(const QString &dir)
Sets the current directory being displayed.
bool m_sync_octave_dir
Flag if syncing with Octave.
void accept_directory_line_edit(void)
Accepts user input a the line edit for the current directory.
void toggle_header(int col)
void popdownmenu_newfile(bool)
Popdown menu options.
void file_renamed_signal(bool)
Emitted, when a file or directory is renamed.
void displayed_directory_changed(const QString &dir)
Emitted, whenever the currently displayed directory changed.
void open_item_in_app(const QModelIndex &index)
void load_file_signal(const QString &fileName)
Emitted, whenever the user requested to load a file in the text editor.
void contextmenu_add_to_path_subdirs(bool)
Context menu actions.
void contextmenu_open(bool)
Context menu actions.
void run_file_signal(const QFileInfo &info)
Emitted, whenever the user requested to run a file.
void change_directory_up(void)
Slot for handling the up-directory button in the toolbar.
void headercontextmenu_requested(const QPoint &pos)
QToolBar * m_navigation_tool_bar
Variables for the actions.
void contextmenu_run(bool)
Context menu actions.
QComboBox * m_current_directory
The file system view.
void selectAll()
Inherited from octave_doc_widget.
void contextmenu_newdir(bool)
Context menu actions.
void do_sync_browser_directory(void)
Slot for handling the sync browser directory button in the toolbar.
void modify_path_signal(const QStringList &dir_list, bool rm, bool subdirs)
Emitted, when the path has to be modified.
void contextmenu_rm_from_path_subdirs(bool)
Context menu actions.
QStringList m_columns_shown_keys
QList< QFileInfo > get_selected_items_info(bool)
Get currently selected QFileInfo object.
void process_set_current_dir(const QString &parent_name)
Process setting current dir or find in files.
void update_octave_directory(const QString &dir)
Set the internal variable that holds the actual octave variable.
void popdownmenu_newdir(bool)
Popdown menu options.
QString m_octave_dir
The actual Octave directory.
void display_directory(const QString &dir, bool set_octave_dir=true)
set a new directory or open a file
void contextmenu_newfile(bool)
Context menu actions.
void copyClipboard()
Inherited from octave_doc_widget.
void contextmenu_add_to_path(bool, bool rm=false, bool subdirs=false)
Context menu actions.
void contextmenu_findfiles(bool)
Context menu actions.
QSignalMapper * m_sig_mapper
void process_new_dir(const QString &parent_name)
void popdownmenu_search_dir(bool)
Popdown menu options.
QAction * m_sync_octave_directory_action
void contextmenu_rm_from_path(bool)
Context menu actions.
void open_any_signal(const QString &fileName)
Emitted, whenever the user requested to open an unknown type file.
void contextmenu_open_in_editor(bool)
Context menu actions.
void notice_settings(const gui_settings *settings)
Tells the widget to react on changed settings.
void contextmenu_setcurrentdir(bool)
Context menu actions.
void popdownmenu_home(bool)
Popdown menu options.
void contextmenu_requested(const QPoint &pos)
Context menu wanted.
void do_sync_octave_directory(void)
Slot for handling the sync octave directory button in the toolbar.
QFileSystemModel * m_file_system_model
The file system model.
void find_files_signal(const QString &startdir)
Emitted, whenever wants to search for a file .
void file_remove_signal(const QString &old_name, const QString &new_name)
Emitted, whenever the user removes or renames a file.
void contextmenu_delete(bool)
Context menu actions.
files_dock_widget(QWidget *parent, base_qobject &oct_qobj)
QTreeView * m_file_tree_view
The file system view.
void contextmenu_rename(bool)
Context menu actions.
QStringList m_columns_shown
void pasteClipboard()
Inherited from octave_doc_widget.
void contextmenu_copy_selection(bool)
Context menu actions.
base_qobject & m_octave_qobj
void set_title(const QString &)
virtual void save_settings(void)
void make_window(bool widget_was_dragged=false)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool octave_only=false, const QString &icon_alt_name=QString())
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void warning(const char *fmt,...)
Definition: error.cc:1054
void message(const char *name, const char *fmt,...)
Definition: error.cc:947
const gui_pref fb_mru_list("filesdockwidget/mru_dir_list", QVariant(QStringList()))
const gui_pref fb_column_state("filesdockwidget/column_state", QVariant())
const gui_pref fb_show_date("filesdockwidget/showLastModified", QVariant(false))
const gui_pref fb_sync_octdir("filesdockwidget/sync_octave_directory", QVariant(true))
const gui_pref fb_sort_order("filesdockwidget/sort_files_by_order", QVariant(Qt::AscendingOrder))
const gui_pref fb_sort_column("filesdockwidget/sort_files_by_column", QVariant(0))
const gui_pref fb_show_type("filesdockwidget/showFileType", QVariant(false))
const gui_pref fb_show_hidden("filesdockwidget/showHiddenFiles", QVariant(false))
const gui_pref fb_startup_dir("filesdockwidget/startup_dir", QVariant(QString()))
const gui_pref fb_show_size("filesdockwidget/showFileSize", QVariant(false))
const gui_pref fb_txt_file_ext("filesdockwidget/txt_file_extensions", QVariant("m;c;cc;cpp;h;txt"))
const gui_pref fb_restore_last_dir("filesdockwidget/restore_last_dir", QVariant(false))
const gui_pref fb_show_altcol("filesdockwidget/useAlternatingRowColors", QVariant(true))
const QStyle::PixelMetric global_icon_sizes[3]
const gui_pref global_use_native_dialogs("use_native_file_dialogs", QVariant(true))
const gui_pref global_icon_size("toolbar_icon_size", QVariant(0))
T octave_idx_type m
Definition: mx-inlines.cc:773
QString fromStdString(const std::string &s)
const QString key
const QVariant def