view libgui/qterminal/libqterminal/QTerminal.cc @ 31619:ad014fc78bd6

use individual local gui_settings objects Previously, we created a single gui_settings object (derived from QSettings) and accessed it from the resource_manager object. That design is not necessary and is not the way QSettings was designed to be used. Instead of managing a single object, we should be using individual QSettings objects where needed. Each individual QSettings object manages thread-safe access to a single global collection of settings. The Qt docs say that operations on QSettings are not thread safe, but that means that you can't create a QSettings object in one thread and use it in another without some locking. I'm not sure whether we were doing that correctly, but with this change it no longer matters. Each QSettings object does perform locking when reading or writing the underlying global data. * resource-manager.h, resource-manager.cc (resource_manager::m_settings): Delete data member. (resource_manager::get_settings): Delete. * annotation-dialog.cc, QTerminal.cc, QTerminal.h, command-widget.cc, command-widget.h, community-news.cc, dialog.cc, documentation-bookmarks.cc, documentation-bookmarks.h, documentation-dock-widget.cc, documentation-dock-widget.h, documentation.cc, documentation.h, dw-main-window.cc, dw-main-window.h, external-editor-interface.cc, files-dock-widget.cc, files-dock-widget.h, find-files-dialog.cc, history-dock-widget.cc, history-dock-widget.h, file-editor-interface.h, file-editor-tab.cc, file-editor-tab.h, file-editor.cc, file-editor.h, find-dialog.cc, octave-qscintilla.cc, main-window.cc, main-window.h, news-reader.cc, octave-dock-widget.cc, octave-dock-widget.h, qt-interpreter-events.cc, qt-interpreter-events.h, release-notes.cc, resource-manager.cc, resource-manager.h, set-path-dialog.cc, settings-dialog.cc, settings-dialog.h, shortcut-manager.cc, shortcut-manager.h, terminal-dock-widget.cc, terminal-dock-widget.h, variable-editor.cc, variable-editor.h, welcome-wizard.cc, workspace-model.cc, workspace-model.h, workspace-view.cc: Use local gui_settings objects instead of accessing a pointer to a single gui_settings object owned by the resource_manager object.
author John W. Eaton <jwe@octave.org>
date Fri, 02 Dec 2022 14:23:53 -0500
parents 5154c91e0d98
children 0645ea65ca6b
line wrap: on
line source

/*

Copyright (C) 2012-2019 Michael Goffioul.
Copyright (C) 2012-2019 Jacob Dawid.

This file is part of QTerminal.

This program 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.

This program 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 this program.  If not,
see <https://www.gnu.org/licenses/>.

*/

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <QKeySequence>
#include <QWidget>
#include <QStringList>
#include <QColor>
#include <QList>
#include <QMenu>
#include <QClipboard>
#include <QApplication>
#include <QAction>

#include "gui-preferences-global.h"
#include "gui-preferences-cs.h"
#include "gui-preferences-sc.h"
#include "gui-settings.h"
#include "octave-qobject.h"
#include "resource-manager.h"

#include "QTerminal.h"
#if defined (Q_OS_WIN32)
# include "win32/QWinTerminalImpl.h"
#else
# include "unix/QUnixTerminalImpl.h"
#endif

QTerminal *
QTerminal::create (octave::base_qobject& oct_qobj, QWidget *p)
{
#if defined (Q_OS_WIN32)
  QTerminal *terminal = new QWinTerminalImpl (oct_qobj, p);
#else
  QTerminal *terminal = new QUnixTerminalImpl (oct_qobj, p);
#endif

  // FIXME: this function should probably be called from or part of the
  // QTerminal constructor, but I think that would mean some major
  // surgery because then the constructor for QTerminal and the derived
  // Unix- and Windows-specific versions would need access to the
  // base_qobject object, or the design would have to change significantly.

  terminal->construct (oct_qobj);

  return terminal;
}

