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