GNU Octave  8.1.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-2023 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 #include "octave-qtutils.h"
50 
51 #include "interpreter.h"
52 #include "oct-map.h"
53 
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 *
102 ButtonGroup::create (octave::base_qobject& oct_qobj,
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 
122 ButtonGroup::ButtonGroup (octave::base_qobject& oct_qobj,
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, &QFrame::show);
179  // FIXME: What is the intent here? QButtonGroup::show is not a
180  // member of QButtonGroup.
181  QTimer::singleShot (0, buttongroup, SLOT (show (void)));
182  }
183  else
184  frame->hide ();
185 
186  connect (m_buttongroup,
187  QOverload<QAbstractButton *>::of (&QButtonGroup::buttonClicked),
189 }
190 
192 { }
193 
194 bool
195 ButtonGroup::eventFilter (QObject *watched, QEvent *xevent)
196 {
197  if (! m_blockUpdates)
198  {
199  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
200 
201  if (watched == qObject ())
202  {
203  switch (xevent->type ())
204  {
205  case QEvent::Resize:
206  {
207  octave::autolock guard (gh_mgr.graphics_lock ());
208 
209  graphics_object go = object ();
210 
211  if (go.valid_object ())
212  {
213  if (m_title)
214  {
215  const uibuttongroup::properties& pp =
216  Utils::properties<uibuttongroup> (go);
217 
218  if (pp.fontunits_is ("normalized"))
219  {
220  QFrame *frame = qWidget<QFrame> ();
221 
223  (pp, frame->height ()));
224  m_title->resize (m_title->sizeHint ());
225  }
226  }
227  updateLayout ();
228  }
229  }
230  break;
231 
232  case QEvent::MouseButtonPress:
233  {
234  QMouseEvent *m = dynamic_cast<QMouseEvent *> (xevent);
235 
236  if (m->button () == Qt::RightButton)
237  {
238  octave::autolock guard (gh_mgr.graphics_lock ());
239 
241  m->globalPos ());
242  }
243  }
244  break;
245 
246  default:
247  break;
248  }
249  }
250  else if (watched == m_container)
251  {
252  switch (xevent->type ())
253  {
254  case QEvent::Resize:
255  if (qWidget<QWidget> ()->isVisible ())
256  {
257  octave::autolock guard (gh_mgr.graphics_lock ());
258 
259  properties ().update_boundingbox ();
260  }
261  break;
262 
263  default:
264  break;
265  }
266  }
267  }
268 
269  return false;
270 }
271 
272 void
274 {
275  uibuttongroup::properties& pp = properties<uibuttongroup> ();
276  QFrame *frame = qWidget<QFrame> ();
277 
278  m_blockUpdates = true;
279 
280  switch (pId)
281  {
282  case uibuttongroup::properties::ID_POSITION:
283  {
284  Matrix bb = pp.get_boundingbox (false);
285 
286  frame->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
287  octave::math::round (bb(2)), octave::math::round (bb(3)));
288  updateLayout ();
289  }
290  break;
291 
292  case uibuttongroup::properties::ID_BORDERWIDTH:
293  frame->setLineWidth (octave::math::round (pp.get_borderwidth ()));
294  updateLayout ();
295  break;
296 
297  case uibuttongroup::properties::ID_BACKGROUNDCOLOR:
298  case uibuttongroup::properties::ID_FOREGROUNDCOLOR:
299  case uibuttongroup::properties::ID_HIGHLIGHTCOLOR:
300  case uibuttongroup::properties::ID_SHADOWCOLOR:
301  {
302  QPalette pal = frame->palette ();
303 
304  setupPalette (pp, pal);
305  frame->setPalette (pal);
306  if (m_title)
307  m_title->setPalette (pal);
308  }
309  break;
310 
311  case uibuttongroup::properties::ID_TITLE:
312  {
313  QString title = Utils::fromStdString (pp.get_title ());
314 
315  if (title.isEmpty ())
316  {
317  if (m_title)
318  delete m_title;
319  m_title = nullptr;
320  }
321  else
322  {
323  if (! m_title)
324  {
325  QPalette pal = frame->palette ();
326 
327  m_title = new QLabel (title, frame);
328  m_title->setAutoFillBackground (true);
329  m_title->setContentsMargins (4, 0, 4, 0);
330  m_title->setPalette (pal);
332  m_title->show ();
333  }
334  else
335  {
336  m_title->setText (title);
337  m_title->resize (m_title->sizeHint ());
338  }
339  }
340  updateLayout ();
341  }
342  break;
343 
344  case uibuttongroup::properties::ID_TITLEPOSITION:
345  updateLayout ();
346  break;
347 
348  case uibuttongroup::properties::ID_BORDERTYPE:
349  frame->setFrameStyle (frameStyleFromProperties (pp));
350  updateLayout ();
351  break;
352 
353  case uibuttongroup::properties::ID_FONTNAME:
354  case uibuttongroup::properties::ID_FONTSIZE:
355  case uibuttongroup::properties::ID_FONTWEIGHT:
356  case uibuttongroup::properties::ID_FONTANGLE:
357  if (m_title)
358  {
360  m_title->resize (m_title->sizeHint ());
361  updateLayout ();
362  }
363  break;
364 
365  case uibuttongroup::properties::ID_VISIBLE:
366  frame->setVisible (pp.is_visible ());
367  updateLayout ();
368  break;
369 
370  case uibuttongroup::properties::ID_SELECTEDOBJECT:
371  {
372  graphics_handle h = pp.get_selectedobject ();
373 
374  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
375 
376  octave::autolock guard (gh_mgr.graphics_lock ());
377 
378  graphics_object go = gh_mgr.get_object (h);
379 
380  Object *selectedObject = qt_graphics_toolkit::toolkitObject (go);
381  ToggleButtonControl *toggle = static_cast<ToggleButtonControl *>
382  (selectedObject);
383  RadioButtonControl *radio = static_cast<RadioButtonControl *>(selectedObject);
384  if (toggle)
385  {
386  go.get_properties ().set ("value", 1);
387  }
388  else if (radio)
389  {
390  go.get_properties ().set ("value", 1);
391  }
392  else
393  {
394  m_hiddenbutton->setChecked (true);
395  }
396  }
397  break;
398 
399  default:
400  break;
401  }
402 
403  m_blockUpdates = false;
404 }
405 
406 void
408 {
409  update (uibuttongroup::properties::ID_POSITION);
410 
411  // FIXME: is it really necessary to update the opengl canvas here?
412  Canvas *canvas = m_container->canvas (m_handle);
413 
414  if (canvas)
415  canvas->redraw ();
416 }
417 
418 void
420 {
421  uibuttongroup::properties& pp = properties<uibuttongroup> ();
422  QFrame *frame = qWidget<QFrame> ();
423 
424  Matrix bb = pp.get_boundingbox (true);
425  int bw = borderWidthFromProperties (pp);
426 
427  frame->setFrameRect (QRect (octave::math::round (bb(0)) - bw,
428  octave::math::round (bb(1)) - bw,
429  octave::math::round (bb(2)) + 2*bw, octave::math::round (bb(3)) + 2*bw));
430  m_container->setGeometry (octave::math::round (bb(0)),
431  octave::math::round (bb(1)),
432  octave::math::round (bb(2)), octave::math::round (bb(3)));
433 
434  if (m_blockUpdates)
435  pp.update_boundingbox ();
436 
437  if (m_title)
438  {
439  QSize sz = m_title->sizeHint ();
440  int offset = 5;
441 
442  if (pp.titleposition_is ("lefttop"))
443  m_title->move (bw+offset, 0);
444  else if (pp.titleposition_is ("righttop"))
445  m_title->move (frame->width () - bw - offset - sz.width (), 0);
446  else if (pp.titleposition_is ("leftbottom"))
447  m_title->move (bw+offset, frame->height () - sz.height ());
448  else if (pp.titleposition_is ("rightbottom"))
449  m_title->move (frame->width () - bw - offset - sz.width (),
450  frame->height () - sz.height ());
451  else if (pp.titleposition_is ("centertop"))
452  m_title->move (frame->width () / 2 - sz.width () / 2, 0);
453  else if (pp.titleposition_is ("centerbottom"))
454  m_title->move (frame->width () / 2 - sz.width () / 2,
455  frame->height () - sz.height ());
456  }
457 }
458 
459 void
461 {
462  m_hiddenbutton->setChecked (true);
463 }
464 
465 void
466 ButtonGroup::addButton (QAbstractButton *btn)
467 {
468  m_buttongroup->addButton (btn);
469  connect (btn, &QAbstractButton::toggled, this, &ButtonGroup::buttonToggled);
470 }
471 
472 void
474 {
475  Q_UNUSED (toggled);
476  if (! m_blockUpdates)
477  {
478  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
479 
480  octave::autolock guard (gh_mgr.graphics_lock ());
481 
482  uibuttongroup::properties& bp = properties<uibuttongroup> ();
483 
484  graphics_handle oldValue = bp.get_selectedobject ();
485 
486  QAbstractButton *checkedBtn = m_buttongroup->checkedButton ();
487 
488  graphics_handle newValue = graphics_handle ();
489  if (checkedBtn != m_hiddenbutton)
490  {
491  Object *checkedObj = Object::fromQObject (checkedBtn);
492  newValue = checkedObj->properties ().get___myhandle__ ();
493  }
494 
495  if (oldValue != newValue)
496  emit gh_set_event (m_handle, "selectedobject",
497  newValue.as_octave_value (), false);
498  }
499 }
500 
501 void
502 ButtonGroup::buttonClicked (QAbstractButton *btn)
503 {
504  Q_UNUSED (btn);
505 
506  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
507 
508  octave::autolock guard (gh_mgr.graphics_lock ());
509 
510  uibuttongroup::properties& bp = properties<uibuttongroup> ();
511 
512  graphics_handle oldValue = bp.get_selectedobject ();
513 
514  QAbstractButton *checkedBtn = m_buttongroup->checkedButton ();
515  Object *checkedObj = Object::fromQObject (checkedBtn);
516  graphics_handle newValue = checkedObj->properties ().get___myhandle__ ();
517 
518  if (oldValue != newValue)
519  {
520  octave_scalar_map eventData;
521  eventData.setfield ("OldValue", oldValue.as_octave_value ());
522  eventData.setfield ("NewValue", newValue.as_octave_value ());
523  eventData.setfield ("Source", bp.get___myhandle__ ().as_octave_value ());
524  eventData.setfield ("EventName", "SelectionChanged");
525  octave_value selectionChangedEventObject (eventData);
526  emit gh_callback_event (m_handle, "selectionchangedfcn",
527  selectionChangedEventObject);
528  }
529 }
530 
OCTAVE_END_NAMESPACE(octave)
static int frameStyleFromProperties(const uibuttongroup::properties &pp)
Definition: ButtonGroup.cc:57
static int borderWidthFromProperties(const uibuttongroup::properties &pp)
Definition: ButtonGroup.cc:87
static void setupPalette(const uibuttongroup::properties &pp, QPalette &p)
Definition: ButtonGroup.cc:74
void updateLayout(void)
Definition: ButtonGroup.cc:419
Container * m_container
Definition: ButtonGroup.h:80
void redraw(void)
Definition: ButtonGroup.cc:407
ButtonGroup(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go, QButtonGroup *buttongroup, QFrame *frame)
Definition: ButtonGroup.cc:122
void buttonClicked(QAbstractButton *btn)
Definition: ButtonGroup.cc:502
~ButtonGroup(void)
Definition: ButtonGroup.cc:191
void selectNothing(void)
Definition: ButtonGroup.cc:460
QButtonGroup * m_buttongroup
Definition: ButtonGroup.h:78
void buttonToggled(bool toggled)
Definition: ButtonGroup.cc:473
void addButton(QAbstractButton *btn)
Definition: ButtonGroup.cc:466
bool m_blockUpdates
Definition: ButtonGroup.h:82
static ButtonGroup * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ButtonGroup.cc:102
QRadioButton * m_hiddenbutton
Definition: ButtonGroup.h:79
QLabel * m_title
Definition: ButtonGroup.h:81
void update(int pId)
Definition: ButtonGroup.cc:273
bool eventFilter(QObject *watched, QEvent *event)
Definition: ButtonGroup.cc:195
Definition: Canvas.h:50
void redraw(bool sync=false)
Definition: Canvas.cc:57
Canvas * canvas(const graphics_handle &handle, bool create=true)
Definition: Container.cc:55
static void executeAt(octave::interpreter &interp, const base_properties &props, const QPoint &pt)
Definition: ContextMenu.cc:122
Definition: dMatrix.h:42
Definition: Object.h:47
void gh_callback_event(const graphics_handle &h, const std::string &name)
static Object * parentObject(octave::interpreter &interp, const graphics_object &go)
Definition: Object.cc:200
graphics_handle m_handle
Definition: Object.h:153
static Object * fromQObject(QObject *obj)
Definition: Object.cc:213
void gh_set_event(const graphics_handle &h, const std::string &name, const octave_value &value)
base_properties & properties(void)
Definition: Object.h:56
virtual void show(void)
Definition: Object.cc:182
graphics_object object(void) const
Definition: Object.cc:82
virtual QObject * qObject(void)
Definition: Object.h:78
virtual Container * innerContainer(void)=0
octave::interpreter & m_interpreter
Definition: Object.h:136
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
static Object * toolkitObject(const graphics_object &go)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::function< void(void)> fcn_callback
Definition: event-manager.h:43
std::function< void(interpreter &)> meth_callback
Definition: event-manager.h:48
octave_handle graphics_handle
double round(double x)
Definition: lo-mappers.h:136
T octave_idx_type m
Definition: mx-inlines.cc:773
std::complex< double > w(std::complex< double > z, double relerr=0)
template QFont computeFont< uibuttongroup >(const uibuttongroup::properties &props, int height)
QString fromStdString(const std::string &s)
QColor fromRgb(const Matrix &rgb)
T::properties & properties(graphics_object obj)