Salome HOME
updated copyright message
[modules/gui.git] / src / Qtx / QtxColorButton.cxx
1 // Copyright (C) 2007-2023  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:      QtxColorButton.cxx
21 // Author:    Sergey TELKOV
22 //
23 #include "QtxColorButton.h"
24
25 #include <QMenu>
26 #include <QStyle>
27 #include <QLayout>
28 #include <QBitmap>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QColorDialog>
32 #include <QStyleOptionToolButton>
33
34 /*!
35   \class QtxColorButton
36   \brief The QtxColorButton class implements a widget for color
37   preference items editing.
38
39   The color preference item is represented as the colored button with
40   assocoiated popup menu whihc is called when the user presses the small
41   arrow button near it. The popup menu allows selecting of the color
42   from the predefined set. In addition it contains the button which
43   invokes standard "Select color" dialog box.
44
45   Initial color value can be set with setColor() method. Chosen color
46   can be retrieved with the color() method.
47 */
48
49 /*!
50   \brief Constructor.
51   \param parent parent widget
52 */
53 QtxColorButton::QtxColorButton( QWidget* parent )
54 : QToolButton( parent )
55 {
56   setCheckable( false );
57   setPopupMode( InstantPopup );
58
59   QMenu* pm = new QMenu( this );
60   QGridLayout* grid = new QGridLayout( pm );
61   grid->setMargin( 5 );
62   grid->setSpacing( 0 );
63
64   QList<QColor> cList = colorsList();
65   int w = 8;
66   int h = cList.count() / w;
67
68   for ( int y = 0; y < h; y++ )
69   {
70     for ( int x = 0; x < w; x++ )
71     {
72       QColor c = cList.at( x * h + y ).toRgb();
73       QToolButton* btn = new QToolButton( pm );
74       btn->setAutoRaise( true );
75       btn->setCheckable( true );
76       myColors.insert( btn, c );
77       grid->addWidget( btn, y + 1, x );
78
79       btn->installEventFilter( this );
80
81       connect( btn, SIGNAL( clicked( bool ) ), this, SLOT( onToggled( bool ) ) );
82
83       updateButton( btn );
84     }
85   }
86
87   myAutoButton = new QToolButton( pm );
88   myAutoButton->setText( tr( "Auto" ) );
89   myAutoButton->setAutoRaise( true );
90   myAutoButton->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
91   grid->addWidget( myAutoButton, 0, 0, 1, grid->columnCount() );
92   connect( myAutoButton, SIGNAL( clicked( bool ) ), this, SLOT( onAutoClicked( bool ) ) );
93
94   QToolButton* other = new QToolButton( pm );
95   other->setText( tr( "Other colors..." ) );
96   other->setAutoRaise( true );
97   other->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
98   grid->addWidget( other, grid->rowCount(), 0, 1, grid->columnCount() );
99   connect( other, SIGNAL( clicked( bool ) ), this, SLOT( onDialogClicked( bool ) ) );
100
101   other->installEventFilter( this );
102
103   setMenu( pm );
104
105   connect( this, SIGNAL( clicked( bool ) ), this, SLOT( onClicked( bool ) ) );
106   connect( pm,   SIGNAL( aboutToShow() ),   this, SLOT( onAboutToShow() ) );
107
108   myAutoButton->setVisible( false );
109 }
110
111 /*!
112   \brief Destructor.
113 */
114 QtxColorButton::~QtxColorButton()
115 {
116 }
117
118 /*!
119   \brief Get currently selected color.
120
121   Returns null QColor if no color is selected.
122
123   \return selected color
124   \sa setColor()
125 */
126 QColor QtxColorButton::color() const
127 {
128   return myColors.contains( this ) ? myColors[this] : QColor();
129 }
130
131 /*!
132   \brief Set color.
133   \param c color to be set as current
134   \sa color()
135 */
136 void QtxColorButton::setColor( const QColor& c )
137 {
138   myColors.insert( this, c );
139   updateState();
140   update();
141   updateButton( this );
142 }
143
144 /*!
145   \brief Returns the status of "auto" color button in popup widget.
146   \return \c true if the "auto" button is enabled
147 */
148 bool QtxColorButton::isAutoEnabled() const
149 {
150   return myAutoButton->isVisibleTo( myAutoButton->parentWidget() );
151 }
152
153 /*!
154   \brief Enable/disable the "auto" color button in popup widget.
155   \param on enable/disable state
156 */
157 void QtxColorButton::setAutoEnabled( bool on )
158 {
159   myAutoButton->setVisible( on );
160 }
161
162 /*!
163   \brief Returns text of the "auto" color button in popup widget.
164 */
165 QString QtxColorButton::autoText() const
166 {
167   return myAutoButton->text();
168 }
169
170 /*!
171   \brief Sets text of the "auto" color button in popup widget.
172   \param txt new button text
173 */
174 void QtxColorButton::setAutoText( const QString& txt )
175 {
176   myAutoButton->setText( txt );
177 }
178
179 /*!
180   \brief Filter events for the child widgets.
181   \param o event receiver object
182   \param e event
183   \return \c true if the event should be filtered
184 */
185 bool QtxColorButton::eventFilter( QObject* o, QEvent* e )
186 {
187   if ( e->type() == QEvent::Leave )
188     updateButton( ::qobject_cast<QToolButton*>( o ) );
189   return QToolButton::eventFilter( o, e );
190 }
191
192 /*!
193   \brief Called when the popup menu is about to show.
194
195   Updates the menu and child widgets state.
196 */
197 void QtxColorButton::onAboutToShow()
198 {
199   updateState();
200 }
201
202 /*!
203   \brief Called when the button is clicked by the user.
204
205   Emits the signal clicked( QColor ).
206
207   \param on button state (not used)
208 */
209 void QtxColorButton::onClicked( bool )
210 {
211   emit clicked( color() );
212 }
213
214 /*!
215   \brief Called when any color selection button from popup menu
216   is clicked.
217
218   Changes the currently selected color and emits the signal
219   changed( QColor ).
220
221   \param on button state
222 */
223 void QtxColorButton::onToggled( bool on )
224 {
225   const QToolButton* tb = ::qobject_cast<QToolButton*>( sender() );
226   if ( !tb )
227     return;
228
229   QColor old = color();
230
231   if ( on && myColors.contains( tb ) )
232   {
233     myColors.insert( this, myColors[tb] );
234     updateButton( this );
235   }
236
237   if ( menu() )
238     menu()->hide();
239
240   updateState();
241
242   if ( old != color() )
243     emit changed( color() );
244 }
245
246 /*!
247   \brief Called the "Auto" child button from popup menu
248   is clicked.
249
250   Sets the undefined (auto) color as current.
251
252   \param on (not used)
253 */
254 void QtxColorButton::onAutoClicked( bool )
255 {
256   if ( menu() )
257     menu()->hide();
258
259   setColor( QColor() );
260 }
261
262 /*!
263   \brief Called the "Other colors" child button from popup menu
264   is clicked.
265
266   Invokes standard "Select color" dialog box allowing user to select
267   custom color. If the current color is changed by the user, emits
268   the signal changed( QColor ).
269
270   \param on (not used)
271 */
272 void QtxColorButton::onDialogClicked( bool )
273 {
274   QColor c = QColorDialog::getColor( color(), this );
275   if ( !c.isValid() )
276     return;
277
278   QColor old = color();
279
280   setColor( c );
281
282   if ( old != color() )
283     emit changed( color() );
284 }
285
286 /*!
287   \brief Customize paint event for the widget.
288   \param e paint event
289 */
290 void QtxColorButton::paintEvent( QPaintEvent* e )
291 {
292   QToolButton::paintEvent( e );
293
294   QStyleOptionToolButton opt;
295   opt.initFrom( this );
296   opt.text = text();
297   opt.icon = icon();
298   opt.features = QStyleOptionToolButton::Menu;
299
300   QRect r = style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton );
301   r.setTopLeft( r.topLeft() + QPoint( 2, 2 ) );
302   r.setBottomRight( r.bottomRight() - QPoint( 2, 2 ) );
303
304   QPixmap pix( r.size() );
305   pix.fill( palette().color( backgroundRole() ) );
306
307   if ( color().isValid() )
308     drawColor( &pix, isEnabled() ? color() : palette().mid().color(), isEnabled() ? Qt::black : palette().mid().color() );
309   else {
310     QPainter pixp( &pix );
311     pixp.setPen( palette().color( isEnabled() ? QPalette::WindowText : QPalette::Mid ) );
312     pixp.drawRect( 2, 2, pix.width() - 4, pix.height() - 4 );
313     pixp.fillRect( 3, 3, pix.width() - 6, pix.height() - 6, 
314                    QBrush( palette().color( isEnabled() ? QPalette::WindowText : QPalette::Mid ), Qt::BDiagPattern ) );
315     pixp.end();
316   }
317
318   QPainter p( this );
319   p.drawPixmap( r, pix );
320   p.end();
321 }
322
323 /*!
324   \brief Update widget state.
325 */
326 void QtxColorButton::updateState()
327 {
328   QList<QToolButton*> bList = menu()->findChildren<QToolButton*>();
329   for ( QList<QToolButton*>::iterator cit = bList.begin(); cit != bList.end(); ++cit )
330     updateButton( *cit );
331 }
332
333 /*!
334   \brief Update child button state.
335   \param btn child button
336 */
337 void QtxColorButton::updateButton( QToolButton* btn )
338 {
339   QColor c = color().toRgb();
340   bool block = btn->signalsBlocked();
341   btn->blockSignals( true );
342   btn->setChecked( false );
343   if ( myColors.contains( btn ) ) {
344     btn->setIcon( buttonIcon( myColors[btn] ) );
345     btn->setChecked( myColors[btn].toRgb() == c );
346   }
347   btn->setDown( false );
348   btn->blockSignals( block );
349 }
350
351 /*!
352   \brief Generate (if necessary) or get the icon for the button.
353   \param c color to be used for the icon
354   \return icon pixmap for the button
355 */
356 QPixmap QtxColorButton::buttonIcon( const QColor& c ) const
357 {
358   static QMap<int, QPixmap> pixMap;
359
360   if ( pixMap.contains( c.rgb() ) )
361     return pixMap[c.rgb()];
362
363   QPixmap pix( 16, 16 );
364
365   QColor bg = Qt::white;
366   if ( bg == c )
367     bg = Qt::gray;
368   pix.fill( bg );
369
370   drawColor( &pix, c );
371
372   pix.setMask( pix.createHeuristicMask() );
373
374   pixMap.insert( c.rgb(), pix );
375
376   return pix;
377 }
378
379 /*!
380   \brief Draw pixmap.
381   \param pd paint device
382   \param c color
383   \param m margin
384 */
385 void QtxColorButton::drawColor( QPaintDevice* pd, const QColor& c, const QColor& bc, const int m ) const
386 {
387   if ( !pd )
388     return;
389
390   QPainter p( pd );
391   p.setPen( bc );
392   p.fillRect( m, m, pd->width() - 2 * m - 1, pd->height() - 2 * m - 1, QBrush( c ) );
393   p.drawRect( m, m, pd->width() - 2 * m - 1, pd->height() - 2 * m - 1 );
394   p.end();
395 }
396
397 /*!
398   \brief Get predefined list of colors to be used in the popup menu.
399   \return list of colors
400 */
401 QList<QColor> QtxColorButton::colorsList() const
402 {
403   QList<QColor> lst;
404
405   for ( int g = 0; g < 4; g++ )
406   {
407     for ( int r = 0; r < 4; r++ )
408     {
409       for ( int b = 0; b < 3; b++ )
410         lst.append( QColor( qRgb( r * 255 / 3, g * 255 / 3, b * 255 / 2 ) ) );
411     }
412   }
413   return lst;
414 }
415
416 /*!
417   \brief Get size hint for this widget.
418 */
419 QSize QtxColorButton::sizeHint() const
420 {
421   QSize sz = QToolButton::sizeHint();
422   sz.setWidth( sz.width() + 18 );
423   return sz;
424 }
425
426
427 /*!
428   \fn void QtxColorButton::clicked( QColor color );
429   \brief This signal is emitted when the widget button is clicked by
430          the user.
431   \param color current color
432 */
433
434 /*!
435   \fn void QtxColorButton::changed( QColor color );
436   \brief This signal is emitted when the current color is changed.
437   \param color new current color
438 */