Mercurial > octave
view libgui/src/files-dock-widget.cc @ 33499:0f2257136d9c
file browser column width only stored by header state (bug #65030)
* files-dock-widget.cc (files_dock_widget::files_dock_widget):
initialize new boolean variables;
(files_dock_widget::toggle_header): get current column visibility from
QTreeView, not from settings, set new boolean variables if only header
related settings have to be updated in notice_settings
(files_dock_widget::headercontextmenu_requested): get current column
visibility from QTreeView, not from settings;
(files_dock_widget::notice_settings): save current header state if it
is not the first run where the header would be the default one, return
after header related settings if only these are requested to be updated,
restore header state after other settings are updated;
* files-dock-widget.h; new class variables m_first, m_header_settings_only
author | Torsten Lilge <ttl-octave@mailbox.org> |
---|---|
date | Wed, 01 May 2024 20:57:06 +0200 |
parents | fa2dfa9152c8 |
children | 952bb22874fd |
line wrap: on
line source
//////////////////////////////////////////////////////////////////////// // // Copyright (C) 2011-2024 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or <https://octave.org/copyright/>. // // This file is part of Octave. // // Octave is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // <https://www.gnu.org/licenses/>. // //////////////////////////////////////////////////////////////////////// #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include <map> #include <QApplication> #include <QClipboard> #include <QCompleter> #include <QDebug> #include <QDesktopServices> #include <QFileDialog> #include <QFileInfo> #include <QHeaderView> #include <QInputDialog> #include <QLineEdit> #include <QMenu> #include <QMessageBox> #include <QMimeDatabase> #include <QMimeType> #include <QProcess> #include <QSizePolicy> #include <QStyledItemDelegate> #include <QTimer> #include <QToolButton> #include <QUrl> #include "files-dock-widget.h" #include "gui-preferences-fb.h" #include "gui-preferences-global.h" #include "gui-settings.h" #include "qt-interpreter-events.h" #include "oct-env.h" OCTAVE_BEGIN_NAMESPACE(octave) class FileTreeViewer : public QTreeView { public: FileTreeViewer (QWidget *p) : QTreeView (p) { } ~FileTreeViewer () = default; void mousePressEvent (QMouseEvent *e) { if (e->button () != Qt::RightButton) QTreeView::mousePressEvent (e); } }; // to have file renamed in the file tree, it has to be renamed in // QFileSystemModel::setData. // For the editor to behave correctly, some signals must be sent before // and after the rename class file_system_model : public QFileSystemModel { public: file_system_model (files_dock_widget *p) : QFileSystemModel (p) {} ~file_system_model () = default; bool setData (const QModelIndex &idx, const QVariant &value, int role) override { if (!idx.isValid () || idx.column () != 0 || role != Qt::EditRole || (flags (idx) & Qt::ItemIsEditable) == 0) { return false; } QString new_name = value.toString (); QString old_name = idx.data ().toString (); if (new_name == old_name) return true; if (new_name.isEmpty () || QDir::toNativeSeparators (new_name).contains (QDir::separator ())) { display_rename_failed_message (old_name, new_name); return false; } auto parent_dir = QDir (filePath (parent (idx))); files_dock_widget *fdw = static_cast<files_dock_widget *>(parent ()); fdw->file_remove_signal (parent_dir.filePath (old_name), parent_dir.filePath (new_name)); if (!parent_dir.rename (old_name, new_name)) { display_rename_failed_message (old_name, new_name); fdw->file_renamed_signal (false); return false; } fdw->file_renamed_signal (true); emit fileRenamed (parent_dir.absolutePath (), old_name, new_name); revert (); return true; } private: void display_rename_failed_message (const QString &old_name, const QString &new_name) { const QString message = files_dock_widget::tr ("Could not rename file \"%1\" to \"%2\".") .arg (old_name) .arg (new_name); QMessageBox::information (static_cast<QWidget *> (parent ()), QFileSystemModel::tr ("Invalid filename"), message, QMessageBox::Ok); } }; // Delegate to improve ergonomy of file renaming by pre-selecting the text // before the extension. class RenameItemDelegate : public QStyledItemDelegate { public: RenameItemDelegate (QObject *parent = nullptr) : QStyledItemDelegate{ parent } { } void setEditorData (QWidget *editor, const QModelIndex &index) const override { QLineEdit *line_edit = qobject_cast<QLineEdit *> (editor); if (!line_edit) { QStyledItemDelegate::setEditorData (editor, index); return; } QString filename = index.data (Qt::EditRole).toString (); int select_len = filename.indexOf (QChar ('.')); if (select_len == -1) select_len = filename.size (); line_edit->setText (filename); // Qt calls QLineEdit::selectAll after this function is called, so to // actually restrict the selection, we have to post the modification at // the end of the event loop. // QTimer allows this easily with 0 as timeout. QTimer::singleShot (0, [line_edit, select_len] () { line_edit->setSelection (0, select_len); }); } }; class cache_file_icon_provider : public QFileIconProvider { public: cache_file_icon_provider () { m_null_icon = QIcon (); m_file_icon = m_file_icon_provider.icon (QFileIconProvider::File); m_folder_icon = m_file_icon_provider.icon (QFileIconProvider::Folder); } QIcon icon (IconType ict) const { if (ict == QFileIconProvider::File) return m_file_icon; else if (ict == QFileIconProvider::Folder) return m_folder_icon; else return m_null_icon; } QIcon icon (const QFileInfo &fi) const { static bool no_platform_theme = QIcon::themeName ().isEmpty (); if (no_platform_theme) return m_file_icon_provider.icon (fi); QMimeType mime_type = m_db.mimeTypeForFile (fi.absoluteFilePath ()); QString icon_name = mime_type.iconName (); auto mime_type_iter = m_icon_cache.find (icon_name); if (mime_type_iter != m_icon_cache.end ()) return mime_type_iter->second; QIcon icon = QIcon::fromTheme (icon_name); m_icon_cache.insert ({icon_name, icon}); return icon; } private: QIcon m_null_icon; QIcon m_file_icon; QIcon m_folder_icon; QFileIconProvider m_file_icon_provider; QMimeDatabase m_db; static std::map<QString, QIcon> m_icon_cache; }; std::map<QString, QIcon> cache_file_icon_provider::m_icon_cache; files_dock_widget::files_dock_widget (QWidget *p) : octave_dock_widget ("FilesDockWidget", p), m_first (true), m_header_settings_only (false) { set_title (tr ("File Browser")); setToolTip (tr ("Browse your files")); m_sig_mapper = nullptr; m_columns_shown = QStringList (); m_columns_shown.append (tr ("File size")); m_columns_shown.append (tr ("File type")); m_columns_shown.append (tr ("Date modified")); m_columns_shown.append (tr ("Show hidden")); m_columns_shown.append (tr ("Alternating row colors")); m_columns_shown_keys = QStringList (); m_columns_shown_keys.append (fb_show_size.settings_key ()); m_columns_shown_keys.append (fb_show_type.settings_key ()); m_columns_shown_keys.append (fb_show_date.settings_key ()); m_columns_shown_keys.append (fb_show_hidden.settings_key ()); m_columns_shown_keys.append (fb_show_altcol.settings_key ()); m_columns_shown_defs = QList <QVariant> (); m_columns_shown_defs.append (fb_show_size.def ()); m_columns_shown_defs.append (fb_show_type.def ()); m_columns_shown_defs.append (fb_show_date.def ()); m_columns_shown_defs.append (fb_show_hidden.def ()); m_columns_shown_defs.append (fb_show_altcol.def ()); QWidget *container = new QWidget (this); setWidget (container); // Create a toolbar m_navigation_tool_bar = new QToolBar ("", container); m_navigation_tool_bar->setAllowedAreas (Qt::TopToolBarArea); m_navigation_tool_bar->setMovable (false); m_current_directory = new QComboBox (m_navigation_tool_bar); m_current_directory->setToolTip (tr ("Enter the path or filename")); m_current_directory->setEditable (true); m_current_directory->setMaxCount (MaxMRUDirs); m_current_directory->setInsertPolicy (QComboBox::NoInsert); m_current_directory->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon); QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred); m_current_directory->setSizePolicy (sizePol); gui_settings settings; QAction *directory_up_action = new QAction (settings.icon ("folder-up", false, "go-up"), "", m_navigation_tool_bar); directory_up_action->setToolTip (tr ("One directory up")); m_sync_browser_directory_action = new QAction (settings.icon ("go-first"), tr ("Show Octave directory"), m_navigation_tool_bar); m_sync_browser_directory_action->setToolTip (tr ("Go to current Octave directory")); m_sync_browser_directory_action->setEnabled (false); m_sync_octave_directory_action = new QAction (settings.icon ("go-last"), tr ("Set Octave directory"), m_navigation_tool_bar); m_sync_octave_directory_action->setToolTip (tr ("Set Octave directory to current browser directory")); m_sync_octave_directory_action->setEnabled (false); QToolButton *popdown_button = new QToolButton (); popdown_button->setToolTip (tr ("Actions on current directory")); QMenu *popdown_menu = new QMenu (); popdown_menu->addAction (settings.icon ("user-home"), tr ("Show Home Directory"), this, SLOT (popdownmenu_home (bool))); popdown_menu->addAction (m_sync_browser_directory_action); popdown_menu->addAction (m_sync_octave_directory_action); popdown_button->setMenu (popdown_menu); popdown_button->setPopupMode (QToolButton::InstantPopup); popdown_button->setDefaultAction (new QAction (settings.icon ("folder-settings", false, "applications-system"), "", m_navigation_tool_bar)); popdown_menu->addSeparator (); popdown_menu->addAction (settings.icon ("folder"), tr ("Set Browser Directory..."), this, &files_dock_widget::popdownmenu_search_dir); popdown_menu->addSeparator (); popdown_menu->addAction (settings.icon ("edit-find"), tr ("Find Files..."), this, &files_dock_widget::popdownmenu_findfiles); popdown_menu->addSeparator (); popdown_menu->addAction (settings.icon ("document-new"), tr ("New File..."), this, &files_dock_widget::popdownmenu_newfile); popdown_menu->addAction (settings.icon ("folder-new"), tr ("New Directory..."), this, &files_dock_widget::popdownmenu_newdir); m_navigation_tool_bar->addWidget (m_current_directory); m_navigation_tool_bar->addAction (directory_up_action); m_navigation_tool_bar->addWidget (popdown_button); connect (directory_up_action, &QAction::triggered, this, &files_dock_widget::change_directory_up); connect (m_sync_octave_directory_action, &QAction::triggered, this, &files_dock_widget::do_sync_octave_directory); connect (m_sync_browser_directory_action, &QAction::triggered, this, &files_dock_widget::do_sync_browser_directory); // Create the QFileSystemModel starting in the desired directory QDir startup_dir; // take current dir if (settings.bool_value (fb_restore_last_dir)) { // restore last dir from previous session QStringList last_dirs = settings.value (fb_mru_list.settings_key ()).toStringList (); if (last_dirs.length () > 0) startup_dir = QDir (last_dirs.at (0)); // last dir in previous session } else if (! settings.string_value (fb_startup_dir).isEmpty ()) { // do not restore but there is a startup dir configured startup_dir = QDir (settings.value (fb_startup_dir.settings_key ()).toString ()); } if (! startup_dir.exists ()) { // the configured startup dir does not exist, take actual one startup_dir = QDir (); } m_file_system_model = new file_system_model (this); m_file_system_model->setResolveSymlinks (false); m_file_system_model->setFilter (QDir::System | QDir::NoDotAndDotDot | QDir::AllEntries); QModelIndex rootPathIndex = m_file_system_model->setRootPath (startup_dir.absolutePath ()); m_file_icon_provider = new cache_file_icon_provider (); m_file_system_model->setIconProvider (m_file_icon_provider); // Attach the model to the QTreeView and set the root index m_file_tree_view = new FileTreeViewer (container); m_file_tree_view->setSelectionMode (QAbstractItemView::ExtendedSelection); m_file_tree_view->setModel (m_file_system_model); m_file_tree_view->setRootIndex (rootPathIndex); m_file_tree_view->setSortingEnabled (true); m_file_tree_view->setAlternatingRowColors (true); m_file_tree_view->setAnimated (true); m_file_tree_view->setToolTip (tr ("Double-click to open file/folder, right click for alternatives")); // allow renaming directly in the tree view with // m_file_tree_view->edit (index) m_file_system_model->setReadOnly (false); // delegate to improve rename ergonomy by pre-selecting text up to the // extension auto *rename_delegate = new RenameItemDelegate (this); m_file_tree_view->setItemDelegateForColumn (0, rename_delegate); // prevent the tree view to override Octave's double-click behavior m_file_tree_view->setEditTriggers (QAbstractItemView::NoEditTriggers); // create the rename action (that will be added to context menu) // and associate to F2 key shortcut m_rename_action = new QAction (tr ("Rename..."), this); m_rename_action->setShortcut (Qt::Key_F2); m_rename_action->setShortcutContext (Qt::WidgetWithChildrenShortcut); connect (m_rename_action, &QAction::triggered, this, &files_dock_widget::contextmenu_rename); addAction (m_rename_action); // get sort column and order as well as column state (order and width) m_file_tree_view->sortByColumn (settings.int_value (fb_sort_column), static_cast<Qt::SortOrder> (settings.uint_value (fb_sort_order))); // FIXME: use value<Qt::SortOrder> instead of static cast after // dropping support of Qt 5.4 // Set header properties for sorting m_file_tree_view->header ()->setSectionsClickable (true); m_file_tree_view->header ()->setSectionsMovable (true); m_file_tree_view->header ()->setSortIndicatorShown (true); QStringList mru_dirs = settings.value (fb_mru_list.settings_key ()).toStringList (); m_current_directory->addItems (mru_dirs); m_current_directory->setEditText (m_file_system_model->fileInfo (rootPathIndex). absoluteFilePath ()); connect (m_file_tree_view, &FileTreeViewer::activated, this, &files_dock_widget::item_double_clicked); // add context menu to tree_view m_file_tree_view->setContextMenuPolicy (Qt::CustomContextMenu); connect (m_file_tree_view, &FileTreeViewer::customContextMenuRequested, this, &files_dock_widget::contextmenu_requested); m_file_tree_view->header ()->setContextMenuPolicy (Qt::CustomContextMenu); connect (m_file_tree_view->header (), &QHeaderView::customContextMenuRequested, this, &files_dock_widget::headercontextmenu_requested); // Layout the widgets vertically with the toolbar on top QVBoxLayout *vbox_layout = new QVBoxLayout (); vbox_layout->setSpacing (0); vbox_layout->addWidget (m_navigation_tool_bar); vbox_layout->addWidget (m_file_tree_view); vbox_layout->setContentsMargins (1, 1, 1, 1); container->setLayout (vbox_layout); // FIXME: Add right-click contextual menus for copying, pasting, // deleting files (and others). connect (m_current_directory->lineEdit (), &QLineEdit::returnPressed, this, &files_dock_widget::accept_directory_line_edit); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) connect (m_current_directory, &QComboBox::textActivated, this, &files_dock_widget::set_current_directory); #else connect (m_current_directory, SIGNAL (activated (const QString&)), this, SLOT (set_current_directory (const QString&))); #endif QCompleter *completer = new QCompleter (m_file_system_model, this); m_current_directory->setCompleter (completer); setFocusProxy (m_current_directory); m_sync_octave_dir = true; // default, overwritten with notice_settings () m_octave_dir = ""; if (! p) make_window (); // Initialize column order and width of the file browser. From this post, // https://www.qtcentre.org/threads/26675-QTableView-saving-restoring-columns-widths // this might fail if done directly in the constructor. This effect shows // up in the GUI since Qt 6.6.x. As a solution, the following timer ensures // that the header is restored when the event loop is idle. QTimer::singleShot (0, this, SLOT(restore_header_state ())); } void files_dock_widget::restore_header_state () { gui_settings settings; if (settings.contains (fb_column_state.settings_key ())) m_file_tree_view->header ()->restoreState (settings.value (fb_column_state.settings_key ()).toByteArray ()); } void files_dock_widget::save_settings () { gui_settings settings; int sort_column = m_file_tree_view->header ()->sortIndicatorSection (); Qt::SortOrder sort_order = m_file_tree_view->header ()->sortIndicatorOrder (); settings.setValue (fb_sort_column.settings_key (), sort_column); settings.setValue (fb_sort_order.settings_key (), sort_order); settings.setValue (fb_column_state.settings_key (), m_file_tree_view->header ()->saveState ()); QStringList dirs; for (int i=0; i< m_current_directory->count (); i++) { dirs.append (m_current_directory->itemText (i)); } settings.setValue (fb_mru_list.settings_key (), dirs); settings.sync (); octave_dock_widget::save_settings (); if (m_sig_mapper) delete m_sig_mapper; } void files_dock_widget::item_double_clicked (const QModelIndex& index) { // Retrieve the file info associated with the model index. QFileInfo fileInfo = m_file_system_model->fileInfo (index); set_current_directory (fileInfo.absoluteFilePath ()); } void files_dock_widget::set_current_directory (const QString& dir) { display_directory (dir); } void files_dock_widget::accept_directory_line_edit () { display_directory (m_current_directory->currentText ()); } void files_dock_widget::change_directory_up () { QDir dir = QDir (m_file_system_model->filePath (m_file_tree_view->rootIndex ())); dir.cdUp (); display_directory (dir.absolutePath ()); } void files_dock_widget::do_sync_octave_directory () { QDir dir = QDir (m_file_system_model->filePath (m_file_tree_view->rootIndex ())); emit displayed_directory_changed (dir.absolutePath ()); } void files_dock_widget::do_sync_browser_directory () { display_directory (m_octave_dir, false); // false: no sync of octave dir } void files_dock_widget::update_octave_directory (const QString& dir) { m_octave_dir = dir; if (m_sync_octave_dir) display_directory (m_octave_dir, false); // false: no sync of octave dir } void files_dock_widget::display_directory (const QString& dir, bool set_octave_dir) { QFileInfo fileInfo (dir); if (fileInfo.exists ()) { if (fileInfo.isDir ()) { m_file_tree_view->setRootIndex (m_file_system_model-> index (fileInfo.absoluteFilePath ())); m_file_system_model->setRootPath (fileInfo.absoluteFilePath ()); if (m_sync_octave_dir && set_octave_dir) process_set_current_dir (fileInfo.absoluteFilePath ()); // see if it's in the list, and if it is, // remove it and then put at top of the list int index = m_current_directory->findText (fileInfo.absoluteFilePath ()); if (index != -1) { m_current_directory->removeItem (index); } m_current_directory->insertItem (0, fileInfo.absoluteFilePath ()); m_current_directory->setCurrentIndex (0); } else { QString abs_fname = fileInfo.absoluteFilePath (); QString suffix = fileInfo.suffix ().toLower (); gui_settings settings; QString ext = settings.string_value (fb_txt_file_ext); #if defined (HAVE_QT_SPLITBEHAVIOR_ENUM) QStringList extensions = ext.split (";", Qt::SkipEmptyParts); #else QStringList extensions = ext.split (";", QString::SkipEmptyParts); #endif if (QFile::exists (abs_fname)) { if (extensions.contains (suffix)) emit open_file (fileInfo.absoluteFilePath ()); else emit open_any_signal (abs_fname); } } } } void files_dock_widget::open_item_in_app (const QModelIndex& index) { // Retrieve the file info associated with the model index. QFileInfo fileInfo = m_file_system_model->fileInfo (index); QString file = fileInfo.absoluteFilePath (); QDesktopServices::openUrl (QUrl::fromLocalFile (file)); } void files_dock_widget::toggle_header (int col) { gui_settings settings; if (col <= 2) { // Toggle column visibility m_file_tree_view->setColumnHidden (col + 1, ! m_file_tree_view->isColumnHidden (col +1)); } else { // Other actions depending on new settings QString key = m_columns_shown_keys.at (col); bool active = settings.value (key, false).toBool (); // Toggle the settings in the settings file settings.setValue (key, ! active); settings.sync (); // Reload header related settings only m_header_settings_only = true; notice_settings (); } } void files_dock_widget::headercontextmenu_requested (const QPoint& mpos) { QMenu menu (this); if (m_sig_mapper) delete m_sig_mapper; m_sig_mapper = new QSignalMapper (this); gui_settings settings; for (int i = 0; i < m_columns_shown.size (); i++) { QAction *action = menu.addAction (m_columns_shown.at (i), m_sig_mapper, SLOT (map ())); m_sig_mapper->setMapping (action, i); action->setCheckable (true); if (i <= 2) { // Column visibility action->setChecked (! m_file_tree_view->isColumnHidden (i +1)); } else { // Other actions depending on settings action->setChecked (settings.value (m_columns_shown_keys.at (i), m_columns_shown_defs.at (i)).toBool ()); } } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) connect (m_sig_mapper, &QSignalMapper::mappedInt, this, &files_dock_widget::toggle_header); #else connect (m_sig_mapper, SIGNAL (mapped (int)), this, SLOT (toggle_header (int))); #endif menu.exec (m_file_tree_view->mapToGlobal (mpos)); } void files_dock_widget::contextmenu_requested (const QPoint& mpos) { QMenu menu (this); QModelIndex index = m_file_tree_view->indexAt (mpos); if (index.isValid ()) { QFileInfo info = m_file_system_model->fileInfo (index); QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList sel = m->selectedRows (); // check if item at mouse position is seleccted if (! sel.contains (index)) { // is not selected -> clear actual selection and select this item m->setCurrentIndex (index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); } gui_settings settings; // construct the context menu depending on item menu.addAction (settings.icon ("document-open"), tr ("Open"), this, &files_dock_widget::contextmenu_open); if (info.isDir ()) { menu.addAction (tr ("Open in System File Explorer"), this, &files_dock_widget::contextmenu_open_in_app); } if (info.isFile ()) menu.addAction (tr ("Open in Text Editor"), this, &files_dock_widget::contextmenu_open_in_editor); menu.addAction (tr ("Copy Selection to Clipboard"), this, &files_dock_widget::contextmenu_copy_selection); if (info.isFile () && info.suffix () == "m") menu.addAction (settings.icon ("media-playback-start"), tr ("Run"), this, &files_dock_widget::contextmenu_run); if (info.isFile ()) menu.addAction (tr ("Load Data"), this, &files_dock_widget::contextmenu_load); if (info.isDir ()) { menu.addSeparator (); menu.addAction (settings.icon ("go-first"), tr ("Set Current Directory"), this, &files_dock_widget::contextmenu_setcurrentdir); QMenu *add_path_menu = menu.addMenu (tr ("Add to Path")); add_path_menu->addAction (tr ("Selected Directories"), this, [this] (bool checked) { contextmenu_add_to_path (checked); }); add_path_menu->addAction (tr ("Selected Directories and Subdirectories"), this, &files_dock_widget::contextmenu_add_to_path_subdirs); QMenu *rm_path_menu = menu.addMenu (tr ("Remove from Path")); rm_path_menu->addAction (tr ("Selected Directories"), this, &files_dock_widget::contextmenu_rm_from_path); rm_path_menu->addAction (tr ("Selected Directories and Subdirectories"), this, &files_dock_widget::contextmenu_rm_from_path_subdirs); menu.addSeparator (); menu.addAction (settings.icon ("edit-find"), tr ("Find Files..."), this, &files_dock_widget::contextmenu_findfiles); } menu.addSeparator (); menu.addAction (m_rename_action); menu.addAction (settings.icon ("edit-delete"), tr ("Delete..."), this, &files_dock_widget::contextmenu_delete); if (info.isDir ()) { menu.addSeparator (); menu.addAction (settings.icon ("document-new"), tr ("New File..."), this, &files_dock_widget::contextmenu_newfile); menu.addAction (settings.icon ("folder-new"), tr ("New Directory..."), this, &files_dock_widget::contextmenu_newdir); } // show the menu menu.exec (m_file_tree_view->mapToGlobal (mpos)); } } void files_dock_widget::contextmenu_open (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); for (const auto& it : rows) { QFileInfo file = m_file_system_model->fileInfo (it); if (file.exists ()) display_directory (file.absoluteFilePath ()); } } void files_dock_widget::contextmenu_open_in_editor (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); for (const auto& it : rows) { QFileInfo file = m_file_system_model->fileInfo (it); if (file.exists ()) emit open_file (file.absoluteFilePath ()); } } void files_dock_widget::contextmenu_open_in_app (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); for (const auto& it : rows) open_item_in_app (it); } void files_dock_widget::contextmenu_copy_selection (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); QStringList selection; for (const auto& it : rows) { QFileInfo info = m_file_system_model->fileInfo (it); selection << info.fileName (); } QClipboard *clipboard = QApplication::clipboard (); clipboard->setText (selection.join ("\n")); } void files_dock_widget::contextmenu_load (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; QFileInfo info = m_file_system_model->fileInfo (index); emit load_file_signal (info.fileName ()); } } void files_dock_widget::contextmenu_run (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; QFileInfo info = m_file_system_model->fileInfo (index); emit run_file_signal (info); } } void files_dock_widget::contextmenu_rename (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; m_file_tree_view->edit (index); } } void files_dock_widget::contextmenu_delete (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); int file_cnt = rows.size (); bool multiple_files = (file_cnt > 1); for (auto it = rows.begin (); it != rows.end (); it++) { QModelIndex index = *it; QFileInfo info = m_file_system_model->fileInfo (index); QMessageBox::StandardButton dlg_answer; if (multiple_files) if (it == rows.begin ()) { dlg_answer = QMessageBox::question (this, tr ("Delete file/directory"), tr ("Are you sure you want to delete all %1 selected files?\n").arg (file_cnt), QMessageBox::Yes | QMessageBox::No); if (dlg_answer != QMessageBox::Yes) return; } else dlg_answer = QMessageBox::Yes; else { dlg_answer = QMessageBox::question (this, tr ("Delete file/directory"), tr ("Are you sure you want to delete\n") + info.filePath (), QMessageBox::Yes | QMessageBox::No); } if (dlg_answer == QMessageBox::Yes) { if (info.isDir ()) { // see if directory is empty QDir path (info.absoluteFilePath ()); QList<QFileInfo> fileLst = path.entryInfoList (QDir::Hidden | QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System); if (fileLst.count () != 0) QMessageBox::warning (this, tr ("Delete file/directory"), tr ("Can not delete a directory that is not empty")); else m_file_system_model->rmdir (index); } else { // Close the file in the editor if open emit file_remove_signal (info.filePath (), QString ()); // Remove the file. bool st = m_file_system_model->remove (index); if (! st) { QMessageBox::warning (this, tr ("Deletion error"), tr ("Could not delete file \"%1\"."). arg (info.filePath ())); // Reload the old file } emit file_renamed_signal (st); } m_file_system_model->revert (); } } } // Get the currently selected files/dirs and return their file info // in a list. QList<QFileInfo> files_dock_widget::get_selected_items_info (bool dir) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); QList<QFileInfo> infos; for (const auto& idx : rows) { QFileInfo info = m_file_system_model->fileInfo (idx); if (info.exists () && ((dir & info.isDir ()) || (! dir && info.isFile ()))) infos.append (info); } return infos; } void files_dock_widget::contextmenu_newfile (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; QFileInfo info = m_file_system_model->fileInfo (index); QString parent_dir = info.filePath (); process_new_file (parent_dir); } } void files_dock_widget::contextmenu_newdir (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; QFileInfo info = m_file_system_model->fileInfo (index); QString parent_dir = info.filePath (); process_new_dir (parent_dir); } } void files_dock_widget::contextmenu_setcurrentdir (bool) { QList<QFileInfo> infos = get_selected_items_info (true); if (infos.length () > 0 && infos.first ().isDir ()) process_set_current_dir (infos.first ().absoluteFilePath ()); } void files_dock_widget::contextmenu_add_to_path (bool, bool rm, bool subdirs) { QList<QFileInfo> infos = get_selected_items_info (true); QStringList dir_list; for (int i = 0; i < infos.length (); i++) dir_list.append (infos.at (i).absoluteFilePath ()); if (infos.length () > 0) emit modify_path_signal (dir_list, rm, subdirs); } void files_dock_widget::contextmenu_add_to_path_subdirs (bool) { contextmenu_add_to_path (true, false, true); } void files_dock_widget::contextmenu_rm_from_path (bool) { contextmenu_add_to_path (true, true, false); } void files_dock_widget::contextmenu_rm_from_path_subdirs (bool) { contextmenu_add_to_path (true, true, true); } void files_dock_widget::contextmenu_findfiles (bool) { QItemSelectionModel *m = m_file_tree_view->selectionModel (); QModelIndexList rows = m->selectedRows (); if (rows.size () > 0) { QModelIndex index = rows[0]; QFileInfo info = m_file_system_model->fileInfo (index); if (info.isDir ()) { process_find_files (info.absoluteFilePath ()); } } } void files_dock_widget::notice_settings () { gui_settings settings; if (m_first) m_first = false; else { // Save current state in case some settings are messing up the state settings.setValue (fb_column_state.settings_key (), m_file_tree_view->header ()->saveState ()); settings.sync (); } QDir::Filters current_filter = m_file_system_model->filter (); if (settings.value (m_columns_shown_keys.at (3), false).toBool ()) m_file_system_model->setFilter (current_filter | QDir::Hidden); else m_file_system_model->setFilter (current_filter & (~QDir::Hidden)); m_file_tree_view->setAlternatingRowColors (settings.value (m_columns_shown_keys.at (4), true).toBool ()); m_file_tree_view->setModel (m_file_system_model); // Done if only settings changed by toggle:header were requested if (m_header_settings_only) { m_header_settings_only = false; return; } int size_idx = settings.int_value (global_icon_size); size_idx = (size_idx > 0) - (size_idx < 0) + 1; // Make valid index from 0 to 2 QStyle *st = style (); int icon_size = st->pixelMetric (global_icon_sizes[size_idx]); m_navigation_tool_bar->setIconSize (QSize (icon_size, icon_size)); // enable the buttons to sync octave/browser dir // only if this is not done by default m_sync_octave_dir = settings.bool_value (fb_sync_octdir); m_sync_octave_directory_action->setEnabled (! m_sync_octave_dir); m_sync_browser_directory_action->setEnabled (! m_sync_octave_dir); // If m_sync_octave_dir is enabled, then we want the file browser to // update to match the current working directory of the // interpreter. We don't want to queue any signal to change the // interpreter's current working directory. In this case, we just // want the GUI to match the state of the interpreter. if (m_sync_octave_dir) do_sync_browser_directory (); // Restore column state in case the setting above somehow changed them QTimer::singleShot (0, this, SLOT(restore_header_state ())); } void files_dock_widget::popdownmenu_home (bool) { QString dir = QString::fromStdString (sys::env::get_home_directory ()); if (dir.isEmpty ()) dir = QDir::homePath (); set_current_directory (dir); } void files_dock_widget::popdownmenu_search_dir (bool) { // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved. int opts = QFileDialog::ShowDirsOnly; gui_settings settings; if (! settings.bool_value (global_use_native_dialogs)) opts |= QFileDialog::DontUseNativeDialog; QString dir = QFileDialog::getExistingDirectory (this, tr ("Set directory of file browser"), m_file_system_model->rootPath (), QFileDialog::Option (opts)); set_current_directory (dir); } void files_dock_widget::popdownmenu_findfiles (bool) { process_find_files (m_file_system_model->rootPath ()); } void files_dock_widget::popdownmenu_newdir (bool) { process_new_dir (m_file_system_model->rootPath ()); } void files_dock_widget::popdownmenu_newfile (bool) { process_new_file (m_file_system_model->rootPath ()); } void files_dock_widget::process_new_file (const QString& parent_dir) { bool ok; QString name = QInputDialog::getText (this, tr ("Create File"), tr ("Create file in\n", "String ends with \\n!") + parent_dir, QLineEdit::Normal, tr ("New File.txt"), &ok); if (ok && name.length () > 0) { name = parent_dir + '/' + name; QFile file (name); file.open (QIODevice::WriteOnly); m_file_system_model->revert (); } } void files_dock_widget::process_new_dir (const QString& parent_dir) { bool ok; QString name = QInputDialog::getText (this, tr ("Create Directory"), tr ("Create folder in\n", "String ends with \\n!") + parent_dir, QLineEdit::Normal, tr ("New Directory"), &ok); if (ok && name.length () > 0) { QDir dir (parent_dir); dir.mkdir (name); m_file_system_model->revert (); } } void files_dock_widget::process_set_current_dir (const QString& dir) { emit displayed_directory_changed (dir); } void files_dock_widget::process_find_files (const QString& dir) { emit find_files_signal (dir); } void files_dock_widget::copyClipboard () { if (m_file_tree_view->hasFocus ()) contextmenu_copy_selection (true); if (m_current_directory->hasFocus ()) { QClipboard *clipboard = QApplication::clipboard (); QLineEdit *edit = m_current_directory->lineEdit (); if (edit && edit->hasSelectedText ()) { clipboard->setText (edit->selectedText ()); } } } void files_dock_widget::pasteClipboard () { if (m_current_directory->hasFocus ()) { QClipboard *clipboard = QApplication::clipboard (); QString str = clipboard->text (); QLineEdit *edit = m_current_directory->lineEdit (); if (edit && str.length () > 0) edit->insert (str); } } void files_dock_widget::selectAll () { if (m_file_tree_view->hasFocus ()) m_file_tree_view->selectAll (); if (m_current_directory->hasFocus ()) { QLineEdit *edit = m_current_directory->lineEdit (); if (edit) { edit->selectAll (); } } } OCTAVE_END_NAMESPACE(octave)