summaryrefslogtreecommitdiffstats
path: root/src/kcolorcombo2.h
blob: 5fa3f9d3d83e91cbe74fa520ebb253b95b9eff8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/***************************************************************************
 *   Copyright (C) 2005 by Sébastien Laoût                                 *
 *   slaout@linux62.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#ifndef KCOLORCOMBO2_H
#define KCOLORCOMBO2_H

#include <tqcombobox.h>
#include <tqcolor.h>
#include <tqpixmap.h>

class KColorPopup;

/**
 * @short A combobox to display or allow user selection of a color in a user-friendly way.
 *
 * A combobox widget that popup an array of colors for the user to easily pick a common color.\n
 * He/she can use the popup to quickly pick a reasonable color or open a color chooser dialog for a more precise choice.\n
 * The user can also choose a default color (the standard background color, text color, etc... it's to the programmer to make sense of this property).\n
 * \n
 * The user is also offered some facilities: like KColorButton he/she can copy a color or paste it
 * (with standard keyboard shortcuts, usually Ctrl+C and Ctrl+V), and he/she can drag or drop colors.
 *
 * @par Quick usage:
 * Just create a new KColorCombo2() with the initial color and eventually an allowed default color
 * (eg. TDEGlobalSettings::baseColor() for a background color, TDEGlobalSettings::textColor()...).\n
 * You will be noticed of the color the user selects with the signal changed(), or you can use color() to get the color at any moment.\n
 * Note that they can return an invalid color (see TQColor::isValid()) if the user chosen the default color (if he can choose that).\n
 * It's then easy to save in settings, but if you want the real color (even for the default), you can get it with effectiveColor().
 *
 * @par Notes about default color:
 * If you set a default color using TQt or TDE standard colors, the user can change them in the TDE Control Center,
 * but this widget willn't be update and will still show the old one.\n
 * To be noticed of such color change and then update the widget with the new standard color, you can use one of those two methods:
 * @code
 * void TQWidgetDerivate::paletteChange(const TQPalette &oldPalette) { // TQWidgetDerivate is a parent or near custom widget
 *     theComboBox->setDefaultColor(theNewDefaultColor);
 *     TQWidget::paletteChange(oldPalette);
 * }
 * @endcode
 * or connect the signal TDEApplication::tdedisplayPaletteChanged() to a slot that will set the default color of this widget.
 *
 * @par Advanced usage:
 * By default, the combobox show a well balanced rainbow, OK for most usages, and you don't need to do anything for it to work.\n
 * You however can set your own color array by calling newColorArray() with the number of columns and rows.
 * Then, setColorAt() several times to fill the array.\n
 * This allow the most flexibility. But if you just want a rainbow with more or less colors, setRainbowPreset() is what you want.\n
 * If you worry about performance issues of creating a combobox with the default color array and then allocating another color array by yourself,
 * note that the default color array is not allocated in the constructor, but as soon as it is demanded (on first popup if no array has been
 * set before, or on first call of any accessors: colorAt(), columnCount(), setColorAt()...).
 * Finally, colorRectPixmap() and drawColorRect() allow to draw the color rounded-rectangle in other places for a consistent look.
 *
 * @see TDEGlobalSettings Use one of the static functions to get TDE standard colors for default values.
 * @see KColorButton    The same, but without the rainbow popup or the choice of a default color.
 * @see KColorDialog    The dialog that is shown when the user click the "Other..." entry.
 * @author Sébastien Laoût <slaout@linux62.org>
 *
 * @image html commoncolorselector.png "Common Color Selector ComboBox"
 */
class KColorCombo2 : public TQComboBox
{
  Q_OBJECT
  
  TQ_PROPERTY(TQColor color        READ color        WRITE setColor)
  TQ_PROPERTY(TQColor defaultColor READ defaultColor WRITE setDefaultColor)

  public slots:
	/**
	 * Change the selected color.\n
	 * If the popup is open, it will not reflect the change. FIXME: Should it?
	 * @param color The new selected color. Can be invalid to select the default one.\n
	 *              If @p color is invalid and no default color is allowed, the function will keep the old one.
	 */
	void setColor(const TQColor &color);

	/**
	 * Change the default color.
	 * @param color The color to return if the user choose the default one. If it is not valid, the user willn't be allowed to choose a default one.
	 * @see defaultColor() to get it.
	 */
	void setDefaultColor(const TQColor &color);

  signals:
	/**
	 * Emitted when the color of the widget is changed, either with setColor() or via user selection.
	 * @see color() to know the content of @p newColor.
	 */
	void changed(const TQColor &newColor);

  public:
	/**
	 * Constructs a color combobox with parent @p parent called @p name.
	 * @param color         The initial selected color. If it is not valid, the default one will then be selected.\n
	 *                      But if @p color is invalid and there is no default color, the result is undefined.
	 * @param defaultColor  The color to return if the user choose the default one. If it is not valid, the user willn't be allowed to choose a default one.
	 */
	KColorCombo2(const TQColor &color, const TQColor &defaultColor, TQWidget *parent = 0, const char *name = 0);

