view libgui/src/octave-qobject.cc @ 27197:b8c0d5ad024f

refactor and rename some qt application objects * octave-qobject.h, octave-qobject.cc: New files. Move octave_qapplication, octave_qt_app, octave_qt_cli_app, and octave_qt_gui_app classes here from main-window.h and main-window.cc. Rename octave_qt_app to base_qobject. Rename octave_qt_cli_app to cli_qobject. Rename octave_qt_gui_app to gui_qobject. * qt-application.h, qt-application.cc: Rename from octave-gui.h and octave-gui.cc. Rename gui_application class to qt_application. Change all uses. * libgui/src/module.mk: Update.
author John W. Eaton <jwe@octave.org>
date Sat, 22 Jun 2019 17:30:16 -0500
parents
children a044202208af
line wrap: on
line source

/*

Copyright (C) 2013-2019 John W. Eaton
Copyright (C) 2011-2019 Jacob Dawid

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 <QApplication>
#include <QFile>
#include <QTextCodec>
#include <QThread>
#include <QTranslator>
#include <QTimer>

#include <utility>

#include "dialog.h"
#include "interpreter-qobject.h"
#include "main-window.h"
#include "octave-qobject.h"
#include "octave-qt-link.h"
#include "qt-application.h"
#include "resource-manager.h"

#include "oct-env.h"
#include "version.h"

namespace octave
{
  // Disable all Qt messages by default.

  static void
#if defined (QTMESSAGEHANDLER_ACCEPTS_QMESSAGELOGCONTEXT)
  message_handler (QtMsgType, const QMessageLogContext &, const QString &)
#else
  message_handler (QtMsgType, const char *)
#endif
  { }

  //! Reimplement QApplication::notify.  Octave's own exceptions are
  //! caught and rethrown in the interpreter thread.

  bool octave_qapplication::notify (QObject *receiver, QEvent *ev)
  {
    try
      {
        return QApplication::notify (receiver, ev);
      }
    catch (execution_exception&)
      {
        octave_link::post_exception (std::current_exception ());
      }

   return false;
  }

  // We will create a QApplication object, even if START_GUI is false,
  // so that we can use Qt widgets for plot windows when running in
  // command-line mode.  Note that we are creating an
  // octave_qapplication object but handling it as a QApplication object
  // because the octave_qapplication should behave identically to a
  // QApplication object except that it overrides the notify method so
  // we can handle forward Octave interpreter exceptions from the GUI
  // thread to the interpreter thread.

  base_qobject::base_qobject (qt_application& app_context)
    : QObject (), m_app_context (app_context),
      m_argc (m_app_context.sys_argc ()),
      m_argv (m_app_context.sys_argv ()),
      m_octave_qapp (new octave_qapplication (m_argc, m_argv)),
      m_qt_tr (new QTranslator ()), m_gui_tr (new QTranslator ()),
      m_qsci_tr (new QTranslator ()), m_translators_installed (false),
      m_octave_qt_link (new octave_qt_link ()),
      m_interpreter_qobject (new interpreter_qobject (m_app_context)),
      m_main_thread (new QThread ())
  {
    std::string show_gui_msgs =
      sys::env::getenv ("OCTAVE_SHOW_GUI_MESSAGES");

    // Installing our handler suppresses the messages.

    if (show_gui_msgs.empty ())
      {
#if defined (HAVE_QINSTALLMESSAGEHANDLER)
        qInstallMessageHandler (message_handler);
#else
        qInstallMsgHandler (message_handler);
#endif
      }

    // Set the codec for all strings (before wizard or any GUI object)
#if ! defined (Q_OS_WIN32)
    QTextCodec::setCodecForLocale (QTextCodec::codecForName ("UTF-8"));
#endif

#if defined (HAVE_QT4)
    QTextCodec::setCodecForCStrings (QTextCodec::codecForName ("UTF-8"));
#endif

    // Initialize global Qt application metadata.

    QCoreApplication::setApplicationName ("GNU Octave");
    QCoreApplication::setApplicationVersion (OCTAVE_VERSION);

    // Register octave_value_list for connecting thread crossing signals.

    qRegisterMetaType<octave_value_list> ("octave_value_list");

    // Force left-to-right alignment (see bug #46204)
    m_octave_qapp->setLayoutDirection (Qt::LeftToRight);

    octave_link::connect_link (m_octave_qt_link);

    connect (m_octave_qt_link, SIGNAL (confirm_shutdown_signal (void)),
             this, SLOT (confirm_shutdown_octave (void)));

    connect (m_octave_qt_link,
             SIGNAL (copy_image_to_clipboard_signal (const QString&, bool)),
             this, SLOT (copy_image_to_clipboard (const QString&, bool)));

    connect_uiwidget_links ();

    connect (m_interpreter_qobject, SIGNAL (octave_finished_signal (int)),
             this, SLOT (handle_octave_finished (int)));

    connect (m_interpreter_qobject, SIGNAL (octave_finished_signal (int)),
             m_main_thread, SLOT (quit (void)));

    connect (m_main_thread, SIGNAL (finished (void)),
             m_main_thread, SLOT (deleteLater (void)));
  }

  base_qobject::~base_qobject (void)
  {
    delete m_interpreter_qobject;
    delete m_octave_qapp;

    string_vector::delete_c_str_vec (m_argv);
  }

  void base_qobject::config_translators (void)
  {
    if (m_translators_installed)
      return;

    resource_manager::config_translators (m_qt_tr, m_qsci_tr, m_gui_tr);

    m_octave_qapp->installTranslator (m_qt_tr);
    m_octave_qapp->installTranslator (m_gui_tr);
    m_octave_qapp->installTranslator (m_qsci_tr);

    m_translators_installed = true;
  }

  void base_qobject::start_main_thread (void)
  {
    // Defer initializing and executing the interpreter until after the main
    // window and QApplication are running to prevent race conditions
    QTimer::singleShot (0, m_interpreter_qobject, SLOT (execute (void)));

    m_interpreter_qobject->moveToThread (m_main_thread);

    m_main_thread->start ();
  }

  int base_qobject::exec (void)
  {
    return m_octave_qapp->exec ();
  }

  void base_qobject::handle_octave_finished (int exit_status)
  {
    qApp->exit (exit_status);
  }

  void base_qobject::confirm_shutdown_octave (void)
  {
    confirm_shutdown_octave_internal (true);
  }

  void base_qobject::copy_image_to_clipboard (const QString& file,
                                              bool remove_file)
  {
    QClipboard *clipboard = QApplication::clipboard ();

    QImage img (file);

    if (img.isNull ())
      {
        // Report error?
        return;
      }

    clipboard->setImage (img);

    if (remove_file)
      QFile::remove (file);
  }

  // Create a message dialog with specified string, buttons and decorative
  // text.

  void base_qobject::handle_create_dialog (const QString& message,
                                           const QString& title,
                                           const QString& icon,
                                           const QStringList& button,
                                           const QString& defbutton,
                                           const QStringList& role)
  {
    MessageDialog *message_dialog = new MessageDialog (message, title, icon,
                                                       button, defbutton, role);
    message_dialog->setAttribute (Qt::WA_DeleteOnClose);
    message_dialog->show ();
  }

  // Create a list dialog with specified list, initially selected, mode,
  // view size and decorative text.

  void base_qobject::handle_create_listview (const QStringList& list,
                                             const QString& mode,
                                             int wd, int ht,
                                             const QIntList& initial,
                                             const QString& name,
                                             const QStringList& prompt,
                                             const QString& ok_string,
                                             const QString& cancel_string)
  {
    ListDialog *list_dialog = new ListDialog (list, mode, wd, ht,
                                              initial, name, prompt,
                                              ok_string, cancel_string);

    list_dialog->setAttribute (Qt::WA_DeleteOnClose);
    list_dialog->show ();
  }

  // Create an input dialog with specified prompts and defaults, title and
  // row/column size specifications.
  void base_qobject::handle_create_inputlayout (const QStringList& prompt,
                                                const QString& title,
                                                const QFloatList& nr,
                                                const QFloatList& nc,
                                                const QStringList& defaults)
  {
    InputDialog *input_dialog = new InputDialog (prompt, title, nr, nc,
                                                 defaults);

    input_dialog->setAttribute (Qt::WA_DeleteOnClose);
    input_dialog->show ();
  }

  void base_qobject::handle_create_filedialog (const QStringList& filters,
                                               const QString& title,
                                               const QString& filename,
                                               const QString& dirname,
                                               const QString& multimode)
  {
    FileDialog *file_dialog = new FileDialog (filters, title, filename,
                                              dirname, multimode);

    file_dialog->setAttribute (Qt::WA_DeleteOnClose);
    file_dialog->show ();
  }

  // Connect the signals emitted when the Octave thread wants to create
  // a dialog box of some sort.  Perhaps a better place for this would be
  // as part of the QUIWidgetCreator class.  However, mainWindow currently
  // is not a global variable and not accessible for connecting.

  void base_qobject::connect_uiwidget_links (void)
  {
    connect (&uiwidget_creator,
             SIGNAL (create_dialog (const QString&, const QString&,
                                    const QString&, const QStringList&,
                                    const QString&, const QStringList&)),
             this,
             SLOT (handle_create_dialog (const QString&, const QString&,
                                         const QString&, const QStringList&,
                                         const QString&, const QStringList&)));

    // Register QIntList so that list of ints may be part of a signal.
    qRegisterMetaType<QIntList> ("QIntList");
    connect (&uiwidget_creator,
             SIGNAL (create_listview (const QStringList&, const QString&,
                                      int, int, const QIntList&,
                                      const QString&, const QStringList&,
                                      const QString&, const QString&)),
             this,
             SLOT (handle_create_listview (const QStringList&, const QString&,
                                           int, int, const QIntList&,
                                           const QString&, const QStringList&,
                                           const QString&, const QString&)));

    // Register QFloatList so that list of floats may be part of a signal.
    qRegisterMetaType<QFloatList> ("QFloatList");
    connect (&uiwidget_creator,
             SIGNAL (create_inputlayout (const QStringList&, const QString&,
                                         const QFloatList&, const QFloatList&,
                                         const QStringList&)),
             this,
             SLOT (handle_create_inputlayout (const QStringList&, const QString&,
                                              const QFloatList&,
                                              const QFloatList&,
                                              const QStringList&)));

    connect (&uiwidget_creator,
             SIGNAL (create_filedialog (const QStringList &,const QString&,
                                        const QString&, const QString&,
                                        const QString&)),
             this,
             SLOT (handle_create_filedialog (const QStringList &, const QString&,
                                             const QString&, const QString&,
                                             const QString&)));
  }

  void base_qobject::confirm_shutdown_octave_internal (bool closenow)
  {
    // Wait for link thread to go to sleep state.
    m_octave_qt_link->lock ();

    m_octave_qt_link->shutdown_confirmation (closenow);

    m_octave_qt_link->unlock ();

    // Awake the worker thread so that it continues shutting down (or not).
    m_octave_qt_link->wake_all ();
  }

  cli_qobject::cli_qobject (qt_application& app_context)
    : base_qobject (app_context)
  {
    // Get settings file.
    resource_manager::reload_settings ();

    // After settings.
    config_translators ();

    m_octave_qapp->setQuitOnLastWindowClosed (false);

    start_main_thread ();
  }

  gui_qobject::gui_qobject (qt_application& app_context)
    : base_qobject (app_context),
      m_main_window (new main_window (*this, m_octave_qt_link))
  {
    connect (m_interpreter_qobject, SIGNAL (octave_ready_signal (void)),
             m_main_window, SLOT (handle_octave_ready (void)));

    m_app_context.gui_running (true);

    start_main_thread ();
  }

  gui_qobject::~gui_qobject (void)
  {
    delete m_main_window;
  }

  void gui_qobject::confirm_shutdown_octave (void)
  {
    bool closenow = true;

    if (m_main_window)
      closenow = m_main_window->confirm_shutdown_octave ();

    confirm_shutdown_octave_internal (closenow);
  }
}