// slot for the terminal's context menu
void
QTerminal::handleCustomContextMenuRequested (const QPoint& at)
  {
    QClipboard * cb = QApplication::clipboard ();
    QString selected_text = selectedText();
    bool has_selected_text = ! selected_text.isEmpty ();

    _edit_action->setVisible (false);
    m_edit_selected_action->setVisible (false);
    m_help_selected_action->setVisible (false);
    m_doc_selected_action->setVisible (false);

#if defined (Q_OS_WIN32)
    // include this when in windows because there is no filter for
    // detecting links and error messages yet
    if (has_selected_text)
      {
        QRegExp file ("(?:[ \\t]+)(\\S+) at line (\\d+) column (?:\\d+)");

        int pos = file.indexIn (selected_text);

        if (pos > -1)
          {
            QString file_name = file.cap (1);
            QString line = file.cap (2);

            _edit_action->setVisible (true);
            _edit_action->setText (tr ("Edit %1 at line %2")
                                   .arg (file_name).arg (line));

            QStringList data;
            data << file_name << line;
            _edit_action->setData (data);
          }
      }
#endif

    if (has_selected_text)
      {
        QRegExp expr (".*\b*(\\w+)\b*.*");

        int pos = expr.indexIn (selected_text);

        if (pos > -1)
          {
            QString expr_found = expr.cap (1);

            m_edit_selected_action->setVisible (true);
            m_edit_selected_action->setText (tr ("Edit %1").arg (expr_found));
            m_edit_selected_action->setData (expr_found);

            m_help_selected_action->setVisible (true);
            m_help_selected_action->setText (tr ("Help on %1").arg (expr_found));
            m_help_selected_action->setData (expr_found);

            m_doc_selected_action->setVisible (true);
            m_doc_selected_action->setText (tr ("Documentation on %1")
                                            .arg (expr_found));
            m_doc_selected_action->setData (expr_found);
          }
      }

    _paste_action->setEnabled (cb->text().length() > 0);
    _copy_action->setEnabled (has_selected_text);
    _run_selection_action->setVisible (has_selected_text);

    // Get the actions of any hotspots the filters may have found
    QList<QAction*> actions = get_hotspot_actions (at);
    if (actions.length ())
      _contextMenu->addSeparator ();
    for (int i = 0; i < actions.length (); i++)
      _contextMenu->addAction (actions.at(i));

    // Finally, show the context menu
    _contextMenu->exec (mapToGlobal (at));

    // Cleaning up, remove actions of the hotspot
    for (int i = 0; i < actions.length (); i++)
      _contextMenu->removeAction (actions.at(i));
  }

// slot for running the selected code
void
QTerminal::run_selection ()
{
  QStringList commands = selectedText ().split (QRegExp ("[\r\n]"),
#if defined (HAVE_QT_SPLITBEHAVIOR_ENUM)
                                                Qt::SkipEmptyParts);
#else
                                                QString::SkipEmptyParts);
#endif
  for (int i = 0; i < commands.size (); i++)
    emit execute_command_in_terminal_signal (commands.at (i));

}

// slot for edit files in error messages
void
QTerminal::edit_file ()
{
  QString file = _edit_action->data ().toStringList ().at (0);
  int line = _edit_action->data ().toStringList ().at (1).toInt ();

  emit edit_mfile_request (file,line);
}

// slot for edit selected function names
void QTerminal::edit_selected ()
{
  QString file = m_edit_selected_action->data ().toString ();

  emit edit_mfile_request (file,0);
}

// slot for showing help on selected epxression
void QTerminal::help_on_expression ()
{
  QString expr = m_help_selected_action->data ().toString ();

  emit execute_command_in_terminal_signal ("help " + expr);
}

// slot for showing documentation on selected epxression
void QTerminal::doc_on_expression ()
{
  QString expr = m_doc_selected_action->data ().toString ();
  m_octave_qobj.show_documentation_window (expr);
}

