view libgui/graphics/Backend.cc @ 27303:07b330708e3c

use Qt signals for interpreter callbacks in Qt graphics toolkit * Backend.h, Backend.cc (Backend::m_interpreter): New data member. (Backend::createObject): Pass pointer to invoking Backend object in signal argument list. (Backend::interpreter_event): New slots. Access interpreter event_manager to post interpreter callbacks events here. * ObjectFactory.h, ObjectFactory.cc (ObjectFactory::createObject): New arg, backend. Connect interpreter_event signal from newly created object to interpreter_event slot in backend object. * __init_qt__.cc (__init__): Pass interpreter to Backend constructor. * module.mk (OCTAVE_GUI_GRAPHICS_MOC): New files, moc-Canvas.cc and moc-Container.cc. * Object.h (Object::interpreter_event): New signals. * Canvas.h, Canvas.cc (Canvas::interprter_event): New signals. (Canvas::canvasMouseReleaseEvent): Emit interpreter_event signal instead of accessing interpreter event_manager object directly here. * GLCanvas.cc (GLCanvas::do_print): Emit interpreter_event signal instead of accessing interpreter event_manager object directly here. * Container.h, Container.cc (Container::interpreter_event): New signals. (Container::canvas): Forward Canvas interpreter_event signals to Container interpreter_event signals. * ButtonGroup.cc (ButtonGroup::ButtonGroup) Forward Container interpreter_event signals to the ButtonGroup interpreter_event signal. Since ButtonGroup is derived from Object, this connection and the one made by ObjectFactory::createObject has the effect of passing the interpreter_event signal up to the Backend interpreter_event slot. * Figure.cc (Figure::Figure): Likewise, for Figure. * Panel.cc (Panel::Panel): Likewise, for Panel.
author John W. Eaton <jwe@octave.org>
date Mon, 29 Jul 2019 10:43:20 -0400
parents e3d886685813
children 22265a75be74
line wrap: on
line source

