1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2011-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if defined (HAVE_CONFIG_H) 27 # include "config.h" 28 #endif 29 30 #include <QApplication> 31 #include <QClipboard> 32 #include <QCompleter> 33 #include <QLabel> 34 #include <QMenu> 35 #include <QScrollBar> 36 #include <QVBoxLayout> 37 38 #include "gui-preferences-cs.h" 39 #include "gui-preferences-global.h" 40 #include "gui-preferences-hw.h" 41 #include "history-dock-widget.h" 42 #include "octave-qobject.h" 43 44 #include "cmd-hist.h" 45 46 #include "error.h" 47 48 namespace octave 49 { history_dock_widget(QWidget * p,base_qobject & oct_qobj)50 history_dock_widget::history_dock_widget (QWidget *p, base_qobject& oct_qobj) 51 : octave_dock_widget ("HistoryDockWidget", p, oct_qobj) 52 { 53 setStatusTip (tr ("Browse and search the command history.")); 54 55 connect (this, SIGNAL (command_create_script (const QString&)), 56 p, SLOT (new_file (const QString&))); 57 58 connect (this, SIGNAL (information (const QString&)), 59 p, SLOT (report_status_message (const QString&))); 60 61 connect (this, SIGNAL (command_double_clicked (const QString&)), 62 p, SLOT (execute_command_in_terminal (const QString&))); 63 64 construct (); 65 } 66 set_history(const QStringList & hist)67 void history_dock_widget::set_history (const QStringList& hist) 68 { 69 m_history_model->setStringList (hist); 70 m_history_list_view->scrollToBottom (); 71 } 72 append_history(const QString & hist_entry)73 void history_dock_widget::append_history (const QString& hist_entry) 74 { 75 QStringList lst = m_history_model->stringList (); 76 lst.append (hist_entry); 77 78 QScrollBar *scroll_bar = m_history_list_view->verticalScrollBar (); 79 80 bool at_bottom = scroll_bar->maximum () - scroll_bar->value () < 1; 81 82 m_history_model->setStringList (lst); 83 84 // Scroll if slider position at bottom. 85 if (at_bottom) 86 m_history_list_view->scrollToBottom (); 87 } 88 clear_history(void)89 void history_dock_widget::clear_history (void) 90 { 91 m_history_model->setStringList (QStringList ()); 92 } 93 save_settings(void)94 void history_dock_widget::save_settings (void) 95 { 96 resource_manager& rmgr = m_octave_qobj.get_resource_manager (); 97 gui_settings *settings = rmgr.get_settings (); 98 99 if (! settings) 100 return; 101 102 settings->setValue (hw_filter_active.key, m_filter_checkbox->isChecked ()); 103 settings->setValue (hw_filter_shown.key, m_filter_shown); 104 105 QStringList mru; 106 for (int i = 0; i < m_filter->count (); i++) 107 mru.append (m_filter->itemText (i)); 108 settings->setValue (hw_mru_list.key, mru); 109 110 settings->sync (); 111 112 octave_dock_widget::save_settings (); 113 } 114 update_filter_history(void)115 void history_dock_widget::update_filter_history (void) 116 { 117 QString text = m_filter->currentText (); // get current text 118 int index = m_filter->findText (text); // and its actual index 119 120 if (index > -1) 121 m_filter->removeItem (index); // remove if already existing 122 123 m_filter->insertItem (0, text); // (re)insert at beginning 124 m_filter->setCurrentIndex (0); 125 } 126 filter_activate(bool state)127 void history_dock_widget::filter_activate (bool state) 128 { 129 m_filter->setEnabled (state); 130 m_sort_filter_proxy_model.setDynamicSortFilter (state); 131 132 if (state) 133 m_sort_filter_proxy_model.setFilterWildcard (m_filter->currentText ()); 134 else 135 m_sort_filter_proxy_model.setFilterWildcard (QString ()); 136 } 137 ctxMenu(const QPoint & xpos)138 void history_dock_widget::ctxMenu (const QPoint& xpos) 139 { 140 QMenu menu (this); 141 142 QModelIndex index = m_history_list_view->indexAt (xpos); 143 144 if (index.isValid () && index.column () == 0) 145 { 146 resource_manager& rmgr = m_octave_qobj.get_resource_manager (); 147 148 menu.addAction (rmgr.icon ("edit-copy"), tr ("Copy"), this, 149 SLOT (handle_contextmenu_copy (bool))); 150 menu.addAction (tr ("Evaluate"), this, 151 SLOT (handle_contextmenu_evaluate (bool))); 152 menu.addAction (rmgr.icon ("document-new"), tr ("Create script"), this, 153 SLOT (handle_contextmenu_create_script (bool))); 154 } 155 if (m_filter_shown) 156 menu.addAction (tr ("Hide filter"), this, 157 SLOT (handle_contextmenu_filter ())); 158 else 159 menu.addAction (tr ("Show filter"), this, 160 SLOT (handle_contextmenu_filter ())); 161 162 menu.exec (m_history_list_view->mapToGlobal (xpos)); 163 } 164 handle_double_click(QModelIndex modelIndex)165 void history_dock_widget::handle_double_click (QModelIndex modelIndex) 166 { 167 emit command_double_clicked (modelIndex.data ().toString ()); 168 } 169 handle_contextmenu_copy(bool)170 void history_dock_widget::handle_contextmenu_copy (bool) 171 { 172 QString text; 173 QItemSelectionModel *selectionModel = m_history_list_view->selectionModel (); 174 QModelIndexList rows = selectionModel->selectedRows (); 175 bool prev_valid_row = false; 176 for (auto it = rows.begin (); it != rows.end (); it++) 177 { 178 if ((*it).isValid ()) 179 { 180 if (prev_valid_row) 181 text += '\n'; 182 text += (*it).data ().toString (); 183 prev_valid_row = true; 184 } 185 } 186 QApplication::clipboard ()->setText (text); 187 } 188 handle_contextmenu_evaluate(bool)189 void history_dock_widget::handle_contextmenu_evaluate (bool) 190 { 191 QItemSelectionModel *selectionModel = m_history_list_view->selectionModel (); 192 QModelIndexList rows = selectionModel->selectedRows (); 193 for (auto it = rows.begin () ; it != rows.end (); it++) 194 { 195 if ((*it).isValid ()) 196 emit command_double_clicked ((*it).data ().toString ()); 197 } 198 } 199 handle_contextmenu_create_script(bool)200 void history_dock_widget::handle_contextmenu_create_script (bool) 201 { 202 QString text; 203 QItemSelectionModel *selectionModel = m_history_list_view->selectionModel (); 204 QModelIndexList rows = selectionModel->selectedRows (); 205 206 bool prev_valid_row = false; 207 for (auto it = rows.begin (); it != rows.end (); it++) 208 { 209 if ((*it).isValid ()) 210 { 211 if (prev_valid_row) 212 text += '\n'; 213 text += (*it).data ().toString (); 214 prev_valid_row = true; 215 } 216 } 217 218 if (text.length () > 0) 219 emit command_create_script (text); 220 } 221 handle_contextmenu_filter(void)222 void history_dock_widget::handle_contextmenu_filter (void) 223 { 224 m_filter_shown = ! m_filter_shown; 225 m_filter_widget->setVisible (m_filter_shown); 226 } 227 copyClipboard(void)228 void history_dock_widget::copyClipboard (void) 229 { 230 if (m_history_list_view->hasFocus ()) 231 handle_contextmenu_copy (true); 232 if (m_filter->lineEdit ()->hasFocus () 233 && m_filter->lineEdit ()->hasSelectedText ()) 234 { 235 QClipboard *clipboard = QApplication::clipboard (); 236 clipboard->setText (m_filter->lineEdit ()->selectedText ()); 237 } 238 } 239 pasteClipboard(void)240 void history_dock_widget::pasteClipboard (void) 241 { 242 if (m_filter->lineEdit ()->hasFocus ()) 243 { 244 QClipboard *clipboard = QApplication::clipboard (); 245 QString str = clipboard->text (); 246 if (str.length () > 0) 247 m_filter->lineEdit ()->insert (str); 248 } 249 } 250 selectAll(void)251 void history_dock_widget::selectAll (void) 252 { 253 if (m_filter->lineEdit ()->hasFocus ()) 254 m_filter->lineEdit ()->selectAll (); 255 256 if (m_history_list_view->hasFocus ()) 257 m_history_list_view->selectAll (); 258 } 259 handle_visibility(bool visible)260 void history_dock_widget::handle_visibility (bool visible) 261 { 262 octave_dock_widget::handle_visibility (visible); 263 264 if (visible) 265 { 266 int filter_state = m_filter_checkbox->isChecked (); 267 filter_activate (filter_state); 268 } 269 } 270 construct(void)271 void history_dock_widget::construct (void) 272 { 273 m_history_model = new QStringListModel (); 274 m_sort_filter_proxy_model.setSourceModel (m_history_model); 275 m_history_list_view = new QListView (this); 276 m_history_list_view->setModel (&m_sort_filter_proxy_model); 277 m_history_list_view->setAlternatingRowColors (true); 278 m_history_list_view->setEditTriggers (QAbstractItemView::NoEditTriggers); 279 m_history_list_view->setStatusTip 280 (tr ("Double-click a command to transfer it to the Command Window.")); 281 m_history_list_view->setSelectionMode (QAbstractItemView::ExtendedSelection); 282 m_history_list_view->setContextMenuPolicy (Qt::CustomContextMenu); 283 connect (m_history_list_view, 284 SIGNAL (customContextMenuRequested (const QPoint &)), this, 285 SLOT (ctxMenu (const QPoint &))); 286 287 m_filter = new QComboBox (this); 288 m_filter->setToolTip (tr ("Enter text to filter the command history")); 289 m_filter->setEditable (true); 290 m_filter->setMaxCount (MaxFilterHistory); 291 m_filter->setInsertPolicy (QComboBox::NoInsert); 292 m_filter->setSizeAdjustPolicy 293 (QComboBox::AdjustToMinimumContentsLengthWithIcon); 294 QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred); 295 m_filter->setSizePolicy (sizePol); 296 m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive); 297 298 QLabel *filter_label = new QLabel (tr ("Filter")); 299 300 m_filter_checkbox = new QCheckBox (); 301 302 setWindowIcon (QIcon (":/actions/icons/logo.png")); 303 set_title (tr ("Command History")); 304 setWidget (new QWidget ()); 305 306 m_filter_widget = new QWidget (this); 307 QHBoxLayout *filter_layout = new QHBoxLayout (); 308 filter_layout->addWidget (filter_label); 309 filter_layout->addWidget (m_filter_checkbox); 310 filter_layout->addWidget (m_filter); 311 filter_layout->setMargin(0); 312 m_filter_widget->setLayout (filter_layout); 313 314 QVBoxLayout *hist_layout = new QVBoxLayout (); 315 hist_layout->addWidget (m_filter_widget); 316 hist_layout->addWidget (m_history_list_view); 317 318 hist_layout->setMargin (2); 319 hist_layout->setSpacing (0); 320 widget ()->setLayout (hist_layout); 321 322 // Init state of the filter 323 resource_manager& rmgr = m_octave_qobj.get_resource_manager (); 324 gui_settings *settings = rmgr.get_settings (); 325 326 m_filter_shown 327 = settings->value (hw_filter_shown).toBool (); 328 m_filter_widget->setVisible (m_filter_shown); 329 330 m_filter->addItems (settings->value (hw_mru_list).toStringList ()); 331 332 bool filter_state 333 = settings->value (hw_filter_active).toBool (); 334 m_filter_checkbox->setChecked (filter_state); 335 filter_activate (filter_state); 336 337 // Connect signals and slots 338 connect (m_filter, SIGNAL (editTextChanged (const QString&)), 339 &m_sort_filter_proxy_model, 340 SLOT (setFilterWildcard (const QString&))); 341 connect (m_filter_checkbox, SIGNAL (toggled (bool)), 342 this, SLOT (filter_activate (bool))); 343 connect (m_filter->lineEdit (), SIGNAL (editingFinished (void)), 344 this, SLOT (update_filter_history (void))); 345 346 connect (m_history_list_view, SIGNAL (doubleClicked (QModelIndex)), 347 this, SLOT (handle_double_click (QModelIndex))); 348 349 m_history_list_view->setTextElideMode (Qt::ElideRight); 350 } 351 notice_settings(const gui_settings * settings)352 void history_dock_widget::notice_settings (const gui_settings *settings) 353 { 354 QFont font = QFont (); 355 356 font.setStyleHint (QFont::TypeWriter); 357 QString default_font = settings->value (global_mono_font).toString (); 358 359 font.setFamily (settings->value (cs_font.key, default_font).toString ()); 360 font.setPointSize (settings->value (cs_font_size).toInt ()); 361 362 m_history_list_view->setFont (font); 363 } 364 365 } 366