1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2011-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 <QEvent> 31 #include <QFrame> 32 #include <QLabel> 33 #include <QMouseEvent> 34 #include <QTimer> 35 36 #include "Canvas.h" 37 #include "Container.h" 38 #include "ContextMenu.h" 39 #include "Panel.h" 40 #include "QtHandlesUtils.h" 41 42 #include "octave-qobject.h" 43 44 #include "graphics.h" 45 #include "interpreter.h" 46 47 namespace QtHandles 48 { 49 50 static int frameStyleFromProperties(const uipanel::properties & pp)51 frameStyleFromProperties (const uipanel::properties& pp) 52 { 53 if (pp.bordertype_is ("none")) 54 return QFrame::NoFrame; 55 else if (pp.bordertype_is ("etchedin")) 56 return (QFrame::Box | QFrame::Sunken); 57 else if (pp.bordertype_is ("etchedout")) 58 return (QFrame::Box | QFrame::Raised); 59 else if (pp.bordertype_is ("beveledin")) 60 return (QFrame::Panel | QFrame::Sunken); 61 else if (pp.bordertype_is ("beveledout")) 62 return (QFrame::Panel | QFrame::Raised); 63 else 64 return (QFrame::Panel | QFrame::Plain); 65 } 66 67 static void setupPalette(const uipanel::properties & pp,QPalette & p)68 setupPalette (const uipanel::properties& pp, QPalette& p) 69 { 70 p.setColor (QPalette::Window, 71 Utils::fromRgb (pp.get_backgroundcolor_rgb ())); 72 p.setColor (QPalette::WindowText, 73 Utils::fromRgb (pp.get_foregroundcolor_rgb ())); 74 p.setColor (QPalette::Light, 75 Utils::fromRgb (pp.get_highlightcolor_rgb ())); 76 p.setColor (QPalette::Dark, 77 Utils::fromRgb (pp.get_shadowcolor_rgb ())); 78 } 79 80 static int borderWidthFromProperties(const uipanel::properties & pp)81 borderWidthFromProperties (const uipanel::properties& pp) 82 { 83 int bw = 0; 84 85 if (! pp.bordertype_is ("none")) 86 { 87 bw = octave::math::round (pp.get_borderwidth ()); 88 if (pp.bordertype_is ("etchedin") || pp.bordertype_is ("etchedout")) 89 bw *= 2; 90 } 91 92 return bw; 93 } 94 95 Panel* create(octave::base_qobject & oct_qobj,octave::interpreter & interp,const graphics_object & go)96 Panel::create (octave::base_qobject& oct_qobj, octave::interpreter& interp, 97 const graphics_object& go) 98 { 99 Object *parent = parentObject (interp, go); 100 101 if (parent) 102 { 103 Container *container = parent->innerContainer (); 104 105 if (container) 106 return new Panel (oct_qobj, interp, go, new QFrame (container)); 107 } 108 109 return nullptr; 110 } 111 Panel(octave::base_qobject & oct_qobj,octave::interpreter & interp,const graphics_object & go,QFrame * frame)112 Panel::Panel (octave::base_qobject& oct_qobj, octave::interpreter& interp, 113 const graphics_object& go, QFrame *frame) 114 : Object (oct_qobj, interp, go, frame), m_container (nullptr), 115 m_title (nullptr), m_blockUpdates (false), 116 m_previous_bbox (Matrix (1, 4, 0)) 117 { 118 uipanel::properties& pp = properties<uipanel> (); 119 120 frame->setObjectName ("UIPanel"); 121 frame->setAutoFillBackground (true); 122 Matrix bb = pp.get_boundingbox (false); 123 frame->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)), 124 octave::math::round (bb(2)), octave::math::round (bb(3))); 125 frame->setFrameStyle (frameStyleFromProperties (pp)); 126 frame->setLineWidth (octave::math::round (pp.get_borderwidth ())); 127 QPalette pal = frame->palette (); 128 setupPalette (pp, pal); 129 frame->setPalette (pal); 130 131 m_container = new Container (frame, oct_qobj, interp); 132 m_container->canvas (m_handle); 133 134 connect (m_container, SIGNAL (interpeter_event (const fcn_callback&)), 135 this, SIGNAL (interpeter_event (const fcn_callback&))); 136 137 connect (m_container, SIGNAL (interpeter_event (const meth_callback&)), 138 this, SIGNAL (interpeter_event (const meth_callback&))); 139 140 if (frame->hasMouseTracking ()) 141 { 142 for (auto *w : frame->findChildren<QWidget*> ()) 143 w->setMouseTracking (true); 144 } 145 146 QString title = Utils::fromStdString (pp.get_title ()); 147 if (! title.isEmpty ()) 148 { 149 m_title = new QLabel (title, frame); 150 m_title->setAutoFillBackground (true); 151 m_title->setContentsMargins (4, 0, 4, 0); 152 m_title->setPalette (pal); 153 m_title->setFont (Utils::computeFont<uipanel> (pp, bb(3))); 154 } 155 156 frame->installEventFilter (this); 157 m_container->installEventFilter (this); 158 159 graphics_object fig (go.get_ancestor ("figure")); 160 if (! fig.get ("keypressfcn").isempty ()) 161 m_container->canvas (m_handle)->addEventMask (Canvas::KeyPress); 162 163 if (! fig.get ("keyreleasefcn").isempty ()) 164 m_container->canvas (m_handle)->addEventMask (Canvas::KeyRelease); 165 166 if (pp.is_visible ()) 167 QTimer::singleShot (0, frame, SLOT (show (void))); 168 else 169 frame->hide (); 170 } 171 ~Panel(void)172 Panel::~Panel (void) 173 { } 174 175 bool eventFilter(QObject * watched,QEvent * xevent)176 Panel::eventFilter (QObject *watched, QEvent *xevent) 177 { 178 if (! m_blockUpdates) 179 { 180 gh_manager& gh_mgr = m_interpreter.get_gh_manager (); 181 182 if (watched == qObject ()) 183 { 184 switch (xevent->type ()) 185 { 186 case QEvent::Resize: 187 { 188 octave::autolock guard (gh_mgr.graphics_lock ()); 189 190 graphics_object go = object (); 191 192 if (go.valid_object ()) 193 { 194 if (m_title) 195 { 196 const uipanel::properties& pp = 197 Utils::properties<uipanel> (go); 198 199 if (pp.fontunits_is ("normalized")) 200 { 201 QFrame *frame = qWidget<QFrame> (); 202 203 m_title->setFont (Utils::computeFont<uipanel> 204 (pp, frame->height ())); 205 m_title->resize (m_title->sizeHint ()); 206 } 207 } 208 updateLayout (); 209 } 210 } 211 break; 212 213 case QEvent::MouseButtonPress: 214 { 215 QMouseEvent *m = dynamic_cast<QMouseEvent *> (xevent); 216 217 if (m->button () == Qt::RightButton) 218 { 219 octave::autolock guard (gh_mgr.graphics_lock ()); 220 221 graphics_object go = object (); 222 223 if (go.valid_object ()) 224 ContextMenu::executeAt (m_interpreter, 225 go.get_properties (), 226 m->globalPos ()); 227 } 228 } 229 break; 230 231 default: 232 break; 233 } 234 } 235 else if (watched == m_container) 236 { 237 switch (xevent->type ()) 238 { 239 case QEvent::Resize: 240 if (qWidget<QWidget> ()->isVisible ()) 241 { 242 octave::autolock guard (gh_mgr.graphics_lock ()); 243 244 graphics_object go = object (); 245 246 if (go.valid_object ()) 247 go.get_properties ().update_boundingbox (); 248 } 249 break; 250 251 default: 252 break; 253 } 254 } 255 } 256 257 return false; 258 } 259 260 void update(int pId)261 Panel::update (int pId) 262 { 263 uipanel::properties& pp = properties<uipanel> (); 264 QFrame *frame = qWidget<QFrame> (); 265 266 m_blockUpdates = true; 267 268 switch (pId) 269 { 270 case uipanel::properties::ID_POSITION: 271 { 272 Matrix bb = pp.get_boundingbox (false); 273 if (m_previous_bbox(0) != bb(0) || m_previous_bbox(1) != bb(1) 274 || m_previous_bbox(2) != bb(2) || m_previous_bbox(3) != bb(3)) 275 { 276 frame->setGeometry (octave::math::round (bb(0)), 277 octave::math::round (bb(1)), 278 octave::math::round (bb(2)), 279 octave::math::round (bb(3))); 280 updateLayout (); 281 } 282 m_previous_bbox = bb; 283 } 284 break; 285 286 case uipanel::properties::ID_BORDERWIDTH: 287 frame->setLineWidth (octave::math::round (pp.get_borderwidth ())); 288 updateLayout (); 289 break; 290 291 case uipanel::properties::ID_BACKGROUNDCOLOR: 292 case uipanel::properties::ID_FOREGROUNDCOLOR: 293 case uipanel::properties::ID_HIGHLIGHTCOLOR: 294 case uipanel::properties::ID_SHADOWCOLOR: 295 { 296 QPalette pal = frame->palette (); 297 298 setupPalette (pp, pal); 299 frame->setPalette (pal); 300 if (m_title) 301 m_title->setPalette (pal); 302 } 303 break; 304 305 case uipanel::properties::ID_TITLE: 306 { 307 QString title = Utils::fromStdString (pp.get_title ()); 308 309 if (title.isEmpty ()) 310 { 311 if (m_title) 312 delete m_title; 313 m_title = nullptr; 314 } 315 else 316 { 317 if (! m_title) 318 { 319 QPalette pal = frame->palette (); 320 321 m_title = new QLabel (title, frame); 322 m_title->setAutoFillBackground (true); 323 m_title->setContentsMargins (4, 0, 4, 0); 324 m_title->setPalette (pal); 325 m_title->setFont (Utils::computeFont<uipanel> (pp)); 326 m_title->show (); 327 } 328 else 329 { 330 m_title->setText (title); 331 m_title->resize (m_title->sizeHint ()); 332 } 333 } 334 updateLayout (); 335 } 336 break; 337 338 case uipanel::properties::ID_TITLEPOSITION: 339 updateLayout (); 340 break; 341 342 case uipanel::properties::ID_BORDERTYPE: 343 frame->setFrameStyle (frameStyleFromProperties (pp)); 344 updateLayout (); 345 break; 346 347 case uipanel::properties::ID_FONTNAME: 348 case uipanel::properties::ID_FONTSIZE: 349 case uipanel::properties::ID_FONTWEIGHT: 350 case uipanel::properties::ID_FONTANGLE: 351 if (m_title) 352 { 353 m_title->setFont (Utils::computeFont<uipanel> (pp)); 354 m_title->resize (m_title->sizeHint ()); 355 updateLayout (); 356 } 357 break; 358 359 case uipanel::properties::ID_VISIBLE: 360 frame->setVisible (pp.is_visible ()); 361 updateLayout (); 362 break; 363 364 default: 365 break; 366 } 367 368 m_blockUpdates = false; 369 } 370 371 void redraw(void)372 Panel::redraw (void) 373 { 374 update (uipanel::properties::ID_POSITION); 375 376 Canvas *canvas = m_container->canvas (m_handle); 377 378 if (canvas) 379 canvas->redraw (); 380 } 381 382 void updateLayout(void)383 Panel::updateLayout (void) 384 { 385 uipanel::properties& pp = properties<uipanel> (); 386 QFrame *frame = qWidget<QFrame> (); 387 388 Matrix bb = pp.get_boundingbox (true); 389 int bw = borderWidthFromProperties (pp); 390 391 frame->setFrameRect (QRect (octave::math::round (bb(0)) - bw, 392 octave::math::round (bb(1)) - bw, 393 octave::math::round (bb(2)) + 2*bw, octave::math::round (bb(3)) + 2*bw)); 394 m_container->setGeometry (octave::math::round (bb(0)), 395 octave::math::round (bb(1)), 396 octave::math::round (bb(2)), octave::math::round (bb(3))); 397 398 if (m_blockUpdates) 399 pp.update_boundingbox (); 400 401 if (m_title) 402 { 403 QSize sz = m_title->sizeHint (); 404 int offset = 5; 405 406 if (pp.titleposition_is ("lefttop")) 407 m_title->move (bw+offset, 0); 408 else if (pp.titleposition_is ("righttop")) 409 m_title->move (frame->width () - bw - offset - sz.width (), 0); 410 else if (pp.titleposition_is ("leftbottom")) 411 m_title->move (bw+offset, frame->height () - sz.height ()); 412 else if (pp.titleposition_is ("rightbottom")) 413 m_title->move (frame->width () - bw - offset - sz.width (), 414 frame->height () - sz.height ()); 415 else if (pp.titleposition_is ("centertop")) 416 m_title->move (frame->width () / 2 - sz.width () / 2, 0); 417 else if (pp.titleposition_is ("centerbottom")) 418 m_title->move (frame->width () / 2 - sz.width () / 2, 419 frame->height () - sz.height ()); 420 } 421 } 422 423 void do_connections(const QObject * receiver,const QObject *)424 Panel::do_connections (const QObject *receiver, const QObject* /* emitter */) 425 { 426 Object::do_connections (receiver); 427 Object::do_connections (receiver, m_container->canvas (m_handle)); 428 } 429 430 }; 431