subtraction of already painted area: be fool and
[kdelibs.git] / kdeui / kcolordialog.cpp
blob94ee0c70ed3529b443badfcc99b48d5d012fd44d
1 /* This file is part of the KDE libraries
2 Copyright (C) 1997 Martin Jones (mjones@kde.org)
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
17 Boston, MA 02110-1301, USA.
19 //-----------------------------------------------------------------------------
20 // KDE color selection dialog.
22 // 1999-09-27 Espen Sand <espensa@online.no>
23 // KColorDialog is now subclassed from KDialogBase. I have also extended
24 // KColorDialog::getColor() so that it contains a parent argument. This
25 // improves centering capability.
27 // layout management added Oct 1997 by Mario Weilguni
28 // <mweilguni@sime.com>
31 #include <stdio.h>
32 #include <stdlib.h>
34 #include <qcheckbox.h>
35 #include <qcombobox.h>
36 #include <qdrawutil.h>
37 #include <qevent.h>
38 #include <qfile.h>
39 #include <qimage.h>
40 #include <qlabel.h>
41 #include <qlayout.h>
42 #include <qlineedit.h>
43 #include <qvalidator.h>
44 #include <qpainter.h>
45 #include <qpushbutton.h>
46 #include <qspinbox.h>
47 #include <qtimer.h>
49 #include <kapplication.h>
50 #include <kconfig.h>
51 #include <kglobal.h>
52 #include <kglobalsettings.h>
53 #include <kiconloader.h>
54 #include <klistbox.h>
55 #include <klocale.h>
56 #include <kmessagebox.h>
57 #include <kseparator.h>
58 #include <kpalette.h>
59 #include <kimageeffect.h>
61 #include "kcolordialog.h"
62 #include "kcolordrag.h"
63 #include "kstaticdeleter.h"
64 #include <config.h>
65 #include <kdebug.h>
67 #include "config.h"
68 #ifdef Q_WS_X11
69 #include <X11/Xlib.h>
71 // defined in qapplication_x11.cpp
72 typedef int (*QX11EventFilter) (XEvent*);
73 extern QX11EventFilter qt_set_x11_event_filter (QX11EventFilter filter);
74 #endif
76 struct ColorPaletteNameType
78 const char* m_fileName;
79 const char* m_displayName;
82 const ColorPaletteNameType colorPaletteName[]=
84 { "Recent_Colors", I18N_NOOP2( "palette name", "* Recent Colors *" ) },
85 { "Custom_Colors", I18N_NOOP2( "palette name", "* Custom Colors *" ) },
86 { "40.colors", I18N_NOOP2( "palette name", "Forty Colors" ) },
87 { "Royal.colors", I18N_NOOP2( "palette name", "Royal Colors" ) },
88 { "Web.colors", I18N_NOOP2( "palette name", "Web Colors" ) },
89 { 0, 0 } // end of data
92 const int recentColorIndex = 0;
94 class KColorSpinBox : public QSpinBox
96 public:
97 KColorSpinBox(int minValue, int maxValue, int step, QWidget* parent)
98 : QSpinBox(minValue, maxValue, step, parent, "kcolorspinbox")
99 { }
101 // Override Qt's braindead auto-selection.
102 virtual void valueChange()
104 updateDisplay();
105 emit valueChanged( value() );
106 emit valueChanged( currentValueText() );
112 #define STANDARD_PAL_SIZE 17
114 KColor::KColor()
115 : QColor()
117 r = 0; g = 0; b = 0; h = 0; s = 0; v = 0;
120 KColor::KColor( const KColor &col)
121 : QColor( col )
123 h = col.h; s = col.s; v = col.v;
124 r = col.r; g = col.g; b = col.b;
127 KColor::KColor( const QColor &col)
128 : QColor( col )
130 QColor::getRgb(&r, &g, &b);
131 QColor::getHsv(&h, &s, &v);
134 bool KColor::operator==(const KColor& col) const
136 return (h == col.h) && (s == col.s) && (v == col.v) &&
137 (r == col.r) && (g == col.g) && (b == col.b);
140 KColor& KColor::operator=(const KColor& col)
142 *(QColor *)this = col;
143 h = col.h; s = col.s; v = col.v;
144 r = col.r; g = col.g; b = col.b;
145 return *this;
148 void
149 KColor::setHsv(int _h, int _s, int _v)
151 h = _h; s = _s; v = _v;
152 QColor::setHsv(h, s, v);
153 QColor::rgb(&r, &g, &b);
156 void
157 KColor::setRgb(int _r, int _g, int _b)
159 r = _r; g = _g; b = _b;
160 QColor::setRgb(r, g, b);
161 QColor::hsv(&h, &s, &v);
164 void
165 KColor::rgb(int *_r, int *_g, int *_b) const
167 *_r = r; *_g = g; *_b = b;
170 void
171 KColor::hsv(int *_h, int *_s, int *_v) const
173 *_h = h; *_s = s; *_v = v;
177 static QColor *standardPalette = 0;
178 static KStaticDeleter<QColor> spd;
180 static void createStandardPalette()
182 if ( standardPalette )
183 return;
185 spd.setObject(standardPalette, new QColor [STANDARD_PAL_SIZE], true/*array*/);
187 int i = 0;
189 standardPalette[i++] = Qt::red;
190 standardPalette[i++] = Qt::green;
191 standardPalette[i++] = Qt::blue;
192 standardPalette[i++] = Qt::cyan;
193 standardPalette[i++] = Qt::magenta;
194 standardPalette[i++] = Qt::yellow;
195 standardPalette[i++] = Qt::darkRed;
196 standardPalette[i++] = Qt::darkGreen;
197 standardPalette[i++] = Qt::darkBlue;
198 standardPalette[i++] = Qt::darkCyan;
199 standardPalette[i++] = Qt::darkMagenta;
200 standardPalette[i++] = Qt::darkYellow;
201 standardPalette[i++] = Qt::white;
202 standardPalette[i++] = Qt::lightGray;
203 standardPalette[i++] = Qt::gray;
204 standardPalette[i++] = Qt::darkGray;
205 standardPalette[i++] = Qt::black;
209 KHSSelector::KHSSelector( QWidget *parent, const char *name )
210 : KXYSelector( parent, name )
212 setRange( 0, 0, 359, 255 );
215 void KHSSelector::updateContents()
217 drawPalette(&pixmap);
220 void KHSSelector::resizeEvent( QResizeEvent * )
222 updateContents();
225 void KHSSelector::drawContents( QPainter *painter )
227 painter->drawPixmap( contentsRect().x(), contentsRect().y(), pixmap );
230 void KHSSelector::drawPalette( QPixmap *pixmap )
232 int xSize = contentsRect().width(), ySize = contentsRect().height();
233 QImage image( xSize, ySize, 32 );
234 QColor col;
235 int h, s;
236 uint *p;
238 for ( s = ySize-1; s >= 0; s-- )
240 p = (uint *) image.scanLine( ySize - s - 1 );
241 for( h = 0; h < xSize; h++ )
243 col.setHsv( 359*h/(xSize-1), 255*s/(ySize-1), 192 );
244 *p = col.rgb();
245 p++;
249 if ( QColor::numBitPlanes() <= 8 )
251 createStandardPalette();
252 KImageEffect::dither( image, standardPalette, STANDARD_PAL_SIZE );
254 pixmap->convertFromImage( image );
258 //-----------------------------------------------------------------------------
260 KValueSelector::KValueSelector( QWidget *parent, const char *name )
261 : KSelector( KSelector::Vertical, parent, name ), _hue(0), _sat(0)
263 setRange( 0, 255 );
264 pixmap.setOptimization( QPixmap::BestOptim );
267 KValueSelector::KValueSelector(Orientation o, QWidget *parent, const char *name
269 : KSelector( o, parent, name), _hue(0), _sat(0)
271 setRange( 0, 255 );
272 pixmap.setOptimization( QPixmap::BestOptim );
275 void KValueSelector::updateContents()
277 drawPalette(&pixmap);
280 void KValueSelector::resizeEvent( QResizeEvent * )
282 updateContents();
285 void KValueSelector::drawContents( QPainter *painter )
287 painter->drawPixmap( contentsRect().x(), contentsRect().y(), pixmap );
290 void KValueSelector::drawPalette( QPixmap *pixmap )
292 int xSize = contentsRect().width(), ySize = contentsRect().height();
293 QImage image( xSize, ySize, 32 );
294 QColor col;
295 uint *p;
296 QRgb rgb;
298 if ( orientation() == KSelector::Horizontal )
300 for ( int v = 0; v < ySize; v++ )
302 p = (uint *) image.scanLine( ySize - v - 1 );
304 for( int x = 0; x < xSize; x++ )
306 col.setHsv( _hue, _sat, 255*x/(xSize-1) );
307 rgb = col.rgb();
308 *p++ = rgb;
313 if( orientation() == KSelector::Vertical )
315 for ( int v = 0; v < ySize; v++ )
317 p = (uint *) image.scanLine( ySize - v - 1 );
318 col.setHsv( _hue, _sat, 255*v/(ySize-1) );
319 rgb = col.rgb();
320 for ( int i = 0; i < xSize; i++ )
321 *p++ = rgb;
325 if ( QColor::numBitPlanes() <= 8 )
327 createStandardPalette();
328 KImageEffect::dither( image, standardPalette, STANDARD_PAL_SIZE );
330 pixmap->convertFromImage( image );
333 //-----------------------------------------------------------------------------
335 KColorCells::KColorCells( QWidget *parent, int rows, int cols )
336 : QGridView( parent )
338 shade = true;
339 setNumRows( rows );
340 setNumCols( cols );
341 colors = new QColor [ rows * cols ];
343 for ( int i = 0; i < rows * cols; i++ )
344 colors[i] = QColor();
346 selected = 0;
347 inMouse = false;
349 // Drag'n'Drop
350 setAcceptDrops( true);
352 setHScrollBarMode( AlwaysOff );
353 setVScrollBarMode( AlwaysOff );
354 viewport()->setBackgroundMode( PaletteBackground );
355 setBackgroundMode( PaletteBackground );
358 KColorCells::~KColorCells()
360 delete [] colors;
363 void KColorCells::setColor( int colNum, const QColor &col )
365 colors[colNum] = col;
366 updateCell( colNum/numCols(), colNum%numCols() );
369 void KColorCells::paintCell( QPainter *painter, int row, int col )
371 QBrush brush;
372 int w = 1;
374 if (shade)
376 qDrawShadePanel( painter, 1, 1, cellWidth()-2,
377 cellHeight()-2, colorGroup(), true, 1, &brush );
378 w = 2;
380 QColor color = colors[ row * numCols() + col ];
381 if (!color.isValid())
383 if (!shade) return;
384 color = backgroundColor();
387 painter->setPen( color );
388 painter->setBrush( QBrush( color ) );
389 painter->drawRect( w, w, cellWidth()-w*2, cellHeight()-w*2 );
391 if ( row * numCols() + col == selected )
392 painter->drawWinFocusRect( w, w, cellWidth()-w*2, cellHeight()-w*2 );
395 void KColorCells::resizeEvent( QResizeEvent * )
397 setCellWidth( width() / numCols() );
398 setCellHeight( height() / numRows() );
401 void KColorCells::mousePressEvent( QMouseEvent *e )
403 inMouse = true;
404 mPos = e->pos();
407 int KColorCells::posToCell(const QPoint &pos, bool ignoreBorders)
409 int row = pos.y() / cellHeight();
410 int col = pos.x() / cellWidth();
411 int cell = row * numCols() + col;
413 if (!ignoreBorders)
415 int border = 2;
416 int x = pos.x() - col * cellWidth();
417 int y = pos.y() - row * cellHeight();
418 if ( (x < border) || (x > cellWidth()-border) ||
419 (y < border) || (y > cellHeight()-border))
420 return -1;
422 return cell;
425 void KColorCells::mouseMoveEvent( QMouseEvent *e )
427 if( !(e->state() && LeftButton)) return;
429 if(inMouse) {
430 int delay = KGlobalSettings::dndEventDelay();
431 if(e->x() > mPos.x()+delay || e->x() < mPos.x()-delay ||
432 e->y() > mPos.y()+delay || e->y() < mPos.y()-delay){
433 // Drag color object
434 int cell = posToCell(mPos);
435 if ((cell != -1) && colors[cell].isValid())
437 KColorDrag *d = new KColorDrag( colors[cell], this);
438 d->dragCopy();
444 void KColorCells::dragEnterEvent( QDragEnterEvent *event)
446 event->accept( acceptDrags && KColorDrag::canDecode( event));
449 void KColorCells::dropEvent( QDropEvent *event)
451 QColor c;
452 if( KColorDrag::decode( event, c)) {
453 int cell = posToCell(event->pos(), true);
454 setColor(cell,c);
458 void KColorCells::mouseReleaseEvent( QMouseEvent *e )
460 int cell = posToCell(mPos);
461 int currentCell = posToCell(e->pos());
463 // If we release the mouse in another cell and we don't have
464 // a drag we should ignore this event.
465 if (currentCell != cell)
466 cell = -1;
468 if ( (cell != -1) && (selected != cell) )
470 int prevSel = selected;
471 selected = cell;
472 updateCell( prevSel/numCols(), prevSel%numCols() );
473 updateCell( cell/numCols(), cell%numCols() );
476 inMouse = false;
477 if (cell != -1)
478 emit colorSelected( cell );
481 void KColorCells::mouseDoubleClickEvent( QMouseEvent * /*e*/ )
483 int cell = posToCell(mPos);
485 if (cell != -1)
486 emit colorDoubleClicked( cell );
490 //-----------------------------------------------------------------------------
492 KColorPatch::KColorPatch( QWidget *parent ) : QFrame( parent )
494 setFrameStyle( QFrame::Panel | QFrame::Sunken );
495 colContext = 0;
496 setAcceptDrops( true);
499 KColorPatch::~KColorPatch()
501 if ( colContext )
502 QColor::destroyAllocContext( colContext );
505 void KColorPatch::setColor( const QColor &col )
507 if ( colContext )
508 QColor::destroyAllocContext( colContext );
509 colContext = QColor::enterAllocContext();
510 color.setRgb( col.rgb() );
511 color.alloc();
512 QColor::leaveAllocContext();
514 QPainter painter;
516 painter.begin( this );
517 drawContents( &painter );
518 painter.end();
521 void KColorPatch::drawContents( QPainter *painter )
523 painter->setPen( color );
524 painter->setBrush( QBrush( color ) );
525 painter->drawRect( contentsRect() );
528 void KColorPatch::mouseMoveEvent( QMouseEvent *e )
530 // Drag color object
531 if( !(e->state() && LeftButton)) return;
532 KColorDrag *d = new KColorDrag( color, this);
533 d->dragCopy();
536 void KColorPatch::dragEnterEvent( QDragEnterEvent *event)
538 event->accept( KColorDrag::canDecode( event));
541 void KColorPatch::dropEvent( QDropEvent *event)
543 QColor c;
544 if( KColorDrag::decode( event, c)) {
545 setColor( c);
546 emit colorChanged( c);
550 class KPaletteTable::KPaletteTablePrivate
552 public:
553 QMap<QString,QColor> m_namedColorMap;
556 KPaletteTable::KPaletteTable( QWidget *parent, int minWidth, int cols)
557 : QWidget( parent ), cells(0), mPalette(0), mMinWidth(minWidth), mCols(cols)
559 d = new KPaletteTablePrivate;
561 i18n_namedColors = i18n("Named Colors");
563 QStringList diskPaletteList = KPalette::getPaletteList();
564 QStringList paletteList;
566 // We must replace the untranslated file names by translate names (of course only for KDE's standard palettes)
567 for ( int i = 0; colorPaletteName[i].m_fileName; ++i )
569 diskPaletteList.remove( colorPaletteName[i].m_fileName );
570 paletteList.append( i18n( "palette name", colorPaletteName[i].m_displayName ) );
572 paletteList += diskPaletteList;
573 paletteList.append( i18n_namedColors );
575 QVBoxLayout *layout = new QVBoxLayout( this );
577 combo = new QComboBox( false, this );
578 combo->insertStringList( paletteList );
579 layout->addWidget(combo);
581 sv = new QScrollView( this );
582 QSize cellSize = QSize( mMinWidth, 120);
583 sv->setHScrollBarMode( QScrollView::AlwaysOff);
584 sv->setVScrollBarMode( QScrollView::AlwaysOn);
585 QSize minSize = QSize(sv->verticalScrollBar()->width(), 0);
586 minSize += QSize(sv->frameWidth(), 0);
587 minSize += QSize(cellSize);
588 sv->setFixedSize(minSize);
589 layout->addWidget(sv);
591 mNamedColorList = new KListBox( this, "namedColorList", 0 );
592 mNamedColorList->setFixedSize(minSize);
593 mNamedColorList->hide();
594 layout->addWidget(mNamedColorList);
595 connect( mNamedColorList, SIGNAL(highlighted( const QString & )),
596 this, SLOT( slotColorTextSelected( const QString & )) );
598 setFixedSize( sizeHint());
599 connect( combo, SIGNAL(activated(const QString &)),
600 this, SLOT(slotSetPalette( const QString &)));
603 KPaletteTable::~KPaletteTable()
605 delete mPalette;
606 delete d;
609 QString
610 KPaletteTable::palette() const
612 return combo->currentText();
616 static const char * const *namedColorFilePath( void )
619 // 2000-02-05 Espen Sand.
620 // Add missing filepaths here. Make sure the last entry is 0!
622 static const char * const path[] =
624 #ifdef X11_RGBFILE
625 X11_RGBFILE,
626 #endif
627 "/usr/X11R6/lib/X11/rgb.txt",
628 "/usr/openwin/lib/X11/rgb.txt", // for Solaris.
631 return path;
637 void
638 KPaletteTable::readNamedColor( void )
640 if( mNamedColorList->count() != 0 )
642 return; // Strings already present
645 KGlobal::locale()->insertCatalogue("kdelibs_colors");
648 // Code somewhat inspired by KPalette.
651 const char * const *path = namedColorFilePath();
652 for( int i=0; path[i]; ++i )
654 QFile paletteFile( path[i] );
655 if( !paletteFile.open( IO_ReadOnly ) )
657 continue;
660 QString line;
661 QStringList list;
662 while( paletteFile.readLine( line, 100 ) != -1 )
664 int red, green, blue;
665 int pos = 0;
667 if( sscanf(line.ascii(), "%d %d %d%n", &red, &green, &blue, &pos ) == 3 )
670 // Remove duplicates. Every name with a space and every name
671 // that start with "gray".
673 QString name = line.mid(pos).stripWhiteSpace();
674 if( name.isNull() || name.find(' ') != -1 ||
675 name.find( "gray" ) != -1 || name.find( "grey" ) != -1 )
677 continue;
680 const QColor color ( red, green, blue );
681 if ( color.isValid() )
683 const QString colorName( i18n("color", name.latin1() ) );
684 list.append( colorName );
685 d->m_namedColorMap[ colorName ] = color;
690 list.sort();
691 mNamedColorList->insertStringList( list );
692 break;
695 if( mNamedColorList->count() == 0 )
698 // Give the error dialog box a chance to center above the
699 // widget (or dialog). If we had displayed it now we could get a
700 // situation where the (modal) error dialog box pops up first
701 // preventing the real dialog to become visible until the
702 // error dialog box is removed (== bad UI).
704 QTimer::singleShot( 10, this, SLOT(slotShowNamedColorReadError()) );
709 void
710 KPaletteTable::slotShowNamedColorReadError( void )
712 if( mNamedColorList->count() == 0 )
714 QString msg = i18n(""
715 "Unable to read X11 RGB color strings. The following "
716 "file location(s) were examined:\n");
718 const char * const *path = namedColorFilePath();
719 for( int i=0; path[i]; ++i )
721 msg += path[i];
722 msg += "\n";
724 KMessageBox::sorry( this, msg );
730 // 2000-02-12 Espen Sand
731 // Set the color in two steps. The setPalette() slot will not emit a signal
732 // with the current color setting. The reason is that setPalette() is used
733 // by the color selector dialog on startup. In the color selector dialog
734 // we normally want to display a startup color which we specify
735 // when the dialog is started. The slotSetPalette() slot below will
736 // set the palette and then use the information to emit a signal with the
737 // new color setting. It is only used by the combobox widget.
739 void
740 KPaletteTable::slotSetPalette( const QString &_paletteName )
742 setPalette( _paletteName );
743 if( mNamedColorList->isVisible() )
745 int item = mNamedColorList->currentItem();
746 mNamedColorList->setCurrentItem( item < 0 ? 0 : item );
747 slotColorTextSelected( mNamedColorList->currentText() );
749 else
751 slotColorCellSelected(0); // FIXME: We need to save the current value!!
756 void
757 KPaletteTable::setPalette( const QString &_paletteName )
759 QString paletteName( _paletteName);
760 if (paletteName.isEmpty())
761 paletteName = i18n_recentColors;
763 if (combo->currentText() != paletteName)
765 bool found = false;
766 for(int i = 0; i < combo->count(); i++)
768 if (combo->text(i) == paletteName)
770 combo->setCurrentItem(i);
771 found = true;
772 break;
775 if (!found)
777 combo->insertItem(paletteName);
778 combo->setCurrentItem(combo->count()-1);
782 // We must again find the file name of the palette from the eventual translation
783 for ( int i = 0; colorPaletteName[i].m_fileName; ++i )
785 if ( paletteName == i18n( "palette name", colorPaletteName[i].m_displayName ) )
787 paletteName = colorPaletteName[i].m_fileName;
788 break;
794 // 2000-02-12 Espen Sand
795 // The palette mode "i18n_namedColors" does not use the KPalette class.
796 // In fact, 'mPalette' and 'cells' are 0 when in this mode. The reason
797 // for this is maninly that KPalette reads from and writes to files using
798 // "locate()". The colors used in "i18n_namedColors" mode comes from the
799 // X11 diretory and is not writable. I don't think this fit in KPalette.
801 if( !mPalette || mPalette->name() != paletteName )
803 if( paletteName == i18n_namedColors )
805 sv->hide();
806 mNamedColorList->show();
807 readNamedColor();
809 delete cells; cells = 0;
810 delete mPalette; mPalette = 0;
812 else
814 mNamedColorList->hide();
815 sv->show();
817 delete cells;
818 delete mPalette;
819 mPalette = new KPalette(paletteName);
820 int rows = (mPalette->nrColors()+mCols-1) / mCols;
821 if (rows < 1) rows = 1;
822 cells = new KColorCells( sv->viewport(), rows, mCols);
823 cells->setShading(false);
824 cells->setAcceptDrags(false);
825 QSize cellSize = QSize( mMinWidth, mMinWidth * rows / mCols);
826 cells->setFixedSize( cellSize );
827 for( int i = 0; i < mPalette->nrColors(); i++)
829 cells->setColor( i, mPalette->color(i) );
831 connect( cells, SIGNAL( colorSelected( int ) ),
832 SLOT( slotColorCellSelected( int ) ) );
833 connect( cells, SIGNAL( colorDoubleClicked( int ) ),
834 SLOT( slotColorCellDoubleClicked( int ) ) );
835 sv->addChild( cells );
836 cells->show();
837 sv->updateScrollBars();
844 void
845 KPaletteTable::slotColorCellSelected( int col )
847 if (!mPalette || (col >= mPalette->nrColors()))
848 return;
849 emit colorSelected( mPalette->color(col), mPalette->colorName(col) );
852 void
853 KPaletteTable::slotColorCellDoubleClicked( int col )
855 if (!mPalette || (col >= mPalette->nrColors()))
856 return;
857 emit colorDoubleClicked( mPalette->color(col), mPalette->colorName(col) );
861 void
862 KPaletteTable::slotColorTextSelected( const QString &colorText )
864 emit colorSelected( d->m_namedColorMap[ colorText ], colorText );
868 void
869 KPaletteTable::addToCustomColors( const QColor &color)
871 setPalette(i18n_customColors);
872 mPalette->addColor( color );
873 mPalette->save();
874 delete mPalette;
875 mPalette = 0;
876 setPalette(i18n_customColors);
879 void
880 KPaletteTable::addToRecentColors( const QColor &color)
883 // 2000-02-12 Espen Sand.
884 // The 'mPalette' is always 0 when current mode is i18n_namedColors
886 bool recentIsSelected = false;
887 if ( mPalette && mPalette->name() == colorPaletteName[ recentColorIndex ].m_fileName )
889 delete mPalette;
890 mPalette = 0;
891 recentIsSelected = true;
893 KPalette *recentPal = new KPalette( colorPaletteName[ recentColorIndex ].m_fileName );
894 if (recentPal->findColor(color) == -1)
896 recentPal->addColor( color );
897 recentPal->save();
899 delete recentPal;
900 if (recentIsSelected)
901 setPalette( i18n( "palette name", colorPaletteName[ recentColorIndex ].m_displayName ) );
904 class KColorDialog::KColorDialogPrivate {
905 public:
906 KPaletteTable *table;
907 QString originalPalette;
908 bool bRecursion;
909 bool bEditRgb;
910 bool bEditHsv;
911 bool bEditHtml;
912 bool bColorPicking;
913 QLabel *colorName;
914 QLineEdit *htmlName;
915 KColorSpinBox *hedit;
916 KColorSpinBox *sedit;
917 KColorSpinBox *vedit;
918 KColorSpinBox *redit;
919 KColorSpinBox *gedit;
920 KColorSpinBox *bedit;
921 KColorPatch *patch;
922 KHSSelector *hsSelector;
923 KPalette *palette;
924 KValueSelector *valuePal;
925 QVBoxLayout* l_right;
926 QGridLayout* tl_layout;
927 QCheckBox *cbDefaultColor;
928 KColor defaultColor;
929 KColor selColor;
930 #ifdef Q_WS_X11
931 QX11EventFilter oldfilter;
932 #endif
936 KColorDialog::KColorDialog( QWidget *parent, const char *name, bool modal )
937 :KDialogBase( parent, name, modal, i18n("Select Color"),
938 modal ? Help|Ok|Cancel : Help|Close,
939 Ok, true )
941 d = new KColorDialogPrivate;
942 d->bRecursion = true;
943 d->bColorPicking = false;
944 #ifdef Q_WS_X11
945 d->oldfilter = 0;
946 #endif
947 d->cbDefaultColor = 0L;
948 setHelp( QString::fromLatin1("kcolordialog.html"), QString::null );
949 connect( this, SIGNAL(okClicked(void)),this,SLOT(slotWriteSettings(void)));
950 connect( this, SIGNAL(closeClicked(void)),this,SLOT(slotWriteSettings(void)));
952 QLabel *label;
955 // Create the top level page and its layout
957 QWidget *page = new QWidget( this );
958 setMainWidget( page );
960 QGridLayout *tl_layout = new QGridLayout( page, 3, 3, 0, spacingHint() );
961 d->tl_layout = tl_layout;
962 tl_layout->addColSpacing( 1, spacingHint() * 2 );
965 // the more complicated part: the left side
966 // add a V-box
968 QVBoxLayout *l_left = new QVBoxLayout();
969 tl_layout->addLayout(l_left, 0, 0);
972 // add a H-Box for the XY-Selector and a grid for the
973 // entry fields
975 QHBoxLayout *l_ltop = new QHBoxLayout();
976 l_left->addLayout(l_ltop);
978 // a little space between
979 l_left->addSpacing(10);
981 QGridLayout *l_lbot = new QGridLayout(3, 6);
982 l_left->addLayout(l_lbot);
985 // the palette and value selector go into the H-box
987 d->hsSelector = new KHSSelector( page );
988 d->hsSelector->setMinimumSize(140, 70);
989 l_ltop->addWidget(d->hsSelector, 8);
990 connect( d->hsSelector, SIGNAL( valueChanged( int, int ) ),
991 SLOT( slotHSChanged( int, int ) ) );
993 d->valuePal = new KValueSelector( page );
994 d->valuePal->setMinimumSize(26, 70);
995 l_ltop->addWidget(d->valuePal, 1);
996 connect( d->valuePal, SIGNAL( valueChanged( int ) ),
997 SLOT( slotVChanged( int ) ) );
1001 // add the HSV fields
1003 label = new QLabel( i18n("H:"), page );
1004 label->setAlignment(AlignRight | AlignVCenter);
1005 l_lbot->addWidget(label, 0, 2);
1006 d->hedit = new KColorSpinBox( 0, 359, 1, page );
1007 d->hedit->setValidator( new QIntValidator( d->hedit ) );
1008 l_lbot->addWidget(d->hedit, 0, 3);
1009 connect( d->hedit, SIGNAL( valueChanged(int) ),
1010 SLOT( slotHSVChanged() ) );
1012 label = new QLabel( i18n("S:"), page );
1013 label->setAlignment(AlignRight | AlignVCenter);
1014 l_lbot->addWidget(label, 1, 2);
1015 d->sedit = new KColorSpinBox( 0, 255, 1, page );
1016 d->sedit->setValidator( new QIntValidator( d->sedit ) );
1017 l_lbot->addWidget(d->sedit, 1, 3);
1018 connect( d->sedit, SIGNAL( valueChanged(int) ),
1019 SLOT( slotHSVChanged() ) );
1021 label = new QLabel( i18n("V:"), page );
1022 label->setAlignment(AlignRight | AlignVCenter);
1023 l_lbot->addWidget(label, 2, 2);
1024 d->vedit = new KColorSpinBox( 0, 255, 1, page );
1025 d->vedit->setValidator( new QIntValidator( d->vedit ) );
1026 l_lbot->addWidget(d->vedit, 2, 3);
1027 connect( d->vedit, SIGNAL( valueChanged(int) ),
1028 SLOT( slotHSVChanged() ) );
1031 // add the RGB fields
1033 label = new QLabel( i18n("R:"), page );
1034 label->setAlignment(AlignRight | AlignVCenter);
1035 l_lbot->addWidget(label, 0, 4);
1036 d->redit = new KColorSpinBox( 0, 255, 1, page );
1037 d->redit->setValidator( new QIntValidator( d->redit ) );
1038 l_lbot->addWidget(d->redit, 0, 5);
1039 connect( d->redit, SIGNAL( valueChanged(int) ),
1040 SLOT( slotRGBChanged() ) );
1042 label = new QLabel( i18n("G:"), page );
1043 label->setAlignment(AlignRight | AlignVCenter);
1044 l_lbot->addWidget( label, 1, 4);
1045 d->gedit = new KColorSpinBox( 0, 255,1, page );
1046 d->gedit->setValidator( new QIntValidator( d->gedit ) );
1047 l_lbot->addWidget(d->gedit, 1, 5);
1048 connect( d->gedit, SIGNAL( valueChanged(int) ),
1049 SLOT( slotRGBChanged() ) );
1051 label = new QLabel( i18n("B:"), page );
1052 label->setAlignment(AlignRight | AlignVCenter);
1053 l_lbot->addWidget(label, 2, 4);
1054 d->bedit = new KColorSpinBox( 0, 255, 1, page );
1055 d->bedit->setValidator( new QIntValidator( d->bedit ) );
1056 l_lbot->addWidget(d->bedit, 2, 5);
1057 connect( d->bedit, SIGNAL( valueChanged(int) ),
1058 SLOT( slotRGBChanged() ) );
1061 // the entry fields should be wide enough to hold 8888888
1063 int w = d->hedit->fontMetrics().width("8888888");
1064 d->hedit->setFixedWidth(w);
1065 d->sedit->setFixedWidth(w);
1066 d->vedit->setFixedWidth(w);
1068 d->redit->setFixedWidth(w);
1069 d->gedit->setFixedWidth(w);
1070 d->bedit->setFixedWidth(w);
1073 // add a layout for the right side
1075 d->l_right = new QVBoxLayout;
1076 tl_layout->addLayout(d->l_right, 0, 2);
1079 // Add the palette table
1081 d->table = new KPaletteTable( page );
1082 d->l_right->addWidget(d->table, 10);
1084 connect( d->table, SIGNAL( colorSelected( const QColor &, const QString & ) ),
1085 SLOT( slotColorSelected( const QColor &, const QString & ) ) );
1087 connect(
1088 d->table,
1089 SIGNAL( colorDoubleClicked( const QColor &, const QString & ) ),
1090 SLOT( slotColorDoubleClicked( const QColor &, const QString & ) )
1092 // Store the default value for saving time.
1093 d->originalPalette = d->table->palette();
1096 // a little space between
1098 d->l_right->addSpacing(10);
1100 QHBoxLayout *l_hbox = new QHBoxLayout( d->l_right );
1103 // The add to custom colors button
1105 QPushButton *button = new QPushButton( page );
1106 button->setText(i18n("&Add to Custom Colors"));
1107 l_hbox->addWidget(button, 0, AlignLeft);
1108 connect( button, SIGNAL( clicked()), SLOT( slotAddToCustomColors()));
1111 // The color picker button
1113 button = new QPushButton( page );
1114 button->setPixmap( BarIcon("colorpicker"));
1115 l_hbox->addWidget(button, 0, AlignHCenter );
1116 connect( button, SIGNAL( clicked()), SLOT( slotColorPicker()));
1119 // a little space between
1121 d->l_right->addSpacing(10);
1124 // and now the entry fields and the patch (=colored box)
1126 QGridLayout *l_grid = new QGridLayout( d->l_right, 2, 3);
1128 l_grid->setColStretch(2, 1);
1130 label = new QLabel( page );
1131 label->setText(i18n("Name:"));
1132 l_grid->addWidget(label, 0, 1, AlignLeft);
1134 d->colorName = new QLabel( page );
1135 l_grid->addWidget(d->colorName, 0, 2, AlignLeft);
1137 label = new QLabel( page );
1138 label->setText(i18n("HTML:"));
1139 l_grid->addWidget(label, 1, 1, AlignLeft);
1141 d->htmlName = new QLineEdit( page );
1142 d->htmlName->setMaxLength( 13 ); // Qt's QColor allows 12 hexa-digits
1143 d->htmlName->setText("#FFFFFF"); // But HTML uses only 6, so do not worry about the size
1144 w = d->htmlName->fontMetrics().width(QString::fromLatin1("#DDDDDDD"));
1145 d->htmlName->setFixedWidth(w);
1146 l_grid->addWidget(d->htmlName, 1, 2, AlignLeft);
1148 connect( d->htmlName, SIGNAL( textChanged(const QString &) ),
1149 SLOT( slotHtmlChanged() ) );
1151 d->patch = new KColorPatch( page );
1152 d->patch->setFixedSize(48, 48);
1153 l_grid->addMultiCellWidget(d->patch, 0, 1, 0, 0, AlignHCenter | AlignVCenter);
1154 connect( d->patch, SIGNAL( colorChanged( const QColor&)),
1155 SLOT( setColor( const QColor&)));
1157 tl_layout->activate();
1158 page->setMinimumSize( page->sizeHint() );
1160 readSettings();
1161 d->bRecursion = false;
1162 d->bEditHsv = false;
1163 d->bEditRgb = false;
1164 d->bEditHtml = false;
1166 disableResize();
1167 KColor col;
1168 col.setHsv( 0, 0, 255 );
1169 _setColor( col );
1171 d->htmlName->installEventFilter(this);
1172 d->hsSelector->installEventFilter(this);
1173 d->hsSelector->setAcceptDrops(true);
1176 KColorDialog::~KColorDialog()
1178 #ifdef Q_WS_X11
1179 if (d->bColorPicking)
1180 qt_set_x11_event_filter(d->oldfilter);
1181 #endif
1182 delete d;
1185 bool
1186 KColorDialog::eventFilter( QObject *obj, QEvent *ev )
1188 if ((obj == d->htmlName) || (obj == d->hsSelector))
1189 switch(ev->type())
1191 case QEvent::DragEnter:
1192 case QEvent::DragMove:
1193 case QEvent::DragLeave:
1194 case QEvent::Drop:
1195 case QEvent::DragResponse:
1196 qApp->sendEvent(d->patch, ev);
1197 return true;
1198 default:
1199 break;
1201 return KDialogBase::eventFilter(obj, ev);
1204 void
1205 KColorDialog::setDefaultColor( const QColor& col )
1207 if ( !d->cbDefaultColor )
1210 // a little space between
1212 d->l_right->addSpacing(10);
1215 // and the "default color" checkbox, under all items on the right side
1217 d->cbDefaultColor = new QCheckBox( i18n( "Default color" ), mainWidget() );
1218 d->cbDefaultColor->setChecked(true);
1220 d->l_right->addWidget( d->cbDefaultColor );
1222 mainWidget()->setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); // cancel setFixedSize()
1223 d->tl_layout->activate();
1224 mainWidget()->setMinimumSize( mainWidget()->sizeHint() );
1225 disableResize();
1227 connect( d->cbDefaultColor, SIGNAL( clicked() ), SLOT( slotDefaultColorClicked() ) );
1230 d->defaultColor = col;
1232 slotDefaultColorClicked();
1235 QColor KColorDialog::defaultColor() const
1237 return d->defaultColor;
1240 void KColorDialog::slotDefaultColorClicked()
1242 if ( d->cbDefaultColor->isChecked() )
1244 d->selColor = d->defaultColor;
1245 showColor( d->selColor, i18n( "-default-" ) );
1246 } else
1248 showColor( d->selColor, QString::null );
1252 void
1253 KColorDialog::readSettings()
1255 KConfig* config = KGlobal::config();
1257 QString oldgroup = config->group();
1259 config->setGroup("Colors");
1260 QString palette = config->readEntry("CurrentPalette");
1261 d->table->setPalette(palette);
1262 config->setGroup( oldgroup );
1265 void
1266 KColorDialog::slotWriteSettings()
1268 KConfig* config = KGlobal::config();
1269 config->setGroup("Colors");
1270 QString palette = d->table->palette();
1271 if (!config->hasDefault("CurrentPalette") &&
1272 (d->table->palette() == d->originalPalette))
1274 config->revertToDefault("CurrentPalette");
1276 else
1278 config->writeEntry("CurrentPalette", d->table->palette());
1282 QColor
1283 KColorDialog::color() const
1285 if ( d->cbDefaultColor && d->cbDefaultColor->isChecked() )
1286 return QColor();
1287 if ( d->selColor.isValid() )
1288 d->table->addToRecentColors( d->selColor );
1289 return d->selColor;
1292 void KColorDialog::setColor( const QColor &col )
1294 _setColor( col );
1298 // static function to display dialog and return color
1300 int KColorDialog::getColor( QColor &theColor, QWidget *parent )
1302 KColorDialog dlg( parent, "Color Selector", true );
1303 if ( theColor.isValid() )
1304 dlg.setColor( theColor );
1305 int result = dlg.exec();
1307 if ( result == Accepted )
1309 theColor = dlg.color();
1312 return result;
1316 // static function to display dialog and return color
1318 int KColorDialog::getColor( QColor &theColor, const QColor& defaultCol, QWidget *parent )
1320 KColorDialog dlg( parent, "Color Selector", true );
1321 dlg.setDefaultColor( defaultCol );
1322 dlg.setColor( theColor );
1323 int result = dlg.exec();
1325 if ( result == Accepted )
1326 theColor = dlg.color();
1328 return result;
1331 void KColorDialog::slotRGBChanged( void )
1333 if (d->bRecursion) return;
1334 int red = d->redit->value();
1335 int grn = d->gedit->value();
1336 int blu = d->bedit->value();
1338 if ( red > 255 || red < 0 ) return;
1339 if ( grn > 255 || grn < 0 ) return;
1340 if ( blu > 255 || blu < 0 ) return;
1342 KColor col;
1343 col.setRgb( red, grn, blu );
1344 d->bEditRgb = true;
1345 _setColor( col );
1346 d->bEditRgb = false;
1349 void KColorDialog::slotHtmlChanged( void )
1351 if (d->bRecursion || d->htmlName->text().isEmpty()) return;
1353 QString strColor( d->htmlName->text() );
1355 // Assume that a user does not want to type the # all the time
1356 if ( strColor[0] != '#' )
1358 bool signalsblocked = d->htmlName->signalsBlocked();
1359 d->htmlName->blockSignals(true);
1360 strColor.prepend("#");
1361 d->htmlName->setText(strColor);
1362 d->htmlName->blockSignals(signalsblocked);
1365 const QColor color( strColor );
1367 if ( color.isValid() )
1369 KColor col( color );
1370 d->bEditHtml = true;
1371 _setColor( col );
1372 d->bEditHtml = false;
1376 void KColorDialog::slotHSVChanged( void )
1378 if (d->bRecursion) return;
1379 int hue = d->hedit->value();
1380 int sat = d->sedit->value();
1381 int val = d->vedit->value();
1383 if ( hue > 359 || hue < 0 ) return;
1384 if ( sat > 255 || sat < 0 ) return;
1385 if ( val > 255 || val < 0 ) return;
1387 KColor col;
1388 col.setHsv( hue, sat, val );
1389 d->bEditHsv = true;
1390 _setColor( col );
1391 d->bEditHsv = false;
1394 void KColorDialog::slotHSChanged( int h, int s )
1396 int _h, _s, v;
1397 d->selColor.hsv(&_h, &_s, &v);
1398 if (v < 0)
1399 v = 0;
1400 KColor col;
1401 col.setHsv( h, s, v );
1402 _setColor( col );
1405 void KColorDialog::slotVChanged( int v )
1407 int h, s, _v;
1408 d->selColor.hsv(&h, &s, &_v);
1409 KColor col;
1410 col.setHsv( h, s, v );
1411 _setColor( col );
1414 void KColorDialog::slotColorSelected( const QColor &color )
1416 _setColor( color );
1419 void KColorDialog::slotAddToCustomColors( )
1421 d->table->addToCustomColors( d->selColor );
1424 void KColorDialog::slotColorSelected( const QColor &color, const QString &name )
1426 _setColor( color, name);
1429 void KColorDialog::slotColorDoubleClicked
1431 const QColor & color,
1432 const QString & name
1435 _setColor(color, name);
1436 accept();
1439 void KColorDialog::_setColor(const KColor &color, const QString &name)
1441 if (color.isValid())
1443 if (d->cbDefaultColor && d->cbDefaultColor->isChecked())
1444 d->cbDefaultColor->setChecked(false);
1445 d->selColor = color;
1447 else
1449 if (d->cbDefaultColor && d->cbDefaultColor->isChecked())
1450 d->cbDefaultColor->setChecked(true);
1451 d->selColor = d->defaultColor;
1454 showColor( d->selColor, name );
1456 emit colorSelected( d->selColor );
1459 // show but don't set into selColor, nor emit colorSelected
1460 void KColorDialog::showColor( const KColor &color, const QString &name )
1462 d->bRecursion = true;
1464 if (name.isEmpty())
1465 d->colorName->setText( i18n("-unnamed-"));
1466 else
1467 d->colorName->setText( name );
1469 d->patch->setColor( color );
1471 setRgbEdit( color );
1472 setHsvEdit( color );
1473 setHtmlEdit( color );
1475 int h, s, v;
1476 color.hsv( &h, &s, &v );
1477 d->hsSelector->setValues( h, s );
1478 d->valuePal->blockSignals(true);
1479 d->valuePal->setHue( h );
1480 d->valuePal->setSaturation( s );
1481 d->valuePal->setValue( v );
1482 d->valuePal->updateContents();
1483 d->valuePal->blockSignals(false);
1484 d->valuePal->repaint( false );
1485 d->bRecursion = false;
1489 static QWidget *kde_color_dlg_widget = 0;
1491 #ifdef Q_WS_X11
1492 static int kde_color_dlg_handler(XEvent *event)
1494 if (event->type == ButtonRelease)
1496 QMouseEvent e( QEvent::MouseButtonRelease, QPoint(),
1497 QPoint(event->xmotion.x_root, event->xmotion.y_root) , 0, 0 );
1498 QApplication::sendEvent( kde_color_dlg_widget, &e );
1499 return true;
1501 return false;
1503 #endif
1504 void
1505 KColorDialog::slotColorPicker()
1507 d->bColorPicking = true;
1508 #ifdef Q_WS_X11
1509 d->oldfilter = qt_set_x11_event_filter(kde_color_dlg_handler);
1510 #endif
1511 kde_color_dlg_widget = this;
1512 grabMouse( crossCursor );
1513 grabKeyboard();
1516 void
1517 KColorDialog::mouseReleaseEvent( QMouseEvent *e )
1519 if (d->bColorPicking)
1521 d->bColorPicking = false;
1522 #ifdef Q_WS_X11
1523 qt_set_x11_event_filter(d->oldfilter);
1524 d->oldfilter = 0;
1525 #endif
1526 releaseMouse();
1527 releaseKeyboard();
1528 _setColor( grabColor( e->globalPos() ) );
1529 return;
1531 KDialogBase::mouseReleaseEvent( e );
1534 QColor
1535 KColorDialog::grabColor(const QPoint &p)
1537 QWidget *desktop = QApplication::desktop();
1538 QPixmap pm = QPixmap::grabWindow( desktop->winId(), p.x(), p.y(), 1, 1);
1539 QImage i = pm.convertToImage();
1540 return i.pixel(0,0);
1543 void
1544 KColorDialog::keyPressEvent( QKeyEvent *e )
1546 if (d->bColorPicking)
1548 if (e->key() == Key_Escape)
1550 d->bColorPicking = false;
1551 #ifdef Q_WS_X11
1552 qt_set_x11_event_filter(d->oldfilter);
1553 d->oldfilter = 0;
1554 #endif
1555 releaseMouse();
1556 releaseKeyboard();
1558 e->accept();
1559 return;
1561 KDialogBase::keyPressEvent( e );
1564 void KColorDialog::setRgbEdit( const KColor &col )
1566 if (d->bEditRgb) return;
1567 int r, g, b;
1568 col.rgb( &r, &g, &b );
1570 d->redit->setValue( r );
1571 d->gedit->setValue( g );
1572 d->bedit->setValue( b );
1575 void KColorDialog::setHtmlEdit( const KColor &col )
1577 if (d->bEditHtml) return;
1578 int r, g, b;
1579 col.rgb( &r, &g, &b );
1580 QString num;
1582 num.sprintf("#%02X%02X%02X", r,g,b);
1583 d->htmlName->setText( num );
1587 void KColorDialog::setHsvEdit( const KColor &col )
1589 if (d->bEditHsv) return;
1590 int h, s, v;
1591 col.hsv( &h, &s, &v );
1593 d->hedit->setValue( h );
1594 d->sedit->setValue( s );
1595 d->vedit->setValue( v );
1598 void KHSSelector::virtual_hook( int id, void* data )
1599 { KXYSelector::virtual_hook( id, data ); }
1601 void KValueSelector::virtual_hook( int id, void* data )
1602 { KSelector::virtual_hook( id, data ); }
1604 void KPaletteTable::virtual_hook( int, void* )
1605 { /*BASE::virtual_hook( id, data );*/ }
1607 void KColorCells::virtual_hook( int, void* )
1608 { /*BASE::virtual_hook( id, data );*/ }
1610 void KColorPatch::virtual_hook( int, void* )
1611 { /*BASE::virtual_hook( id, data );*/ }
1613 void KColorDialog::virtual_hook( int id, void* data )
1614 { KDialogBase::virtual_hook( id, data ); }
1617 #include "kcolordialog.moc"
1618 //#endif