changeset 27283:189ca5990c5d

work-in-progress patch for set path dialog Originally submitted by JunWang Edited by Rik and jwe.
author JunWang <jstzwj@aliyun.com>
date Mon, 22 Jul 2019 23:51:01 -0400
parents 49c60d16866f
children 1a8762e5662b
files libgui/src/main-window.cc libgui/src/main-window.h libgui/src/module.mk libgui/src/set-path-dialog.cc libgui/src/set-path-dialog.h libgui/src/set-path-model.cc libgui/src/set-path-model.h
diffstat 7 files changed, 752 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/src/main-window.cc	Mon Jul 22 18:56:31 2019 -0400
+++ b/libgui/src/main-window.cc	Mon Jul 22 23:51:01 2019 -0400
@@ -103,8 +103,8 @@
       m_doc_browser_window (nullptr), m_editor_window (nullptr),
       m_workspace_window (nullptr), m_variable_editor_window (nullptr),
       m_external_editor (new external_editor_interface (this)),
-      m_active_editor (m_external_editor),
-      m_settings_dlg (nullptr), m_find_files_dlg (nullptr),
+      m_active_editor (m_external_editor), m_settings_dlg (nullptr),
+      m_find_files_dlg (nullptr), m_set_path_dlg (nullptr),
       m_release_notes_window (nullptr), m_community_news_window (nullptr),
       m_clipboard (QApplication::clipboard ()),
       m_prevent_readline_conflicts (true), m_suppress_dbg_location (true),
@@ -236,7 +236,7 @@
 
     delete m_find_files_dlg;
     delete m_release_notes_window;
-    delete m_settings_dlg;
+    delete m_community_news_window;
     delete m_community_news_window;
   }
 
@@ -1707,6 +1707,18 @@
     focus_command_window ();  // make sure that the command window has focus
   }
 