void
QTerminal::notice_settings (void)
{
  octave::gui_settings settings;

  // Set terminal font:
  QFont term_font = QFont ();
  term_font.setStyleHint (QFont::TypeWriter);
  QString default_font = settings.value (global_mono_font).toString ();
  term_font.setFamily
    (settings.value (cs_font.key, default_font).toString ());
  term_font.setPointSize
    (settings.value (cs_font_size).toInt ());
  setTerminalFont (term_font);

  QFontMetrics metrics (term_font);
  setMinimumSize (metrics.maxWidth ()*16, metrics.height ()*3);

  QString cursor_type
    = settings.value (cs_cursor).toString ();

  bool cursor_blinking;
  if (settings.contains (global_cursor_blinking.key))
    cursor_blinking = settings.value (global_cursor_blinking).toBool ();
  else
    cursor_blinking = settings.value (cs_cursor_blinking).toBool ();

  for (int ct = IBeamCursor; ct <= UnderlineCursor; ct++)
    {
      if (cursor_type.toStdString () == cs_cursor_types[ct])
        {
          setCursorType ((CursorType) ct, cursor_blinking);
          break;
        }
    }

  bool cursorUseForegroundColor
    = settings.value (cs_cursor_use_fgcol).toBool ();

  int mode = settings.value (cs_color_mode).toInt ();

  setForegroundColor (settings.color_value (cs_colors[0], mode));

  setBackgroundColor (settings.color_value (cs_colors[1], mode));

  setSelectionColor (settings.color_value (cs_colors[2], mode));

  setCursorColor (cursorUseForegroundColor,
                  settings.color_value (cs_colors[3], mode));

  setScrollBufferSize (settings.value (cs_hist_buffer).toInt ());

  // If the Copy shortcut is Ctrl+C, then the Copy action also emits
  // a signal for interrupting the current code executed by the worker.
  // If the Copy shortcut is not Ctrl+C, an extra interrupt action is
  // set up for emitting the interrupt signal.

  QString sc = settings.sc_value (sc_main_edit_copy);

  //  Dis- or enable extra interrupt action: We need an extra option when
  //  copy shortcut is not Ctrl-C or when global shortcuts (like copy) are
  //  disabled.
  bool extra_ir_action
      = (sc != QKeySequence (Qt::ControlModifier | Qt::Key_C).toString ())
        || settings.value (sc_prevent_rl_conflicts).toBool ();

  _interrupt_action->setEnabled (extra_ir_action);
  has_extra_interrupt (extra_ir_action);

  // check whether shortcut Ctrl-D is in use by the main-window
  bool ctrld = settings.value (sc_main_ctrld).toBool ();
  _nop_action->setEnabled (! ctrld);
}

void
QTerminal::construct (octave::base_qobject& oct_qobj)
{
  octave::resource_manager& rmgr = oct_qobj.get_resource_manager ();

  // context menu
  setContextMenuPolicy (Qt::CustomContextMenu);

  _contextMenu = new QMenu (this);

  _copy_action
    = _contextMenu->addAction (rmgr.icon ("edit-copy"), tr ("Copy"), this,
                               SLOT (copyClipboard ()));

  _paste_action
    = _contextMenu->addAction (rmgr.icon ("edit-paste"), tr ("Paste"), this,
                               SLOT (pasteClipboard ()));

  _contextMenu->addSeparator ();

  _selectall_action
    = _contextMenu->addAction (tr ("Select All"), this, SLOT (selectAll ()));

  _run_selection_action
    = _contextMenu->addAction (tr ("Run Selection"), this,
                               SLOT (run_selection ()));

  m_edit_selected_action
    = _contextMenu->addAction (tr ("Edit selection"), this,
                               SLOT (edit_selected ()));
  m_help_selected_action
    = _contextMenu->addAction (tr ("Help on selection"), this,
                               SLOT (help_on_expression ()));
  m_doc_selected_action
    = _contextMenu->addAction (tr ("Documentation on selection"), this,
                               SLOT (doc_on_expression ()));

  _edit_action = _contextMenu->addAction (tr (""), this, SLOT (edit_file ()));

  _contextMenu->addSeparator ();

  _contextMenu->addAction (tr ("Clear Window"), this,
                           SIGNAL (clear_command_window_request ()));

  connect (this, SIGNAL (customContextMenuRequested (QPoint)),
           this, SLOT (handleCustomContextMenuRequested (QPoint)));

  // extra interrupt action
  _interrupt_action = new QAction (this);
  addAction (_interrupt_action);

  _interrupt_action->setShortcut
    (QKeySequence (Qt::ControlModifier + Qt::Key_C));
  _interrupt_action->setShortcutContext (Qt::WidgetWithChildrenShortcut);

  bool ok = connect (_interrupt_action, SIGNAL (triggered ()),
           this, SLOT (terminal_interrupt ()));

  // dummy (nop) action catching Ctrl-D in terminal, no connection
  _nop_action = new QAction (this);
  addAction (_nop_action);

  _nop_action->setShortcut (QKeySequence (Qt::ControlModifier + Qt::Key_D));
  _nop_action->setShortcutContext (Qt::WidgetWithChildrenShortcut);
}