	/**
	 * Constructs a color combobox with parent @p parent called @p name.\n
	 * The user is not allowed to choose a default color, unless you call setDefaultColor() later.
	 * @param color         The initial selected color. If it is invalid, the result is undefined.
	 */
	KColorCombo2(const TQColor &color, TQWidget *parent = 0L, const char *name = 0L);

	/**
	 * Destroys the combobox.
	 */
	virtual ~KColorCombo2();

	/**
	 * Get the color chosen by the user.\n
	 * Can be invalid, if the user chosen the default one.\n
	 * Ideal to store it in settings for later recall.
	 * @see effectiveColor() if you want the color to be always valid.
	 */
	TQColor color() const;

	/**
	 * Return the color chosen by the user.\n
	 * If the user chosen the default color, the default one is then returned, so the returned color is always valid.\n
	 * Ideal to directly use to draw.
	 * @see color() if you want to be notified of a default color choice.
	 */
	TQColor effectiveColor() const;

	/**
	 * Returns the default color or an invalid color if no default color is set (if the user isn't allowed to choose a default color).
	 * @see setDefaultColor() to change it.
	 */
	TQColor defaultColor() const;

	/**
	 * Allocate a new color array of the specified dimention.\n
	 * The new array will have invalid colors: you should then assign them one by one.\n
	 * If one or both of the dimentions are negative or null, this function do nothing (both dimentions are always ensured to be at least equal to 1).
	 * @param columnCount The number of columns of the array.
	 * @param rowCount    The number of rows of the array.
	 * @see setColorAt() to set all colors once the array have been created.
	 */
	void newColorArray(int columnCount, int rowCount);

	/**
	 * Get the number of columns in the array that the user can see to choose.
	 * @see rowCount() for the number of rows, and colorAt() to get a color from the array.
	 */
	int columnCount() const;

	/**
	 * Get the number of rows in the array that the user can see to choose.
	 * @see columnCount() for the number of columns, and colorAt() to get a color from the array.
	 */
	int rowCount() const;

	/**
	 * Set a color in the array at position (column,row).\n
	 * If one or both of the indexes are out of range, this function do nothing.\n
	 * @p column and @p row start from 0 to columnCount()-1 and columnRow()-1.
	 *
	 * @param column The x coordinate of the color to set or change.
	 * @param row    The y coordinate of the color to set or change.
	 * @param color  The color to assign at this position.
	 */
	void setColorAt(int column, int row, const TQColor &color);

	/**
	 * Get a color in the array that the user can see to choose.\n
	 * @p column and @p row start from 0 to columnCount()-1 and columnRow()-1.
	 *
	 * @return The asked color, or an invalid color if the index is out of limit of the array.
	 * @see columnCount() and rowCount() to get the array dimentions.
	 */
	TQColor colorAt(int column, int row)/* const*/;

	/**
	 * Fill the array of colors (that will be shown to the user in the popup that appears when he/she click the arrow) with a rainbow of different luminosity.\n
	 * This rainbow representation have the advantage of being natural and well structured for a human to be able to select reasonable colors.\n
	 * This function will allocate a color array by itself depending on the parameters (no need to call newColorArray()).
	 * @param colorColumnCount The number of columns. The 360 possible colors of the rainbow will be splitted to take the wanted number of colors, equaly separated.
	 * @param lightRowCount    There is always at least 1 row of colors: the "pure" colors: pure red, pure blue, pure green, pure fushia...\n
	 *                         Additionnaly, you can add row on top: they will contain the same colors, but lighter.\n
	 *                         The parameter @p lightRowCount specify how many different lighting grades shoud be shown (from near to white, but not white, to "pure").
	 * @param darkRowCount     Finally, on bottom of the row of "pure colors", you can put any variety of dark colors (from "pure", to near to black, but not black).\n
	 *                         So, the number of rows is equal to @p lightRowCount + 1 + @p darkRowCount. On top are light colors, gradually going to dark ones on bottom.
	 * @param withGray         If true, another column (so there will be @p colorColumnCount+1 columns) is added on the very-right of the popup
	 *                         to show different gray values, matching the brightness of the sibling colors.
	 *
	 * The most acceptable parameters:
	 * @li The default values are good to have the 7 colors of the rainbow + colors between them, and light/dark colors are well distinct.
	 * @li If the color is a background color, you can set @p darkRowCount to 0, so only light colors are shown.
	 * @li The inverse is true for text color choice: you can set @p lightRowCount to 0.
	 * @li But be careful: some advanced users prefer white text on dark background, so you eg. can set @p lightRowCount to a big value and
	 *     @p darkRowCount to a small one for a fewer choice of dark colors, but at least some ones.
	 */
	void setRainbowPreset(int colorColumnCount = 12, int lightRowCount = 4, int darkRowCount = 4, bool withGray = true);
	//void setHsvPreset(TQColor hue[], TQColor saturation[], TQColor value[], bool withGray = true);