+  void main_window::handle_set_path_dialog_request (void)
+  {
+    if (m_set_path_dlg)  // m_set_path_dlg is a guarded pointer!
+      return;
+
+    m_set_path_dlg = new set_path_dialog (this);
+
+    m_set_path_dlg->setModal (false);
+    m_set_path_dlg->setAttribute (Qt::WA_DeleteOnClose);
+    m_set_path_dlg->show ();
+  }
+
   void main_window::find_files (const QString& start_dir)
   {
 
@@ -1764,6 +1776,7 @@
         m_load_workspace_action->setShortcut (no_key);
         m_save_workspace_action->setShortcut (no_key);
         m_preferences_action->setShortcut (no_key);
+        m_set_path_action->setShortcut (no_key);
         m_exit_action->setShortcut (no_key);
 
         // edit menu
@@ -2407,6 +2420,9 @@
 
     edit_menu->addSeparator ();
 
+    m_set_path_action
+      = edit_menu->addAction (tr ("Set Path"));
+
     m_preferences_action
       = edit_menu->addAction (resource_manager::icon ("preferences-system"),
                               tr ("Preferences..."));
@@ -2436,6 +2452,10 @@
 
     connect (m_preferences_action, SIGNAL (triggered (void)),
              this, SLOT (process_settings_dialog_request (void)));
+
+    connect (m_set_path_action, SIGNAL (triggered (void)),
+             this, SLOT (handle_set_path_dialog_request (void)));
+
   }
 
   QAction * main_window::construct_debug_menu_item (const char *icon,
@@ -2770,6 +2790,7 @@
     shortcut_manager::set_shortcut (m_save_workspace_action,
                                     "main_file:save_workspace");
     shortcut_manager::set_shortcut (m_preferences_action, "main_file:preferences");
+    shortcut_manager::set_shortcut (m_set_path_action, "main_file:set_path");
     shortcut_manager::set_shortcut (m_exit_action,"main_file:exit");
 
     // edit menu
--- a/libgui/src/main-window.h	Mon Jul 22 18:56:31 2019 -0400
+++ b/libgui/src/main-window.h	Mon Jul 22 23:51:01 2019 -0400
@@ -49,6 +49,7 @@
 #include "documentation-dock-widget.h"
 #include "files-dock-widget.h"
 #include "find-files-dialog.h"
+#include "set-path-dialog.h"
 #include "history-dock-widget.h"
 #include "octave-dock-widget.h"
 #include "qt-interpreter-events.h"
@@ -198,6 +199,8 @@
 
     void handle_octave_ready ();
 
+    void handle_set_path_dialog_request (void);
+
     //! Find files dialog.
     //!@{
     void find_files (const QString& startdir = QDir::currentPath ());
@@ -330,6 +333,7 @@
     QAction *m_new_figure_action;
     QAction *m_load_workspace_action;
     QAction *m_save_workspace_action;
+    QAction *m_set_path_action;
     QAction *m_preferences_action;
     QAction *m_exit_action;
 
@@ -386,6 +390,9 @@
 
     find_files_dialog *m_find_files_dlg;
 
+    //! Set path dialog
+    QPointer<set_path_dialog> m_set_path_dlg;
+
     //! Release notes window.
 
     QWidget *m_release_notes_window;
--- a/libgui/src/module.mk	Mon Jul 22 18:56:31 2019 -0400
+++ b/libgui/src/module.mk	Mon Jul 22 23:51:01 2019 -0400
@@ -156,7 +156,9 @@
   %reldir%/moc-variable-editor-model.cc \
   %reldir%/moc-find-files-dialog.cc \
   %reldir%/moc-find-files-model.cc \
-  %reldir%/moc-octave-dock-widget.cc
+  %reldir%/moc-octave-dock-widget.cc \
+  %reldir%/moc-set-path-dialog.cc \
+  %reldir%/moc-set-path-model.cc
 
 octave_gui_MOC += \
   $(OCTAVE_GUI_SRC_MOC) \
@@ -214,7 +216,10 @@
   %reldir%/workspace-model.h \
   %reldir%/workspace-view.h \
   %reldir%/variable-editor.h \
-  %reldir%/variable-editor-model.h
+  %reldir%/variable-editor-model.h \
+  %reldir%/set-path-dialog.h \
+  %reldir%/set-path-model.h
+
 
 %canon_reldir%_%canon_reldir%_la_SOURCES = \
   %reldir%/dialog.cc \
@@ -249,7 +254,9 @@
   %reldir%/workspace-model.cc \
   %reldir%/workspace-view.cc \
   %reldir%/variable-editor.cc \
-  %reldir%/variable-editor-model.cc
+  %reldir%/variable-editor-model.cc \
+  %reldir%/set-path-dialog.cc \
+  %reldir%/set-path-model.cc
 
 nodist_%canon_reldir%_%canon_reldir%_la_SOURCES = \
   $(octave_gui_MOC) \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/set-path-dialog.cc	Mon Jul 22 23:51:01 2019 -0400
@@ -0,0 +1,204 @@
+/*
+
+Copyright (C) 2019 JunWang
+
+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 <QPushButton>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QHeaderView>
+#include <QListView>
+#include <QFileDialog>
+#include <QStatusBar>
+#include <QIcon>
+#include <QFileInfo>
+#include <QTimer>
+#include <QDirIterator>
+#include <QTextStream>
+#include <QGroupBox>
+#include <QFileDialog>
+
+#include "set-path-dialog.h"
+#include "set-path-model.h"
+#include "resource-manager.h"
+
+namespace octave
+{
+  set_path_dialog::set_path_dialog (QWidget *parent)
+    : QDialog (parent)
+  {
+    setWindowTitle (tr ("Set Path"));
+
+    m_info_label = new QLabel (tr ("All changes take effect immediately."));
+
+    m_add_folder_button = new QPushButton (tr ("Add Folder..."));
+    m_move_to_top_button = new QPushButton (tr ("Move to Top"));
+    m_move_to_bottom_button = new QPushButton (tr ("Move to Bottom"));
+    m_move_up_button = new QPushButton (tr ("Move Up"));
+    m_move_down_button = new QPushButton (tr ("Move Down"));
+    m_remove_button = new QPushButton (tr ("Remove"));
+
+    m_save_button = new QPushButton (tr ("Save"));
+    m_revert_button = new QPushButton (tr ("Revert"));
+
+    m_save_button->setFocus ();
+
+    connect (m_add_folder_button, SIGNAL (clicked (void)),
+             this, SLOT (add_folder (void)));
+
+    connect (m_remove_button, SIGNAL (clicked (void)),
+             this, SLOT (rmPath (void)));
+
+    connect (m_move_to_top_button, SIGNAL (clicked (void)),
+             this, SLOT (moveToTopPath (void)));
+
+    connect (m_move_to_bottom_button, SIGNAL (clicked (void)),
+             this, SLOT (moveToBottomPath (void)));
+
+    connect (m_move_up_button, SIGNAL (clicked (void)),
+             this, SLOT (moveUpPath (void)));
+
+    connect (m_move_down_button, SIGNAL (clicked (void)),
+             this, SLOT (moveDownPath (void)));
+
+    connect (m_save_button, SIGNAL (clicked (void)),
+             this, SLOT (save (void)));
+
+    connect (m_revert_button, SIGNAL (clicked (void)),
+             this, SLOT (revert (void)));
+
+    set_path_model *model = new set_path_model (this);
+
+    m_path_list = new QListView (this);
+    m_path_list->setWordWrap (false);
+    m_path_list->setModel (model);
+    m_path_list->setSelectionBehavior (QAbstractItemView::SelectRows);
+    m_path_list->setSelectionMode (QAbstractItemView::ExtendedSelection);
+    m_path_list->setAlternatingRowColors (true);
+
+    // layout everything
+    QDialogButtonBox *button_box = new QDialogButtonBox (Qt::Horizontal);
+    button_box->addButton (m_save_button, QDialogButtonBox::ActionRole);
+
+    // add dialog close button
+    m_close_button = button_box->addButton (QDialogButtonBox::Close);
+    connect (button_box, SIGNAL (rejected (void)), this, SLOT (close (void)));
+
+    button_box->addButton (m_revert_button, QDialogButtonBox::ActionRole);
+
+    // path edit options
+    QDialogButtonBox *path_edit_layout = new QDialogButtonBox (Qt::Vertical);
+    path_edit_layout->addButton (m_add_folder_button, QDialogButtonBox::ActionRole);
+    path_edit_layout->addButton (m_move_to_top_button, QDialogButtonBox::ActionRole);
+    path_edit_layout->addButton (m_move_up_button, QDialogButtonBox::ActionRole);
+    path_edit_layout->addButton (m_move_down_button, QDialogButtonBox::ActionRole);
+    path_edit_layout->addButton (m_move_to_bottom_button, QDialogButtonBox::ActionRole);
+    path_edit_layout->addButton (m_remove_button, QDialogButtonBox::ActionRole);
+
+    // main layout
+    QHBoxLayout *main_hboxlayout = new QHBoxLayout;
+    main_hboxlayout->addWidget(path_edit_layout);
+    main_hboxlayout->addWidget(m_path_list);
+
+    QGridLayout *main_layout = new QGridLayout;
+    main_layout->addWidget (m_info_label, 0, 0);
+    main_layout->addLayout (main_hboxlayout, 1, 0);
+    main_layout->addWidget (button_box,2, 0);
+
+    setLayout (main_layout);
+  }
+
+  set_path_dialog::~set_path_dialog (void)
+  {
+  }
+
+  void set_path_dialog::add_folder(void)
+  {
+    QString dir
+      = QFileDialog::getExistingDirectory (this, tr ("Open Directory"),
+                                           "",
+                                           (QFileDialog::ShowDirsOnly
+                                            | QFileDialog::DontResolveSymlinks));
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    m->addPath (dir);
+  }
+
+  void set_path_dialog::rmPath (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    QItemSelectionModel *selmodel = m_path_list->selectionModel ();
+    QModelIndexList indexlist = selmodel->selectedIndexes();
+    m->rmPath (indexlist);
+  }
+
+  void set_path_dialog::moveUpPath (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    QItemSelectionModel *selmodel = m_path_list->selectionModel ();
+    QModelIndexList indexlist = selmodel->selectedIndexes();
+    m->moveUpPath (indexlist);
+  }
+
+  void set_path_dialog::moveDownPath (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    QItemSelectionModel *selmodel = m_path_list->selectionModel ();
+    QModelIndexList indexlist = selmodel->selectedIndexes();
+    m->moveDownPath (indexlist);
+  }
+
+  void set_path_dialog::moveToTopPath (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    QItemSelectionModel *selmodel = m_path_list->selectionModel ();
+    QModelIndexList indexlist = selmodel->selectedIndexes();
+    m->moveToTopPath (indexlist);
+  }
+
+  void set_path_dialog::moveToBottomPath (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    QItemSelectionModel *selmodel = m_path_list->selectionModel ();
+    QModelIndexList indexlist = selmodel->selectedIndexes();
+    m->moveToBottomPath (indexlist);
+  }
+
+  void set_path_dialog::save (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    m->save ();
+  }
+
+  void set_path_dialog::revert (void)
+  {
+    set_path_model *m = static_cast<set_path_model *> (m_path_list->model ());
+    m->revert ();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/set-path-dialog.h	Mon Jul 22 23:51:01 2019 -0400
@@ -0,0 +1,83 @@
+/*
+
+Copyright (C) 2019 JunWang
+
+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 (octave_set_path_dialog_h)
+#define octave_set_path_dialog_h 1
+
+#include <QDialog>
+#include <QModelIndex>
+#include <QFileInfo>
+
+class QLabel;
+class QPushButton;
+class QListView;
+class QVBoxLayout;
+class QHBoxLayout;
+
+namespace octave
+{
+  class set_path_dialog : public QDialog
+  {
+    Q_OBJECT
+
+  public:
+
+    set_path_dialog (QWidget *parent = nullptr);
+
+    virtual ~set_path_dialog (void);
+
+  private slots:
+
+    void add_folder(void);
+
+    void rmPath (void);
+
+    void moveUpPath (void);
+
+    void moveDownPath (void);
+
+    void moveToTopPath (void);
+
+    void moveToBottomPath (void);
+
+    void save (void);
+
+    void revert (void);
+
+  private:
+
+    QLabel *m_info_label;
+    QPushButton *m_save_button;
+    QPushButton *m_close_button;
+    QPushButton *m_revert_button;
+
+    QListView *m_path_list;
+
+    QPushButton *m_add_folder_button;
+    QPushButton *m_move_to_top_button;
+    QPushButton *m_move_to_bottom_button;
+    QPushButton *m_move_up_button;
+    QPushButton *m_move_down_button;
+    QPushButton *m_remove_button;
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/set-path-model.cc	Mon Jul 22 23:51:01 2019 -0400
@@ -0,0 +1,336 @@
+/*
+
+Copyright (C) 2019 JunWang
+
+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 <iostream>
+
+#include <string>
+#include <algorithm>
+
+#include <QFileIconProvider>
+#include <QtAlgorithms>
+#include <QMessageBox>
+
+#include "pathsearch.h"
+
+#include "event-manager.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "load-path.h"
+
+#include "set-path-model.h"
+
+namespace octave
+{
+  set_path_model::set_path_model (QObject *p)
+    : QAbstractListModel (p)
+  {
+    connect (this, SIGNAL (update_data_signal (const QStringList&)),
+             this, SLOT (update_data (const QStringList&)));
+
+    construct ();
+  }
+
+  std::string set_path_model::to_string (void)
+  {
+    std::string path_sep = directory_path::path_sep_str ();
+
+    std::string path_str;
+
+    QStringList::iterator it = m_dirs.begin ();
+
+    while (it < m_dirs.end ())
+      {
+        if (it != m_dirs.begin ())
+          path_str += path_sep;
+        path_str += it->toStdString ();
+        ++it;
+      }
+
+    return path_str;
+  }
+
+  void set_path_model::model_to_path (void)
+  {
+    event_manager& evmgr
+      = __get_event_manager__ ("set_path_model::model_to_path");
+
+    std::string path_str = to_string ();
+
+    evmgr.post_event
+      ([path_str] (void)
+       {
+         // INTERPRETER THREAD
+
+         load_path& lp = __get_load_path__ ("set_path_model::model_to_path");
+
+         lp.set (path_str);
+       });
+  }
+
+  void set_path_model::clear (void)
+  {
+    beginResetModel ();
+
+    m_dirs.clear ();
+
+    endResetModel ();
+  }
+
+  void set_path_model::save (void)
+  {
+    model_to_path ();
+
+    event_manager& evmgr = __get_event_manager__ ("set_path_model::save");
+
+    evmgr.post_event
+      ([] (void)
+       {
+         // INTERPRETER THREAD
+
+         interpreter& interp = __get_interpreter__ ("set_path_model::save");
+
+         interp.feval ("savepath");
+       });
+  }
+
+  void set_path_model::revert (void)
+  {
+    clear ();
+    beginInsertRows (QModelIndex (), 0, m_old_dirs.size () - 1);
+    m_dirs = m_old_dirs;
+    endInsertRows ();
+    model_to_path ();
+  }
+
+  void set_path_model::addPath (const QString& p)
+  {
+    beginInsertRows (QModelIndex (), m_dirs.size (), m_dirs.size ());
+
+    QList<QString>::Iterator it = m_dirs.begin();
+
+    m_dirs.insert (it, p);
+
+    endInsertRows ();
+
+    model_to_path ();
+  }
+
+  void set_path_model::rmPath (const QModelIndexList& indices)
+  {
+    QModelIndexList sorted_indices (indices);
+    std::sort (sorted_indices.begin (), sorted_indices.end ());
+    QStringList::iterator it = m_dirs.begin ();
+    int i = 0;
+    int index_top = 0;
+    while (it < m_dirs.end ())
+      {
+        if (index_top < sorted_indices.size ()
+            && i == sorted_indices[index_top].row ())
+          {
+            int dis = std::distance (m_dirs.begin (), it);
+            beginRemoveRows (QModelIndex (), dis, dis);
+            it = m_dirs.erase (it);
+            endRemoveRows ();
+            ++index_top;
+          }
+        else
+          ++it;
+
+        ++i;
+      }
+
+    model_to_path ();
+  }
+
+  void set_path_model::moveUpPath (const QModelIndexList& indices)
+  {
+    QModelIndexList sorted_indices (indices);
+    std::sort (sorted_indices.begin (), sorted_indices.end ());
+    for (const auto& each : sorted_indices)
+      {
+        if (each.row () == 0)
+          continue;
+
+        beginMoveRows (QModelIndex (), each.row (), each.row (),
+                       QModelIndex (), each.row () - 1);
+
+        m_dirs.swap (each.row () - 1, each.row ());
+
+        endMoveRows ();
+      }
+
+    model_to_path ();
+  }
+
+  void set_path_model::moveDownPath (const QModelIndexList& indices)
+  {
+    QModelIndexList sorted_indices (indices);
+    std::sort (sorted_indices.begin (), sorted_indices.end ());
+    std::reverse (sorted_indices.begin (), sorted_indices.end ());
+    for (const auto& each : sorted_indices)
+      {
+        if (each.row () == m_dirs.size () - 1)
+          continue;
+
+        beginMoveRows (QModelIndex (), each.row(), each.row (),
+                       QModelIndex (), each.row () + 2);
+
+        m_dirs.swap (each.row(), each.row() + 1);
+
+        endMoveRows ();
+      }
+
+    model_to_path ();
+  }
+
+  void set_path_model::moveToTopPath (const QModelIndexList& indices)
+  {
+    QModelIndexList sorted_indices (indices);
+    std::sort (sorted_indices.begin (), sorted_indices.end ());
+
+    QStringList::iterator it = m_dirs.begin ();
+    int i = 0;
+    int index_top = 0;
+    while (it < m_dirs.end ())
+      {
+        if (index_top < indices.size () && indices[index_top].row () == i)
+          {
+            int distance = std::distance (m_dirs.begin (), it);
+            beginMoveRows (QModelIndex (), distance, distance,
+                           QModelIndex (), m_dirs.size ());
+            QString tmp = *it;
+            it = m_dirs.erase (it);
+            m_dirs.push_front (tmp);
+            endMoveRows ();
+            ++index_top;
+          }
+        else
+          ++it;
+
+        ++i;
+      }
+
+    std::reverse (m_dirs.begin (), m_dirs.begin () + indices.size ());
+
+    model_to_path ();
+  }
+
+  void set_path_model::moveToBottomPath (const QModelIndexList& indices)
+  {
+    QModelIndexList sorted_indices (indices);
+    std::sort (sorted_indices.begin (), sorted_indices.end ());
+
+    QStringList::iterator it = m_dirs.begin ();
+    int i = 0;
+    int index_top = 0;
+    while (it < m_dirs.end ())
+      {
+        if (index_top < indices.size () && indices[index_top].row () == i)
+          {
+            int distance = std::distance (m_dirs.begin (), it);
+
+            beginMoveRows (QModelIndex (), distance, distance,
+                           QModelIndex (), m_dirs.size ());
+
+            QString tmp = *it;
+            it = m_dirs.erase (it);
+            m_dirs.push_back (tmp);
+
+            endMoveRows ();
+            ++index_top;
+          }
+        else
+          ++it;
+
+        ++i;
+      }
+
+    model_to_path ();
+  }
+
+  int set_path_model::rowCount (const QModelIndex&) const
+  {
+    return m_dirs.size ();
+  }
+
+  QVariant set_path_model::data (const QModelIndex& idx, int role) const
+  {
+    QVariant retval;
+    if (idx.isValid ())
+      {
+        switch (role)
+          {
+          case Qt::DisplayRole:
+            retval = QVariant (m_dirs[idx.row ()]);
+            break;
+
+          case Qt::DecorationRole:
+            retval = QVariant (QIcon ());
+            break;
+
+          case Qt::SizeHintRole:
+            retval = QVariant (QSize (10, 20));
+            break;
+          }
+      }
+
+    return retval;
+  }
+
+  void set_path_model::construct (void)
+  {
+    event_manager& evmgr = __get_event_manager__ ("set_path_model::construct");
+
+    evmgr.post_event
+      ([this] (void)
+       {
+         load_path& lp = __get_load_path__ ("set_path_model::construct");
+
+         std::list<std::string> dir_list = lp.dir_list ();
+
+         QStringList qs_dir_list;
+
+         for (const auto& dir : dir_list)
+           qs_dir_list << QString::fromStdString (dir);
+
+         emit update_data_signal (qs_dir_list);
+       });
+  }
+
+  void set_path_model::update_data (const QStringList& dirs)
+  {
+    m_old_dirs = m_dirs;
+
+    m_dirs = dirs;
+
+    m_dirs.removeAll (".");
+
+    int numel = m_dirs.size ();
+
+    emit dataChanged (QAbstractListModel::index (0, 0),
+                      QAbstractListModel::index (numel-1, 0));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/set-path-model.h	Mon Jul 22 23:51:01 2019 -0400
@@ -0,0 +1,88 @@
+/*
+
+Copyright (C) 2019 JunWang
+
+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 (octave_set_path_model_h)
+#define octave_set_path_model_h 1
+
+#include <QAbstractListModel>
+#include <QStringList>
+#include <QList>
+#include <QFileInfo>
+#include <QIcon>
+
+namespace octave
+{
+  class set_path_model : public QAbstractListModel
+  {
+    Q_OBJECT
+
+  public:
+
+    set_path_model (QObject *p = nullptr);
+
+    ~set_path_model (void) = default;
+
+    void clear (void);
+
+    void save (void);
+
+    void revert (void);
+
+    void addPath (const QString& p);
+
+    void rmPath (const QModelIndexList& indices);
+
+    void moveUpPath (const QModelIndexList& indices);
+
+    void moveDownPath (const QModelIndexList& indices);
+
+    void moveToTopPath (const QModelIndexList& indices);
+
+    void moveToBottomPath (const QModelIndexList& indices);
+
+    std::string to_string (void);
+
+    void model_to_path (void);
+
+    int rowCount (const QModelIndex& p = QModelIndex ()) const;
+
+    QVariant data (const QModelIndex& idx, int role) const;
+
+  signals:
+
+    void update_data_signal (const QStringList& dirs);
+
+  private slots:
+
+    void update_data (const QStringList& dirs);
+
+  private:
+
+    void construct (void);
+
+    QStringList m_dirs;
+
+    QStringList m_old_dirs;
+  };
+}
+
+#endif