GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
ButtonGroup.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2016-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 <QAbstractButton>
31 #include <QButtonGroup>
32 #include <QEvent>
33 #include <QFrame>
34 #include <QLabel>
35 #include <QMouseEvent>
36 #include <QRadioButton>
37 #include <QTimer>
38 
39 #include "Canvas.h"
40 #include "Container.h"
41 #include "ContextMenu.h"
42 #include "ButtonGroup.h"
43 #include "ToggleButtonControl.h"
44 #include "RadioButtonControl.h"
45 #include "QtHandlesUtils.h"
46 #include "qt-graphics-toolkit.h"
47 
48 #include "octave-qobject.h"
49 
50 #include "interpreter.h"
51 #include "ov-struct.h"
52 
53 namespace QtHandles
54 {
55 
56  static int
58  {
59  if (pp.bordertype_is ("none"))
60  return QFrame::NoFrame;
61  else if (pp.bordertype_is ("etchedin"))
62  return (QFrame::Box | QFrame::Sunken);
63  else if (pp.bordertype_is ("etchedout"))
64  return (QFrame::Box | QFrame::Raised);
65  else if (pp.bordertype_is ("beveledin"))
66  return (QFrame::Panel | QFrame::Sunken);
67  else if (pp.bordertype_is ("beveledout"))
68  return (QFrame::Panel | QFrame::Raised);
69  else
70  return (QFrame::Panel | QFrame::Plain);
71  }
72 
73  static void
74  setupPalette (const uibuttongroup::properties& pp, QPalette& p)
75  {
76  p.setColor (QPalette::Window,
77  Utils::fromRgb (pp.get_backgroundcolor_rgb ()));
78  p.setColor (QPalette::WindowText,
79  Utils::fromRgb (pp.get_foregroundcolor_rgb ()));
80  p.setColor (QPalette::Light,
81  Utils::fromRgb (pp.get_highlightcolor_rgb ()));
82  p.setColor (QPalette::Dark,
83  Utils::fromRgb (pp.get_shadowcolor_rgb ()));
84  }
85 
86  static int
88  {
89  int bw = 0;
90 
91  if (! pp.bordertype_is ("none"))
92  {
93  bw = octave::math::round (pp.get_borderwidth ());
94  if (pp.bordertype_is ("etchedin") || pp.bordertype_is ("etchedout"))
95  bw *= 2;
96  }
97 
98  return bw;
99  }
100 
101  ButtonGroup*
103  octave::interpreter& interp, const graphics_object& go)
104  {
105  Object *parent = parentObject (interp, go);
106 
107  if (parent)
108  {
109  Container *container = parent->innerContainer ();
110 
111  if (container)
112  {
113  QFrame *frame = new QFrame (container);
114  return new ButtonGroup (oct_qobj, interp, go,
115  new QButtonGroup (frame), frame);
116  }
117  }
118 
119  return nullptr;
120  }
121 
123  octave::interpreter& interp,
124  const graphics_object& go,
125  QButtonGroup *buttongroup, QFrame *frame)
126  : Object (oct_qobj, interp, go, frame), m_hiddenbutton (nullptr),
127  m_container (nullptr), m_title (nullptr), m_blockUpdates (false)
128  {
129  uibuttongroup::properties& pp = properties<uibuttongroup> ();
130 
131  frame->setObjectName ("UIButtonGroup");
132  frame->setAutoFillBackground (true);
133  Matrix bb = pp.get_boundingbox (false);
134  frame->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
135  octave::math::round (bb(2)), octave::math::round (bb(3)));
136  frame->setFrameStyle (frameStyleFromProperties (pp));
137  frame->setLineWidth (octave::math::round (pp.get_borderwidth ()));
138  QPalette pal = frame->palette ();
139  setupPalette (pp, pal);
140  frame->setPalette (pal);
141  m_buttongroup = buttongroup;
142  m_hiddenbutton = new QRadioButton (frame);
143  m_hiddenbutton->hide ();
144  m_buttongroup->addButton (m_hiddenbutton);
145 
146  m_container = new Container (frame, oct_qobj, interp);
148 
149  connect (m_container, SIGNAL (interpeter_event (const fcn_callback&)),
150  this, SIGNAL (interpeter_event (const fcn_callback&)));
151 
152  connect (m_container, SIGNAL (interpeter_event (const meth_callback&)),
153  this, SIGNAL (interpeter_event (const meth_callback&)));
154 
155  if (frame->hasMouseTracking ())
156  {
157  for (auto *w : frame->findChildren<QWidget*> ())
158  w->setMouseTracking (true);
159  for (auto *w : buttongroup->findChildren<QWidget*> ())
160  w->setMouseTracking (true);
161  }
162 
163  QString title = Utils::fromStdString (pp.get_title ());
164  if (! title.isEmpty ())
165  {
166  m_title = new QLabel (title, frame);
167  m_title->setAutoFillBackground (true);
168  m_title->setContentsMargins (4, 0, 4, 0);
169  m_title->setPalette (pal);
170  m_title->setFont (Utils::computeFont<uibuttongroup> (pp, bb(3)));
171  }
172 
173  frame->installEventFilter (this);
174  m_container->installEventFilter (this);
175 
176  if (pp.is_visible ())
177  {
178  QTimer::singleShot (0, frame, SLOT (show (void)));
179  QTimer::singleShot (0, buttongroup, SLOT (show (void)));
180  }
181  else
182  frame->hide ();
183 
184  connect (m_buttongroup, SIGNAL (buttonClicked (QAbstractButton*)),
185  SLOT (buttonClicked (QAbstractButton*)));
186  }
187 
189  { }
190 
191  bool
192  ButtonGroup::eventFilter (QObject *watched, QEvent *xevent)
193  {
194  if (! m_blockUpdates)
195  {
197 
198  if (watched == qObject ())
199  {
200  switch (xevent->type ())
201  {
202  case QEvent::Resize:
203  {
204  octave::autolock guard (gh_mgr.graphics_lock ());
205 
206  graphics_object go = object ();
207 
208  if (go.valid_object ())
209  {
210  if (m_title)
211  {
212  const uibuttongroup::properties& pp =
213  Utils::properties<uibuttongroup> (go);
214 
215  if (pp.fontunits_is ("normalized"))
216  {
217  QFrame *frame = qWidget<QFrame> ();
218 
220  (pp, frame->height ()));
221  m_title->resize (m_title->sizeHint ());
222  }
223  }
224  updateLayout ();
225  }
226  }
227  break;
228 
229  case QEvent::MouseButtonPress:
230  {
231  QMouseEvent *m = dynamic_cast<QMouseEvent *> (xevent);
232 
233  if (m->button () == Qt::RightButton)
234  {
235  octave::autolock guard (gh_mgr.graphics_lock ());
236 
238  m->globalPos ());
239  }
240  }
241  break;
242 
243  default:
244  break;
245  }
246  }
247  else if (watched == m_container)
248  {
249  switch (xevent->type ())
250  {
251  case QEvent::Resize:
252  if (qWidget<QWidget> ()->isVisible ())
253  {
254  octave::autolock guard (gh_mgr.graphics_lock ());
255 
257  }
258  break;
259 
260  default:
261  break;
262  }
263  }
264  }
265 
266  return false;
267  }
268 
269  void
271  {
272  uibuttongroup::properties& pp = properties<uibuttongroup> ();
273  QFrame *frame = qWidget<QFrame> ();
274 
275  m_blockUpdates = true;
276 
277  switch (pId)
278  {
279  case uibuttongroup::properties::ID_POSITION:
280  {
281  Matrix bb = pp.get_boundingbox (false);
282 
283  frame->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
284  octave::math::round (bb(2)), octave::math::round (bb(3)));
285  updateLayout ();
286  }
287  break;
288 
289  case uibuttongroup::properties::ID_BORDERWIDTH:
290  frame->setLineWidth (octave::math::round (pp.get_borderwidth ()));
291  updateLayout ();
292  break;
293 
294  case uibuttongroup::properties::ID_BACKGROUNDCOLOR:
295  case uibuttongroup::properties::ID_FOREGROUNDCOLOR:
296  case uibuttongroup::properties::ID_HIGHLIGHTCOLOR:
297  case uibuttongroup::properties::ID_SHADOWCOLOR:
298  {
299  QPalette pal = frame->palette ();
300 
301  setupPalette (pp, pal);
302  frame->setPalette (pal);
303  if (m_title)
304  m_title->setPalette (pal);
305  }
306  break;
307 
308  case uibuttongroup::properties::ID_TITLE:
309  {
310  QString title = Utils::fromStdString (pp.get_title ());
311 
312  if (title.isEmpty ())
313  {
314  if (m_title)
315  delete m_title;
316  m_title = nullptr;
317  }
318  else
319  {
320  if (! m_title)
321  {
322  QPalette pal = frame->palette ();
323 
324  m_title = new QLabel (title, frame);
325  m_title->setAutoFillBackground (true);
326  m_title->setContentsMargins (4, 0, 4, 0);
327  m_title->setPalette (pal);
329  m_title->show ();
330  }
331  else
332  {
333  m_title->setText (title);
334  m_title->resize (m_title->sizeHint ());
335  }
336  }
337  updateLayout ();
338  }
339  break;
340 
341  case uibuttongroup::properties::ID_TITLEPOSITION:
342  updateLayout ();
343  break;
344 
345  case uibuttongroup::properties::ID_BORDERTYPE:
346  frame->setFrameStyle (frameStyleFromProperties (pp));
347  updateLayout ();
348  break;
349 
350  case uibuttongroup::properties::ID_FONTNAME:
351  case uibuttongroup::properties::ID_FONTSIZE:
352  case uibuttongroup::properties::ID_FONTWEIGHT:
353  case uibuttongroup::properties::ID_FONTANGLE:
354  if (m_title)
355  {
357  m_title->resize (m_title->sizeHint ());
358  updateLayout ();
359  }
360  break;
361 
362  case uibuttongroup::properties::ID_VISIBLE:
363  frame->setVisible (pp.is_visible ());
364  updateLayout ();
365  break;
366 
367  case uibuttongroup::properties::ID_SELECTEDOBJECT:
368  {
369  graphics_handle h = pp.get_selectedobject ();
370 
372 
373  octave::autolock guard (gh_mgr.graphics_lock ());
374 
375  graphics_object go = gh_mgr.get_object (h);
376 
377  Object *selectedObject = qt_graphics_toolkit::toolkitObject (go);
378  ToggleButtonControl *toggle = static_cast<ToggleButtonControl *>
379  (selectedObject);
380  RadioButtonControl *radio = static_cast<RadioButtonControl *>(selectedObject);
381  if (toggle)
382  {
383  go.get_properties ().set ("value", 1);
384  }
385  else if (radio)
386  {
387  go.get_properties ().set ("value", 1);
388  }
389  else
390  {
391  m_hiddenbutton->setChecked (true);
392  }
393  }
394  break;
395 
396  default:
397  break;
398  }
399 
400  m_blockUpdates = false;
401  }
402 
403  void
405  {
406  update (uibuttongroup::properties::ID_POSITION);
407 
408  // FIXME: is it really necessary to update the opengl canvas here?
409  Canvas *canvas = m_container->canvas (m_handle);
410 
411  if (canvas)
412  canvas->redraw ();
413  }
414 
415  void
417  {
418  uibuttongroup::properties& pp = properties<uibuttongroup> ();
419  QFrame *frame = qWidget<QFrame> ();
420 
421  Matrix bb = pp.get_boundingbox (true);
422  int bw = borderWidthFromProperties (pp);
423 
424  frame->setFrameRect (QRect (octave::math::round (bb(0)) - bw,
425  octave::math::round (bb(1)) - bw,
426  octave::math::round (bb(2)) + 2*bw, octave::math::round (bb(3)) + 2*bw));
427  m_container->setGeometry (octave::math::round (bb(0)),
428  octave::math::round (bb(1)),
429  octave::math::round (bb(2)), octave::math::round (bb(3)));
430 
431  if (m_blockUpdates)
432  pp.update_boundingbox ();
433 
434  if (m_title)
435  {
436  QSize sz = m_title->sizeHint ();
437  int offset = 5;
438 
439  if (pp.titleposition_is ("lefttop"))
440  m_title->move (bw+offset, 0);
441  else if (pp.titleposition_is ("righttop"))
442  m_title->move (frame->width () - bw - offset - sz.width (), 0);
443  else if (pp.titleposition_is ("leftbottom"))
444  m_title->move (bw+offset, frame->height () - sz.height ());
445  else if (pp.titleposition_is ("rightbottom"))
446  m_title->move (frame->width () - bw - offset - sz.width (),
447  frame->height () - sz.height ());
448  else if (pp.titleposition_is ("centertop"))
449  m_title->move (frame->width () / 2 - sz.width () / 2, 0);
450  else if (pp.titleposition_is ("centerbottom"))
451  m_title->move (frame->width () / 2 - sz.width () / 2,
452  frame->height () - sz.height ());
453  }
454  }
455 
456  void
458  {
459  m_hiddenbutton->setChecked (true);
460  }
461 
462 
463  void
464  ButtonGroup::addButton (QAbstractButton *btn)
465  {
466  m_buttongroup->addButton (btn);
467  connect (btn, SIGNAL (toggled (bool)), SLOT (buttonToggled (bool)));
468  }
469 
470  void
472  {
473  Q_UNUSED (toggled);
474  if (! m_blockUpdates)
475  {
477 
478  octave::autolock guard (gh_mgr.graphics_lock ());
479 
480  uibuttongroup::properties& bp = properties<uibuttongroup> ();
481 
482  graphics_handle oldValue = bp.get_selectedobject ();
483 
484  QAbstractButton *checkedBtn = m_buttongroup->checkedButton ();
485 
486  graphics_handle newValue = graphics_handle ();
487  if (checkedBtn != m_hiddenbutton)
488  {
489  Object *checkedObj = Object::fromQObject (checkedBtn);
490  newValue = checkedObj->properties ().get___myhandle__ ();
491  }
492 
493  if (oldValue != newValue)
494  emit gh_set_event (m_handle, "selectedobject",
495  newValue.as_octave_value (), false);
496  }
497  }
498 
499  void
500  ButtonGroup::buttonClicked (QAbstractButton *btn)
501  {
502  Q_UNUSED (btn);
503 
505 
506  octave::autolock guard (gh_mgr.graphics_lock ());
507 
508  uibuttongroup::properties& bp = properties<uibuttongroup> ();
509 
510  graphics_handle oldValue = bp.get_selectedobject ();
511 
512  QAbstractButton *checkedBtn = m_buttongroup->checkedButton ();
513  Object *checkedObj = Object::fromQObject (checkedBtn);
514  graphics_handle newValue = checkedObj->properties ().get___myhandle__ ();
515 
516  if (oldValue != newValue)
517  {
518  octave_scalar_map eventData;
519  eventData.setfield ("OldValue", oldValue.as_octave_value ());
520  eventData.setfield ("NewValue", newValue.as_octave_value ());
521  eventData.setfield ("Source", bp.get___myhandle__ ().as_octave_value ());
522  eventData.setfield ("EventName", "SelectionChanged");
523  octave_value selectionChangedEventObject (new octave_struct (eventData));
524  emit gh_callback_event (m_handle, "selectionchangedfcn",
525  selectionChangedEventObject);
526  }
527  }
528 
529 };
Definition: dMatrix.h:42
void buttonClicked(QAbstractButton *btn)
Definition: ButtonGroup.cc:500
void buttonToggled(bool toggled)
Definition: ButtonGroup.cc:471
QButtonGroup * m_buttongroup
Definition: ButtonGroup.h:82
bool eventFilter(QObject *watched, QEvent *event)
Definition: ButtonGroup.cc:192
void update(int pId)
Definition: ButtonGroup.cc:270
static ButtonGroup * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ButtonGroup.cc:102
void addButton(QAbstractButton *btn)
Definition: ButtonGroup.cc:464
Container * m_container
Definition: ButtonGroup.h:84
ButtonGroup(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go, QButtonGroup *buttongroup, QFrame *frame)
Definition: ButtonGroup.cc:122
QRadioButton * m_hiddenbutton
Definition: ButtonGroup.h:83
void redraw(bool sync=false)
Definition: Canvas.cc:58
Canvas * canvas(const graphics_handle &handle, bool create=true)
Definition: Container.cc:56
static void executeAt(octave::interpreter &interp, const base_properties &props, const QPoint &pt)
Definition: ContextMenu.cc:123
graphics_object object(void) const
Definition: Object.cc:83
base_properties & properties(void)
Definition: Object.h:60
virtual Container * innerContainer(void)=0
octave::interpreter & m_interpreter
Definition: Object.h:140
static Object * parentObject(octave::interpreter &interp, const graphics_object &go)
Definition: Object.cc:201
void gh_callback_event(const graphics_handle &h, const std::string &name)
virtual void show(void)
Definition: Object.cc:183
static Object * fromQObject(QObject *obj)
Definition: Object.cc:214
graphics_handle m_handle
Definition: Object.h:157
void gh_set_event(const graphics_handle &h, const std::string &name, const octave_value &value)
virtual QObject * qObject(void)
Definition: Object.h:82
static Object * toolkitObject(const graphics_object &go)
virtual void update_boundingbox(void)
Definition: graphics.cc:3526
virtual void set(const caseless_str &, const octave_value &)
graphics_object get_object(double val) const
Definition: graphics.in.h:6260
octave::mutex graphics_lock(void)
Definition: graphics.in.h:6393
base_properties & get_properties(void)
Definition: graphics.in.h:2829
bool valid_object(void) const
Definition: graphics.in.h:2847
Base class for Octave interfaces that use Qt.
gh_manager & get_gh_manager(void)
Definition: interpreter.h:295
octave_value as_octave_value(void) const
Definition: oct-handle.h:80
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11000
octave_handle graphics_handle
T octave_idx_type m
Definition: mx-inlines.cc:773
std::complex< double > w(std::complex< double > z, double relerr=0)
QColor fromRgb(const Matrix &rgb)
template QFont computeFont< uibuttongroup >(const uibuttongroup::properties &props, int height)
QString fromStdString(const std::string &s)
static int frameStyleFromProperties(const uibuttongroup::properties &pp)
Definition: ButtonGroup.cc:57
static void setupPalette(const uibuttongroup::properties &pp, QPalette &p)
Definition: ButtonGroup.cc:74
static int borderWidthFromProperties(const uibuttongroup::properties &pp)
Definition: ButtonGroup.cc:87
double round(double x)
Definition: lo-mappers.h:136
std::function< void(octave::interpreter &)> meth_callback
Definition: event-manager.h:47
std::function< void(void)> fcn_callback
Definition: event-manager.h:46