	/**
	 * Returns a pixmap of a colored rounded-rectangle. The four corners are transparent.\n
	 * Useful if you want to set such a rectangle as an icon for a menu entry, or for drag and drop operation...
	 * @param color     The color of the rectangle. If the color is invalid, a rainbow is then drawn (like for the "Other..." entry in the popup).
	 * @param isDefault True if @p color is the default one and should then be draw with a diagonal line.
	 * @param width     The width of the rectangle pixmap to return.
	 * @param height    The height of the rectangle pixmap to return.
	 *
	 * @see drawColorRect() if you need to draw it directly: it's faster.
	 */
	static TQPixmap colorRectPixmap(const TQColor &color, bool isDefault, int width, int height);

	/**
	 * Draw an image of a colored rounded-rectangle.\n
	 * This is like colorRectPixmap() but significantly faster because there is nothing to copy, and no transparency mask to create and apply.
	 * @param painter   The painter where to draw the image.
	 * @param x         The x coordinate on the @p painter where to draw the rectangle.
	 * @param y         The y coordinate on the @p painter where to draw the rectangle.
	 * @param color     The color of the rectangle. If the color is invalid, a rainbow is then drawn (like for the "Other..." entry in the popup).
	 * @param isDefault True if @p color is the default one and should then be draw with a diagonal line.
	 * @param width     The width of the rectangle pixmap to return.
	 * @param height    The height of the rectangle pixmap to return.
	 *
	 * @see colorRectPixmap() to get a transparent pixmap of the rectangle.
	 */
	static void drawColorRect(TQPainter &painter, int x, int y, const TQColor &color, bool isDefault, int width, int height);

	/**
	 * Get the height of a color rectangle for this combobox.\n
	 * This is equal to the text height, regarding to the current font of this combobox.
	 */
	int colorRectHeight() const;

	/**
	 * Get the width of a color rectangle, depending of the @p height of it.\n
	 * It typically return 1.4 * @p height for decent rectangle proportions.
	 */
	int colorRectWidthForHeight(int height) const;

  protected:
	virtual void popup();
	virtual void mousePressEvent(TQMouseEvent *event);
	virtual void mouseMoveEvent(TQMouseEvent *event);
	virtual bool eventFilter(TQObject *object, TQEvent *event);
	virtual void dragEnterEvent(TQDragEnterEvent *event);
	virtual void dropEvent(TQDropEvent *event);
	virtual void keyPressEvent(TQKeyEvent *event);
	virtual void fontChange(const TQFont &oldFont);

  private:
	/**
	 * Initialization routine common to every constructors.\n
	 * Constructors just have to initialize the TQComboBox, m_color and m_defaultColor
	 * and this function do the rest to complete the creation of this widget.
	 */
	void init();

	/**
	 * Free up all memory allocated for the color array.\n
	 * But only if an array have previously been allocated, of course.
	 */
	void deleteColorArray();

	/**
	 * Update the only item of the combobox to mirror the new selected color.\n
	 * Mainly called on init() and setColor().
	 */
	void updateComboBox();

	KColorPopup *m_popup;
	TQColor       m_color;
	TQColor       m_defaultColor;
	bool         m_discardNextMousePress;
	TQColor     **m_colorArray;
	int          m_columnCount;
	int          m_rowCount;
	TQPoint       m_dragStartPos;

  protected:
	/**
	 * Keep place for future improvements without having to break binary compatibility.\n
	 * Does nothing for the moment.
	 */
	virtual void virtual_hook(int id, void *data);

  private:
	/**
	 * Keep place for future improvements without having to break binary compatibility.
	 */
	class KColorCombo2Private;

	KColorCombo2Private *d;
};



// TODO: setColorArray(TQColor **, int, int) and use signals/slots ??

class KColorPopup : public TQWidget
{
  Q_OBJECT
  
  public:
	KColorPopup(KColorCombo2 *parent);
	~KColorPopup();
	void relayout(); // updateGeometry() ??
  protected:
	void paintEvent(TQPaintEvent */*event*/);
	void mouseMoveEvent(TQMouseEvent *event);
	void mousePressEvent(TQMouseEvent *event);
	void keyPressEvent(TQKeyEvent *event);
	void doSelection();
	void validate();
	void updateCell(int column, int row);

	friend class KColorCombo2;

  private:
	KColorCombo2 *m_selector;
	TQPixmap m_pixmap;
	int m_selectedRow;
	int m_selectedColumn;
	int m_columnOther;
	TQColor m_otherColor;

	static const int MARGIN;
	static const int FRAME_WIDTH;
};



#endif // KCOLORCOMBO2_H