Salome HOME
Update from BR_V5_DEV 13Feb2009
[modules/gui.git] / src / Qtx / QtxColorButton.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File:      QtxColorButton.cxx
23 // Author:    Sergey TELKOV
24 //
25 #include "QtxColorButton.h"
26
27 #include <QMenu>
28 #include <QStyle>
29 #include <QLayout>
30 #include <QBitmap>
31 #include <QPainter>
32 #include <QPaintEvent>
33 #include <QColorDialog>
34 #include <QStyleOptionToolButton>
35
36 /*!
37   \class QtxColorButton
38   \brief The QtxColorButton class implements a widget for color
39   preference items editing.
40
41   The color preference item is represented as the colored button with
42   assocoiated popup menu whihc is called when the user presses the small
43   arrow button near it. The popup menu allows selecting of the color
44   from the predefined set. In addition it contains the button which
45   invokes standard "Select color" dialog box.
46
47   Initial color value can be set with setColor() method. Chosen color
48   can be retrieved with the color() method.
49 */
50
51 /*!
52   \brief Constructor.
53   \param parent parent widget
54 */
55 QtxColorButton::QtxColorButton( QWidget* parent )
56 : QToolButton( parent )
57 {
58   setCheckable( false );
59   setPopupMode( MenuButtonPopup );
60
61   QMenu* pm = new QMenu( this );
62   QGridLayout* grid = new QGridLayout( pm );
63   grid->setMargin( 5 );
64   grid->setSpacing( 0 );
65
66   QList<QColor> cList = colorsList();
67   int w = 8;
68   int h = cList.count() / w;
69
70   for ( int y = 0; y < h; y++ )
71   {
72     for ( int x = 0; x < w; x++ )
73     {
74       QColor c = cList.at( x * h + y ).toRgb();
75       QToolButton* btn = new QToolButton( pm );
76       btn->setAutoRaise( true );
77       btn->setCheckable( true );
78       myColors.insert( btn, c );
79       grid->addWidget( btn, y + 1, x );
80
81       btn->installEventFilter( this );
82
83       connect( btn, SIGNAL( clicked( bool ) ), this, SLOT( onToggled( bool ) ) );
84
85       updateButton( btn );
86     }
87   }
88
89   myAutoButton = new QToolButton( pm );
90   myAutoButton->setText( tr( "Auto" ) );
91   myAutoButton->setAutoRaise( true );
92   myAutoButton->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
93   grid->addWidget( myAutoButton, 0, 0, 1, grid->columnCount() );
94   connect( myAutoButton, SIGNAL( clicked( bool ) ), this, SLOT( onAutoClicked( bool ) ) );
95
96   QToolButton* other = new QToolButton( pm );
97   other->setText( tr( "Other colors..." ) );
98   other->setAutoRaise( true );
99   other->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
100   grid->addWidget( other, grid->rowCount(), 0, 1, grid->columnCount() );
101   connect( other, SIGNAL( clicked( bool ) ), this, SLOT( onDialogClicked( bool ) ) );
102
103   other->installEventFilter( this );
104
105   setMenu( pm );
106
107   connect( this, SIGNAL( clicked( bool ) ), this, SLOT( onClicked( bool ) ) );
108   connect( pm,   SIGNAL( aboutToShow() ),   this, SLOT( onAboutToShow() ) );
109
110   myAutoButton->setVisible( false );
111 }
112
113 /*!
114   \brief Destructor.
115 */
116 QtxColorButton::~QtxColorButton()
117 {
118 }
119
120 /*!
121   \brief Get currently selected color.
122
123   Returns null QColor if no color is selected.
124
125   \return selected color
126   \sa setColor()
127 */
128 QColor QtxColorButton::color() const
129 {
130   return myColors.contains( this ) ? myColors[this] : QColor();
131 }
132
133 /*!
134   \brief Set color.
135   \param c color to be set as current
136   \sa color()
137 */
138 void QtxColorButton::setColor( const QColor& c )
139 {
140   myColors.insert( this, c );
141   updateState();
142   update();
143   updateButton( this );
144 }
145
146 /*!
147   \brief Returns the status of "auto" color button in popup widget.
148   \return \c true if the "auto" button is enabled
149 */
150 bool QtxColorButton::isAutoEnabled() const
151 {
152   return myAutoButton->isVisibleTo( myAutoButton->parentWidget() );
153 }
154
155 /*!
156   \brief Enable/disable the "auto" color button in popup widget.
157   \param on enable/disable state
158 */
159 void QtxColorButton::setAutoEnabled( bool on )
160 {
161   myAutoButton->setVisible( on );
162 }
163
164 /*!
165   \brief Returns text of the "auto" color button in popup widget.
166 */
167 QString QtxColorButton::autoText() const
168 {
169   return myAutoButton->text();
170 }
171
172 /*!
173   \brief Sets text of the "auto" color button in popup widget.
174   \param txt new button text
175 */
176 void QtxColorButton::setAutoText( const QString& txt )
177 {
178   myAutoButton->setText( txt );
179 }
180
181 /*!
182   \brief Filter events for the child widgets.
183   \param o event receiver object
184   \param e event
185   \return \c true if the event should be filtered
186 */
187 bool QtxColorButton::eventFilter( QObject* o, QEvent* e )
188 {
189   if ( e->type() == QEvent::Leave )
190     updateButton( ::qobject_cast<QToolButton*>( o ) );
191   return QToolButton::eventFilter( o, e );
192 }
193
194 /*!
195   \brief Called when the popup menu is about to show.
196
197   Updates the menu and child widgets state.
198 */
199 void QtxColorButton::onAboutToShow()
200 {
201   updateState();
202 }
203
204 /*!
205   \brief Called when the button is clicked by the user.
206
207   Emits the signal clicked( QColor ).
208
209   \param on button state (not used)
210 */
211 void QtxColorButton::onClicked( bool )
212 {
213   emit clicked( color() );
214 }
215
216 /*!
217   \brief Called when any color selection button from popup menu
218   is clicked.
219
220   Changes the currently selected color and emits the signal
221   changed( QColor ).
222
223   \param on button state
224 */
225 void QtxColorButton::onToggled( bool on )
226 {
227   const QToolButton* tb = ::qobject_cast<QToolButton*>( sender() );
228   if ( !tb )
229     return;
230
231   QColor old = color();
232
233   if ( on && myColors.contains( tb ) )
234   {
235     myColors.insert( this, myColors[tb] );
236     updateButton( this );
237   }
238
239   if ( menu() )
240     menu()->hide();
241
242   updateState();
243
244   if ( old != color() )
245     emit changed( color() );
246 }
247
248 /*!
249   \brief Called the "Auto" child button from popup menu
250   is clicked.
251
252   Sets the undefined (auto) color as current.
253
254   \param on (not used)
255 */
256 void QtxColorButton::onAutoClicked( bool )
257 {
258   if ( menu() )
259     menu()->hide();
260
261   setColor( QColor() );
262 }
263
264 /*!
265   \brief Called the "Other colors" child button from popup menu
266   is clicked.
267
268   Invokes standard "Select color" dialog box allowing user to select
269   custom color. If the current color is changed by the user, emits
270   the signal changed( QColor ).
271
272   \param on (not used)
273 */
274 void QtxColorButton::onDialogClicked( bool )
275 {
276   QColor c = QColorDialog::getColor( color(), this );
277   if ( !c.isValid() )
278     return;
279
280   QColor old = color();
281
282   setColor( c );
283
284   if ( old != color() )
285     emit changed( color() );
286 }
287
288 /*!
289   \brief Customize paint event for the widget.
290   \param e paint event
291 */
292 void QtxColorButton::paintEvent( QPaintEvent* e )
293 {
294   QToolButton::paintEvent( e );
295
296   QStyleOptionToolButton opt;
297   opt.initFrom( this );
298   opt.text = text();
299   opt.icon = icon();
300   opt.features = QStyleOptionToolButton::Menu;
301
302   QRect r = style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton );
303   r.setTopLeft( r.topLeft() + QPoint( 2, 2 ) );
304   r.setBottomRight( r.bottomRight() - QPoint( 2, 2 ) );
305
306   QPixmap pix( r.size() );
307   pix.fill( palette().color( backgroundRole() ) );
308
309   if ( color().isValid() )
310     drawColor( &pix, color() );
311   else {
312     QPainter pixp( &pix );
313     pixp.drawRect( 2, 2, pix.width() - 4, pix.height() - 4 );
314     pixp.fillRect( 3, 3, pix.width() - 6, pix.height() - 6, QBrush( 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 = qFindChildren<QToolButton*>( menu() );
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 int m ) const
386 {
387   if ( !pd )
388     return;
389
390   QPainter p( pd );
391   p.setPen( Qt::black );
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   \fn void QtxColorButton::clicked( QColor color );
418   \brief This signal is emitted when the widget button is clicked by
419          the user.
420   \param color current color
421 */
422
423 /*!
424   \fn void QtxColorButton::changed( QColor color );
425   \brief This signal is emitted when the current color is changed.
426   \param color new current color
427 */