1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <widgets/color_swatch.h>
25 #include <wx/dcmemory.h>
26
27 #include <dialogs/dialog_color_picker.h>
28 #include <memory>
29
30 wxDEFINE_EVENT( COLOR_SWATCH_CHANGED, wxCommandEvent );
31
32 using KIGFX::COLOR4D;
33
34
35 // See selcolor.cpp:
36 extern COLOR4D DisplayColorFrame( wxWindow* aParent, COLOR4D aOldColor );
37
38
MakeBitmap(const COLOR4D & aColor,const COLOR4D & aBackground,const wxSize & aSize,const wxSize & aCheckerboardSize,const COLOR4D & aCheckerboardBackground)39 wxBitmap COLOR_SWATCH::MakeBitmap( const COLOR4D& aColor, const COLOR4D& aBackground,
40 const wxSize& aSize, const wxSize& aCheckerboardSize,
41 const COLOR4D& aCheckerboardBackground )
42 {
43 wxBitmap bitmap( aSize );
44 wxBrush brush;
45 wxPen pen;
46 wxMemoryDC iconDC;
47
48 iconDC.SelectObject( bitmap );
49 brush.SetStyle( wxBRUSHSTYLE_SOLID );
50
51 if( aColor == COLOR4D::UNSPECIFIED )
52 {
53 // Draw a checkerboard
54 COLOR4D white;
55 COLOR4D black;
56 bool rowCycle;
57
58 if( aCheckerboardBackground.GetBrightness() > 0.4 )
59 {
60 white = COLOR4D::WHITE;
61 black = white.Darkened( 0.15 );
62 rowCycle = true;
63 }
64 else
65 {
66 black = COLOR4D::BLACK;
67 white = black.Brightened( 0.15 );
68 rowCycle = false;
69 }
70
71 for( int x = 0; x < aSize.x; x += aCheckerboardSize.x )
72 {
73 bool colCycle = rowCycle;
74
75 for( int y = 0; y < aSize.y; y += aCheckerboardSize.y )
76 {
77 COLOR4D color = colCycle ? black : white;
78 brush.SetColour( color.ToColour() );
79 pen.SetColour( color.ToColour() );
80
81 iconDC.SetBrush( brush );
82 iconDC.SetPen( pen );
83 iconDC.DrawRectangle( x, y, x + aCheckerboardSize.x, y + aCheckerboardSize.y );
84
85 colCycle = !colCycle;
86 }
87
88 rowCycle = !rowCycle;
89 }
90 }
91 else
92 {
93 brush.SetColour( aBackground.WithAlpha(1.0).ToColour() );
94 pen.SetColour( aBackground.WithAlpha(1.0).ToColour() );
95
96 iconDC.SetBrush( brush );
97 iconDC.SetPen( pen );
98 iconDC.DrawRectangle( 0, 0, aSize.x, aSize.y );
99
100 brush.SetColour( aColor.ToColour() );
101 pen.SetColour( aColor.ToColour() );
102
103 iconDC.SetBrush( brush );
104 iconDC.SetPen( pen );
105 iconDC.DrawRectangle( 0, 0, aSize.x, aSize.y );
106 }
107
108 return bitmap;
109 }
110
111
COLOR_SWATCH(wxWindow * aParent,const COLOR4D & aColor,int aID,const COLOR4D & aBackground,const COLOR4D & aDefault,SWATCH_SIZE aSwatchSize)112 COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, const COLOR4D& aColor, int aID,
113 const COLOR4D& aBackground, const COLOR4D& aDefault,
114 SWATCH_SIZE aSwatchSize ) :
115 wxPanel( aParent, aID ),
116 m_color( aColor ),
117 m_background( aBackground ),
118 m_default( aDefault ),
119 m_userColors( nullptr ),
120 m_readOnly( false ),
121 m_supportsOpacity( true )
122 {
123 wxASSERT_MSG( aSwatchSize != SWATCH_EXPAND, "SWATCH_EXPAND not supported in COLOR_SWATCH" );
124
125 switch( aSwatchSize )
126 {
127 case SWATCH_MEDIUM: m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU ); break;
128 case SWATCH_SMALL: m_size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU ); break;
129 case SWATCH_LARGE: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
130 case SWATCH_EXPAND: m_size = ConvertDialogToPixels( SWATCH_SIZE_LARGE_DU ); break;
131 }
132
133 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
134 m_checkerboardBg = aParent->GetBackgroundColour();
135
136 auto sizer = new wxBoxSizer( wxHORIZONTAL );
137 SetSizer( sizer );
138
139 wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( aColor, aBackground, m_size,
140 m_checkerboardSize, m_checkerboardBg );
141 m_swatch = new wxStaticBitmap( this, aID, bitmap );
142
143 sizer->Add( m_swatch, 0, 0 );
144
145 setupEvents();
146 }
147
148
COLOR_SWATCH(wxWindow * aParent,wxWindowID aID,const wxPoint & aPos,const wxSize & aSize,long aStyle)149 COLOR_SWATCH::COLOR_SWATCH( wxWindow* aParent, wxWindowID aID, const wxPoint& aPos,
150 const wxSize& aSize, long aStyle ) :
151 wxPanel( aParent, aID, aPos, aSize, aStyle ),
152 m_userColors( nullptr ),
153 m_readOnly( false ),
154 m_supportsOpacity( true )
155 {
156 if( aSize == wxDefaultSize )
157 m_size = ConvertDialogToPixels( SWATCH_SIZE_MEDIUM_DU );
158 else
159 m_size = aSize;
160
161 m_checkerboardSize = ConvertDialogToPixels( CHECKERBOARD_SIZE_DU );
162 m_checkerboardBg = aParent->GetBackgroundColour();
163
164 SetSize( m_size );
165
166 auto sizer = new wxBoxSizer( wxHORIZONTAL );
167 SetSizer( sizer );
168
169 wxBitmap bitmap = COLOR_SWATCH::MakeBitmap( COLOR4D::UNSPECIFIED, COLOR4D::UNSPECIFIED,
170 m_size, m_checkerboardSize, m_checkerboardBg );
171 m_swatch = new wxStaticBitmap( this, aID, bitmap );
172
173 sizer->Add( m_swatch, 0, 0 );
174
175 setupEvents();
176 }
177
178
setupEvents()179 void COLOR_SWATCH::setupEvents()
180 {
181 wxWindow* topLevelParent = GetParent();
182
183 while( topLevelParent && !topLevelParent->IsTopLevel() )
184 topLevelParent = topLevelParent->GetParent();
185
186 if( topLevelParent && dynamic_cast<DIALOG_SHIM*>( topLevelParent ) )
187 {
188 m_swatch->Bind( wxEVT_LEFT_DOWN,
189 [this] ( wxMouseEvent& aEvt )
190 {
191 GetNewSwatchColor();
192 } );
193 }
194 else
195 {
196 // forward click to any other listeners, since we don't want them
197 m_swatch->Bind( wxEVT_LEFT_DOWN, &COLOR_SWATCH::rePostEvent, this );
198
199 // bind the events that trigger the dialog
200 m_swatch->Bind( wxEVT_LEFT_DCLICK,
201 [this] ( wxMouseEvent& aEvt )
202 {
203 GetNewSwatchColor();
204 } );
205 }
206
207 m_swatch->Bind( wxEVT_MIDDLE_DOWN,
208 [this] ( wxMouseEvent& aEvt )
209 {
210 GetNewSwatchColor();
211 } );
212
213 m_swatch->Bind( wxEVT_RIGHT_DOWN, &COLOR_SWATCH::rePostEvent, this );
214 }
215
216
rePostEvent(wxEvent & aEvent)217 void COLOR_SWATCH::rePostEvent( wxEvent& aEvent )
218 {
219 wxPostEvent( this, aEvent );
220 }
221
222
sendSwatchChangeEvent(COLOR_SWATCH & aSender)223 static void sendSwatchChangeEvent( COLOR_SWATCH& aSender )
224 {
225 wxCommandEvent changeEvt( COLOR_SWATCH_CHANGED );
226
227 // use this class as the object (alternative might be to
228 // set a custom event class but that's more work)
229 changeEvt.SetEventObject( &aSender );
230
231 wxPostEvent( &aSender, changeEvt );
232 }
233
234
SetSwatchColor(const COLOR4D & aColor,bool aSendEvent)235 void COLOR_SWATCH::SetSwatchColor( const COLOR4D& aColor, bool aSendEvent )
236 {
237 m_color = aColor;
238
239 wxBitmap bm = MakeBitmap( m_color, m_background, m_size, m_checkerboardSize, m_checkerboardBg );
240 m_swatch->SetBitmap( bm );
241
242 if( aSendEvent )
243 sendSwatchChangeEvent( *this );
244 }
245
246
SetDefaultColor(const COLOR4D & aColor)247 void COLOR_SWATCH::SetDefaultColor( const COLOR4D& aColor )
248 {
249 m_default = aColor;
250 }
251
252
SetSwatchBackground(const COLOR4D & aBackground)253 void COLOR_SWATCH::SetSwatchBackground( const COLOR4D& aBackground )
254 {
255 m_background = aBackground;
256 wxBitmap bm = MakeBitmap( m_color, m_background, m_size, m_checkerboardSize, m_checkerboardBg );
257 m_swatch->SetBitmap( bm );
258 }
259
260
GetSwatchColor() const261 COLOR4D COLOR_SWATCH::GetSwatchColor() const
262 {
263 return m_color;
264 }
265
266
GetNewSwatchColor()267 void COLOR_SWATCH::GetNewSwatchColor()
268 {
269 if( m_readOnly )
270 {
271 if( m_readOnlyCallback )
272 m_readOnlyCallback();
273
274 return;
275 }
276
277 DIALOG_COLOR_PICKER dialog( ::wxGetTopLevelParent( this ), m_color, m_supportsOpacity,
278 m_userColors, m_default );
279
280 if( dialog.ShowModal() == wxID_OK )
281 {
282 COLOR4D newColor = dialog.GetColor();
283
284 if( newColor != COLOR4D::UNSPECIFIED || m_default == COLOR4D::UNSPECIFIED )
285 {
286 m_color = newColor;
287
288 wxBitmap bm = MakeBitmap( newColor, m_background, m_size, m_checkerboardSize,
289 m_checkerboardBg );
290 m_swatch->SetBitmap( bm );
291
292 sendSwatchChangeEvent( *this );
293 }
294 }
295 }
296