Salome HOME
f3e961d749af51b956e72d50b4358c339fa593f4
[modules/gui.git] / src / Qtx / QtxMenu.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File:      QtxMenu.cxx
21 // Author:    Sergey TELKOV
22 //
23 #include "QtxMenu.h"
24
25 #include <QLabel>
26 #include <QLayout>
27 #include <QPainter>
28 #include <QPaintEvent>
29 #include <QTextDocument>
30 #include <QWidgetAction>
31 #include <QLinearGradient>
32 #include <QAbstractTextDocumentLayout>
33 #include <QTimer>
34
35 /*!
36   \class QtxMenu::Title
37   \brief Popup menu title item.
38   \internal
39 */
40
41 class QtxMenu::Title : public QWidget
42 {
43 public:
44   Title( QWidget* = 0 );
45   virtual ~Title();
46
47   QIcon            icon() const;
48   void             setIcon( const QIcon& );
49
50   QString          text() const;
51   void             setText( const QString& );
52
53   Qt::Alignment    alignment() const;
54   void             setAlignment( const Qt::Alignment );
55
56   virtual QSize    sizeHint() const;
57   virtual QSize    minimumSizeHint() const;
58
59 protected:
60   virtual void     paintEvent( QPaintEvent* );
61
62 private:
63   QIcon            myIcon;
64   QString          myText;
65   Qt::Alignment    myAlignment;
66 };
67
68 /*!
69   \brief Constructor.
70   \param parent parent widget
71   \internal
72 */
73 QtxMenu::Title::Title( QWidget* parent )
74 : QWidget( parent ),
75   myAlignment( 0 )
76 {
77 }
78
79 /*!
80   \brief Destructor.
81   \internal
82 */
83 QtxMenu::Title::~Title()
84 {
85 }
86
87 /*!
88   \brief Get title icon.
89   \return title item icon
90   \internal
91 */
92 QIcon QtxMenu::Title::icon() const
93 {
94   return myIcon;
95 }
96
97 /*!
98   \brief Set title icon.
99   \param ico title item icon
100   \internal
101 */
102 void QtxMenu::Title::setIcon( const QIcon& ico )
103 {
104   myIcon = ico;
105 }
106
107 /*!
108   \brief Get title menu text.
109   \return menu text for the title item
110   \internal
111 */
112 QString QtxMenu::Title::text() const
113 {
114   return myText;
115 }
116
117 /*!
118   \brief Set title menu text.
119   \param txt menu text to be used for the title item
120   \internal
121 */
122 void QtxMenu::Title::setText( const QString& txt )
123 {
124   myText = txt;
125 }
126
127 /*!
128   \brief Get title alignment flags.
129   \return title alignment flags
130   \internal
131 */
132 Qt::Alignment QtxMenu::Title::alignment() const
133 {
134   return myAlignment;
135 }
136
137 /*!
138   \brief Set title alignment flags.
139   \param a title alignment flags
140   \internal
141 */
142 void QtxMenu::Title::setAlignment( const Qt::Alignment a )
143 {
144   myAlignment = a;
145 }
146
147 /*!
148   \brief Get recommended size for the title item widget.
149   \return title item widget size
150   \internal
151 */
152 QSize QtxMenu::Title::sizeHint() const
153 {
154   int m = 5;
155   QTextDocument doc;
156   doc.setHtml( text() );
157
158   QSize sz = icon().isNull() ? QSize( 0, 0 ) : icon().actualSize( QSize( 16, 16 ) );
159   sz.setWidth( 2 * m + sz.width() + (int)doc.size().width() );
160   sz.setHeight( 2 * m + qMax( sz.height(), (int)doc.size().height() ) );
161   return sz;
162 }
163
164 /*!
165   \brief Get recommended minimum size for the title item widget.
166   \return title item widget minimum size
167   \internal
168 */
169 QSize QtxMenu::Title::minimumSizeHint() const
170 {
171   return sizeHint();
172 }
173
174 /*!
175   \brief Paint the title item widget.
176   \param e paint event (not used)
177   \internal
178 */
179 void QtxMenu::Title::paintEvent( QPaintEvent* /*e*/ )
180 {
181   int m = 5;
182   QIcon ico = icon();
183   QString txt = text();
184   Qt::Alignment align = alignment();
185
186   QRect base = rect();
187   base.setTop( base.top() + 1 );
188   base.setLeft( base.left() + 1 );
189   base.setRight( base.right() -1 );
190   base.setBottom( base.bottom() - 1 );
191
192   QTextDocument doc;
193   doc.setHtml( txt );
194
195   QSize isz = ico.isNull() ? QSize( 0, 0 ) : ico.actualSize( QSize( 16, 16 ) );
196   QSize sz( (int)doc.size().width(), (int)doc.size().height() );
197
198   QPainter p( this );
199   QAbstractTextDocumentLayout::PaintContext ctx;
200   ctx.palette.setColor( QPalette::Text, palette().color( QPalette::Light ) );
201
202   QLinearGradient linearGrad( base.topLeft(), base.topRight() );
203   linearGrad.setColorAt( 0, palette().color( QPalette::Highlight ) );
204   linearGrad.setColorAt( 1, palette().color( QPalette::Window ) );
205
206   p.fillRect( base, linearGrad );
207
208   QPoint start = base.topLeft() + QPoint( m, m );
209   if ( align & Qt::AlignLeft )
210     start.setX( base.left() + m );
211   else if ( align & Qt::AlignRight )
212     start.setX( base.right() - m - isz.width() - sz.width() );
213   else if ( align & Qt::AlignHCenter )
214     start.setX( base.left() + ( base.width() - isz.width() - sz.width() ) / 2 );
215
216   if ( align & Qt::AlignTop )
217     start.setY( base.top() + m );
218   else if ( align & Qt::AlignBottom )
219     start.setY( base.bottom() - m - qMax( isz.height(), - sz.height() ) );
220   else if ( align & Qt::AlignVCenter )
221     start.setY( base.top() + ( base.height() - qMax( isz.height(), sz.height() ) ) / 2 );
222
223   if ( !ico.isNull() )
224   {
225     ico.paint( &p, QRect( start, isz ) );
226     start.setX( start.x() + isz.width() );
227   }
228
229   p.save();
230   p.translate( start );
231   doc.documentLayout()->draw( &p, ctx );
232   p.restore();
233 }
234
235 /*!
236   \class QtxMenu
237   \brief The class QtxMenu represents the popup menu with the title.
238
239   The title for the popup menu can be set via setTitleText() method.
240   In addition, title item can contain the icon, which can be set using
241   setTitleIcon() method. Current title text and icon can be retrieved with
242   titleText() and titleIcon() methods.
243
244   The title text alignment flags can be changed using setTitleAlignment()
245   method and retrieved with titleAlignment() method.
246
247   By default, QtxMenu::TitleAuto mode is used. In this mode, the title item
248   is shown only if it is not empty. To show title always (even empty), pass
249   QtxMenu::TitleOn to the setTitleMode() method. To hide the title, use 
250   setTitleMode() method with QtxMenu::TitleOff parameter.
251 */
252
253 /*!
254   \brief Constructor.
255   \param parent parent widget
256 */
257 QtxMenu::QtxMenu( QWidget* parent )
258 : QMenu( parent ),
259   myMode( TitleAuto )
260 {
261   myTitle = new Title( this );
262   myAction = new QWidgetAction( this );
263   myAction->setDefaultWidget( myTitle );
264 }
265
266 /*!
267   \brief Destructor.
268 */
269 QtxMenu::~QtxMenu()
270 {
271 }
272
273 /*!
274   \brief Get title menu text.
275   \return menu text for the title item
276 */
277 QString QtxMenu::titleText() const
278 {
279   return myTitle->text();
280 }
281
282 /*!
283   \brief Get title icon.
284   \return title item icon
285 */
286 QIcon QtxMenu::titleIcon() const
287 {
288   return myTitle->icon();
289 }
290
291 /*!
292   \brief Get title item display mode.
293   \return popup menu title display mode (QtxMenu::TitleMode)
294 */
295 QtxMenu::TitleMode QtxMenu::titleMode() const
296 {
297   return myMode;
298 }
299
300 /*!
301   \brief Get title alignment flags.
302   \return title alignment flags
303 */
304 Qt::Alignment QtxMenu::titleAlignment() const
305 {
306   return myTitle->alignment();
307 }
308
309 /*!
310   \brief Set title menu text.
311   \param txt menu text to be used for the title item
312 */
313 void QtxMenu::setTitleText( const QString& txt )
314 {
315   if ( titleText() == txt )
316     return;
317
318   myTitle->setText( txt );
319
320   updateTitle();
321 }
322
323 /*!
324   \brief Set title icon.
325   \param ico title item icon
326 */
327 void QtxMenu::setTitleIcon( const QIcon& ico )
328 {
329   myTitle->setIcon( ico );
330
331   updateTitle();
332 }
333
334 /*!
335   \brief Set title item display mode.
336   \param m popup menu title display mode (QtxMenu::TitleMode)
337 */
338 void QtxMenu::setTitleMode( const QtxMenu::TitleMode m )
339 {
340   if ( myMode == m )
341     return;
342
343   myMode = m;
344
345   updateTitle();
346 }
347
348 /*!
349   \brief Set title alignment flags.
350   \param a title alignment flags
351 */
352 void QtxMenu::setTitleAlignment( const Qt::Alignment a )
353 {
354   if ( titleAlignment() == a )
355     return;
356
357   myTitle->setAlignment( a );
358
359   updateTitle();
360 }
361
362 /*!
363   \brief Append group title to the end of the menu.
364   \param text group title's text
365 */
366 void QtxMenu::addGroup( const QString& text )
367 {
368   Title* aTitle = new Title( this );
369   aTitle->setText( text );
370
371   QWidgetAction* anAction = new QWidgetAction( this );
372   anAction->setDefaultWidget( aTitle );
373
374   addAction( anAction );
375 }
376
377 /*!
378   \brief Append group title to the end of the menu.
379   \param icon group title's icon
380   \param text group title's text
381 */
382 void QtxMenu::addGroup( const QIcon& icon, const QString& text )
383 {
384   Title* aTitle = new Title( this );
385   aTitle->setText( text );
386   aTitle->setIcon( icon );
387
388   QWidgetAction* anAction = new QWidgetAction( this );
389   anAction->setDefaultWidget( aTitle );
390
391   addAction( anAction );
392 }
393
394 /*!
395   \brief Customize show/hide menu operation.
396   \param on new popup menu visibility state
397 */
398 void QtxMenu::setVisible( bool on )
399 {
400   if ( on )
401     insertTitle();
402
403   QMenu::setVisible( on );
404
405   if ( !on )
406     removeTitle();
407 }
408
409 /*!
410   \brief Insert title item to the popup menu.
411 */
412 void QtxMenu::insertTitle()
413 {
414   if ( titleMode() == TitleOff || ( titleMode() == TitleAuto && titleText().trimmed().isEmpty() ) )
415     return;
416
417   if ( actions().isEmpty() )
418     addAction( myAction );
419   else
420     insertAction( actions().first(), myAction );
421 }
422
423 /*!
424   \brief Remove title item from the popup menu.
425 */
426 void QtxMenu::removeTitle()
427 {
428   if ( actions().contains( myAction ) )
429     removeAction( myAction );
430 }
431
432 /*!
433   \brief Update title item.
434 */
435 void QtxMenu::updateTitle()
436 {
437   if ( !actions().contains( myAction ) )
438     return;
439
440   removeTitle();
441   insertTitle();
442 }
443
444 /*!
445   \brief Paint event
446 */
447 void QtxMenu::paintEvent( QPaintEvent* e )
448 {
449   // Force menu resizing, see resizeAfterRepaint().
450   QMenu::paintEvent(e);
451   QTimer::singleShot( 0, this, SLOT( resizeAfterRepaint() ) );
452 }
453
454 void QtxMenu::resizeAfterRepaint()
455 {
456   // this slot is used as a workaround about annoying problem
457   // on some X window System desktops like KDE 5, Unity and other
458   // that causes setting incorrect menu's geometry
459   // after it appears on a screen.
460   resize( sizeHint() );
461 }