/*

Copyright (C) 2011-2019 Michael Goffioul

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 <cstdint>

#include <QApplication>
#include <QFontMetrics>
#include <QThread>

#include "Backend.h"
#include "Logger.h"
#include "Object.h"
#include "ObjectFactory.h"
#include "ObjectProxy.h"
#include "QtHandlesUtils.h"

#include "event-manager.h"
#include "interpreter.h"

//#if INTPTR_MAX == INT32_MAX
//# define OCTAVE_PTR_TYPE octave_uint32
//# define OCTAVE_INTPTR_TYPE uint32_t
//# define OCTAVE_PTR_SCALAR uint32_scalar_value
//#else
# define OCTAVE_PTR_TYPE octave_uint64
# define OCTAVE_INTPTR_TYPE uint64_t
# define OCTAVE_PTR_SCALAR uint64_scalar_value
//#endif

namespace QtHandles
{

  static std::string
  toolkitObjectProperty (const graphics_object& go)
  {
    if (go.isa ("figure"))
      return "__plot_stream__";
    else if (go.isa ("uicontrol")
             || go.isa ("uipanel")
             || go.isa ("uibuttongroup")
             || go.isa ("uimenu")
             || go.isa ("uicontextmenu")
             || go.isa ("uitable")
             || go.isa ("uitoolbar")
             || go.isa ("uipushtool")
             || go.isa ("uitoggletool"))
      return "__object__";
    else
      qCritical ("QtHandles::Backend: no __object__ property known for object "
                 "of type %s", go.type ().c_str ());

    return "";
  }

  Backend::Backend (octave::interpreter& interp)
    : QObject (), base_graphics_toolkit ("qt"), m_interpreter (interp)
  {
    ObjectFactory *factory = ObjectFactory::instance ();

    connect (this, SIGNAL (createObject (Backend *, double)),
             factory, SLOT (createObject (Backend *, double)),
             Qt::BlockingQueuedConnection);
  }

  Backend::~Backend (void)
  { }

  bool
  Backend::initialize (const graphics_object& go)
  {
    if (go.isa ("figure")
        || go.isa ("uicontrol")
        || go.isa ("uipanel")
        || go.isa ("uibuttongroup")
        || go.isa ("uimenu")
        || go.isa ("uicontextmenu")
        || go.isa ("uitable")
        || go.isa ("uitoolbar")
        || go.isa ("uipushtool")
        || go.isa ("uitoggletool"))
      {
        // FIXME: We need to unlock the mutex here but we have no way to know if
        // if it was previously locked by this thread, and thus if we should
        // re-lock it.
        gh_manager::unlock ();

        Logger::debug ("Backend::initialize %s from thread %08x",
                       go.type ().c_str (), QThread::currentThreadId ());

        ObjectProxy *proxy = new ObjectProxy ();
        graphics_object gObj (go);

        OCTAVE_PTR_TYPE tmp (reinterpret_cast<OCTAVE_INTPTR_TYPE> (proxy));
        gObj.get_properties ().set (toolkitObjectProperty (go), tmp);

        emit createObject (this, go.get_handle ().value ());

        return true;
      }

    return false;
  }

  void
  Backend::update (const graphics_object& go, int pId)
  {
    // Rule out obvious properties we want to ignore.
    if (pId == figure::properties::ID___PLOT_STREAM__
        || pId == uicontrol::properties::ID___OBJECT__
        || pId == uipanel::properties::ID___OBJECT__
        || pId == uibuttongroup::properties::ID___OBJECT__
        || pId == uimenu::properties::ID___OBJECT__
        || pId == uicontextmenu::properties::ID___OBJECT__
        || pId == uitable::properties::ID___OBJECT__
        || pId == uitoolbar::properties::ID___OBJECT__
        || pId == uipushtool::properties::ID___OBJECT__
        || pId == uitoggletool::properties::ID___OBJECT__
        || pId == base_properties::ID___MODIFIED__)
      return;

    Logger::debug ("Backend::update %s(%d) from thread %08x",
                   go.type ().c_str (), pId, QThread::currentThreadId ());

    ObjectProxy *proxy = toolkitObjectProxy (go);

    if (proxy)
      {
        if (go.isa ("uicontrol")
            && pId == uicontrol::properties::ID_STYLE)
          {
            // Special case: we need to recreate the control widget
            // associated with the octave graphics_object

            finalize (go);
            initialize (go);
          }
        else
          proxy->update (pId);
      }
  }

  void
  Backend::finalize (const graphics_object& go)
  {
    // FIXME: We need to unlock the mutex here but we have no way to know if
    // if it was previously locked by this thread, and thus if we should
    // re-lock it.
    gh_manager::unlock ();

    Logger::debug ("Backend::finalize %s from thread %08x",
                   go.type ().c_str (), QThread::currentThreadId ());

    ObjectProxy *proxy = toolkitObjectProxy (go);

    if (proxy)
      {
        proxy->finalize ();
        delete proxy;

        graphics_object gObj (go);

        gObj.get_properties ().set (toolkitObjectProperty (go), Matrix ());
      }
  }

  void
  Backend::redraw_figure (const graphics_object& go) const
  {
    if (go.get_properties ().is_visible ())
      {
        ObjectProxy *proxy = toolkitObjectProxy (go);

        if (proxy)
          proxy->redraw ();
      }
  }

  void
  Backend::show_figure (const graphics_object& go) const
  {
    if (go.get_properties ().is_visible ())
      {
        ObjectProxy *proxy = toolkitObjectProxy (go);

        if (proxy)
          proxy->show ();
      }
  }

  void
  Backend::print_figure (const graphics_object& go,
                         const std::string& term,
                         const std::string& file_cmd,
                         const std::string& /*debug_file*/) const
  {
    ObjectProxy *proxy = toolkitObjectProxy (go);

    if (proxy)
      proxy->print (QString::fromStdString (file_cmd),
                    QString::fromStdString (term));
  }

  uint8NDArray
  Backend::get_pixels (const graphics_object& go) const
  {
    uint8NDArray retval;

    if (go.isa ("figure"))
      {
        ObjectProxy *proxy = toolkitObjectProxy (go);

        if (proxy)
          retval = proxy->get_pixels ();
      }

    return retval;
  }

  Matrix
  Backend::get_text_extent (const graphics_object& go) const
  {
    Matrix ext (1, 4, 0.0);

    if (go.isa ("uicontrol"))
      {
        octave_value str = go.get ("string");
        if (! str.isempty ())
          {
            const uicontrol::properties& up =
              dynamic_cast<const uicontrol::properties&> (go.get_properties ());
            Matrix bb = up.get_boundingbox (false);
            QFont font = Utils::computeFont<uicontrol> (up, bb(3));
            QFontMetrics fm (font);

            QString s;
            QSize sz;

            if (str.is_string ())
              {
                s = QString::fromStdString (str.string_value ());
                sz = fm.size (Qt::TextSingleLine, s);
                ext(2) = sz.width ();
                ext(3) = sz.height ();
              }
            else if (str.iscellstr ())
              {
                string_vector sv = str.string_vector_value ();
                double wd = 0.0;
                double hg = 0.0;
                for (octave_idx_type ii = 0; ii < sv.numel (); ii++)
                  {
                    s = QString::fromStdString (sv(ii));
                    sz = fm.size (Qt::TextSingleLine, s);
                    wd = std::max (wd, static_cast<double> (sz.width ()));
                    hg = std::max (hg, static_cast<double> (sz.height ()));
                  }

                ext(2) = wd;
                // FIXME: Find a better way to determine the height of e.g.
                // listbox uicontrol objects
                ext(3) = hg * sv.numel ();
              }
          }
      }

    return ext;
  }

  Object*
  Backend::toolkitObject (const graphics_object& go)
  {
    ObjectProxy *proxy = toolkitObjectProxy (go);

    if (proxy)
      return proxy->object ();

    return nullptr;
  }

  ObjectProxy*
  Backend::toolkitObjectProxy (const graphics_object& go)
  {
    if (go)
      {
        octave_value ov = go.get (toolkitObjectProperty (go));

        if (ov.is_defined () && ! ov.isempty ())
          {
            OCTAVE_INTPTR_TYPE ptr = ov.OCTAVE_PTR_SCALAR ().value ();

            return reinterpret_cast<ObjectProxy *> (ptr);
          }
      }

    return nullptr;
  }

  void Backend::interpreter_event (const octave::fcn_callback& fcn)
  {
    octave::event_manager& evmgr = m_interpreter.get_event_manager ();

    evmgr.post_event (fcn);
  }

  void Backend::interpreter_event (const octave::meth_callback& meth)
  {
    octave::event_manager& evmgr = m_interpreter.get_event_manager ();

    evmgr.post_event (meth);
  }
};