KiCad PCB EDA Suite
dialog_choose_component.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
5  * Copyright (C) 2016-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
26 
27 #include <algorithm>
28 #include <set>
29 #include <wx/utils.h>
30 
31 #include <wx/button.h>
32 #include <wx/dataview.h>
33 #include <wx/panel.h>
34 #include <wx/sizer.h>
35 #include <wx/splitter.h>
36 #include <wx/timer.h>
37 #include <wx/utils.h>
38 
39 #include <class_library.h>
40 #include <sch_base_frame.h>
41 #include <template_fieldnames.h>
42 #include <symbol_lib_table.h>
43 #include <widgets/component_tree.h>
46 
47 
50 
52 
53 
55  CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, int aDeMorganConvert, bool aAllowFieldEdits,
56  bool aShowFootprints )
57  : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
58  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
59  m_fp_sel_ctrl( nullptr ),
60  m_fp_view_ctrl( nullptr ),
61  m_parent( aParent ),
62  m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ),
63  m_allow_field_edits( aAllowFieldEdits ),
64  m_show_footprints( aShowFootprints ),
65  m_load_footprints( aShowFootprints ),
66  m_external_browser_requested( false )
67 {
69 
70  auto sizer = new wxBoxSizer( wxVERTICAL );
71 
72  // Use a slightly different layout, with a details pane spanning the entire window,
73  // if we're not showing footprints.
74  auto vsplitter = aShowFootprints ? nullptr : new wxSplitterWindow(
75  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE | wxSP_3DSASH );
76 
77  m_splitter_tree_canvas = new wxSplitterWindow(
78  vsplitter ? static_cast<wxWindow *>( vsplitter ) : static_cast<wxWindow *>( this ),
79  wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE | wxSP_3DSASH );
80 
81  auto details = aShowFootprints ? nullptr : new wxHtmlWindow(
82  vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO );
83 
84  m_tree = new COMPONENT_TREE( m_splitter_tree_canvas, Prj().SchSymbolLibTable(),
85  aAdapter, COMPONENT_TREE::WIDGETS::ALL, details );
87  auto buttons = new wxStdDialogButtonSizer();
88  m_dbl_click_timer = new wxTimer( this );
89 
90  if( vsplitter )
91  sizer->Add( vsplitter, 1, wxEXPAND | wxALL, 5 );
92  else
93  sizer->Add( m_splitter_tree_canvas, 1, wxEXPAND | wxALL, 5 );
94 
95  buttons->AddButton( new wxButton( this, wxID_OK ) );
96  buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
97  buttons->Realize();
98 
99  sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 );
100  SetSizer( sizer );
101 
102  Bind( wxEVT_INIT_DIALOG, &DIALOG_CHOOSE_COMPONENT::OnInitDialog, this );
103  Bind( wxEVT_IDLE, &DIALOG_CHOOSE_COMPONENT::OnIdle, this );
104  Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this, m_dbl_click_timer->GetId() );
105  Bind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentPreselected, this );
106  Bind( COMPONENT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentSelected, this );
107 
108  m_sch_view_ctrl->Bind( wxEVT_LEFT_DCLICK, &DIALOG_CHOOSE_COMPONENT::OnSchViewDClick, this );
109  m_sch_view_ctrl->Bind( wxEVT_PAINT, &DIALOG_CHOOSE_COMPONENT::OnSchViewPaint, this );
110 
111  if( m_fp_sel_ctrl )
112  m_fp_sel_ctrl->Bind( EVT_FOOTPRINT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnFootprintSelected, this );
113 
114  Layout();
115 
116  if( m_last_dlg_size == wxSize( -1, -1 ) )
117  SetSizeInDU( 320, 256 );
118  else
119  SetSize( m_last_dlg_size );
120 
121  m_splitter_tree_canvas->SetSashGravity( 0.8 );
122  m_splitter_tree_canvas->SetMinimumPaneSize( 20 );
123  // We specify the width of the right window (m_symbol_view_panel), because specify
124  // the width of the left window does not work as expected when SetSashGravity() is called
127  : HorizPixelsFromDU( -100 ) );
128 
129  if( vsplitter )
130  {
131  vsplitter->SetSashGravity( 0.5 );
132  vsplitter->SetMinimumPaneSize( 20 );
133  vsplitter->SplitHorizontally( m_splitter_tree_canvas, details, VertPixelsFromDU( -80 ) );
134  }
135 }
136 
137 
139 {
140  // I am not sure the following two lines are necessary,
141  // but they will not hurt anyone
142  m_dbl_click_timer->Stop();
143  Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this );
144 
145  delete m_dbl_click_timer;
146 
147  m_last_dlg_size = GetSize();
149  - m_splitter_tree_canvas->GetSashPosition();
150 }
151 
152 
153 wxPanel* DIALOG_CHOOSE_COMPONENT::ConstructRightPanel( wxWindow* aParent )
154 {
155  auto panel = new wxPanel( aParent );
156  auto sizer = new wxBoxSizer( wxVERTICAL );
157 
158  m_sch_view_ctrl = new wxPanel( panel, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ),
159  wxFULL_REPAINT_ON_RESIZE | wxTAB_TRAVERSAL );
160  m_sch_view_ctrl->SetLayoutDirection( wxLayout_LeftToRight );
161 
162  if( m_show_footprints )
163  {
164  if( m_allow_field_edits )
165  m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( panel, m_fp_list, true );
166 
168 
169 
170  sizer->Add( m_sch_view_ctrl, 1, wxEXPAND | wxALL, 5 );
171 
172  if( m_fp_sel_ctrl )
173  sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxALL, 5 );
174 
175  sizer->Add( m_fp_view_ctrl, 1, wxEXPAND | wxALL, 5 );
176  }
177  else
178  {
179  sizer->Add( m_sch_view_ctrl, 1, wxEXPAND | wxALL, 5 );
180  }
181 
182  panel->SetSizer( sizer );
183  panel->Layout();
184  sizer->Fit( panel );
185 
186  return panel;
187 }
188 
189 
190 void DIALOG_CHOOSE_COMPONENT::OnInitDialog( wxInitDialogEvent& aEvent )
191 {
193  {
194  // This hides the GAL panel and shows the status label
195  m_fp_view_ctrl->SetStatusText( wxEmptyString );
196  }
197 }
198 
199 
200 // Let the dialog display before starting the footprint load
201 void DIALOG_CHOOSE_COMPONENT::OnIdle( wxIdleEvent& aEvent )
202 {
204  {
205  m_load_footprints = false;
206  m_fp_sel_ctrl->Load( Kiway(), Prj() );
207  }
208 }
209 
210 
212 {
213  return m_tree->GetSelectedLibId( aUnit );
214 }
215 
216 
217 void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent )
218 {
219  // Hack handler because of eaten MouseUp event. See
220  // DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation for the beginning
221  // of this spaghetti noodle.
222 
223  auto state = wxGetMouseState();
224 
225  if( state.LeftIsDown() )
226  {
227  // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
228  // purpose of this timer is defeated.
230  }
231  else
232  {
233  EndQuasiModal( wxID_OK );
234  }
235 }
236 
237 
238 void DIALOG_CHOOSE_COMPONENT::OnSchViewDClick( wxMouseEvent& aEvent )
239 {
241  EndQuasiModal( wxID_OK );
242 }
243 
244 
246 {
248  return;
249 
250  LIB_ALIAS* alias = nullptr;
251 
252  try
253  {
254  alias = Prj().SchSymbolLibTable()->LoadSymbol( aLibId );
255  }
256  catch( const IO_ERROR& ioe )
257  {
258  wxLogError( wxString::Format( _( "Error occurred loading symbol %s from library %s."
259  "\n\n%s" ),
260  aLibId.GetLibItemName().wx_str(),
261  aLibId.GetLibNickname().wx_str(),
262  ioe.What() ) );
263  }
264 
265  if( alias == nullptr )
266  {
267  return;
268  }
269 
270  LIB_FIELD* fp_field = alias->GetPart()->GetField( FOOTPRINT );
271  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
272 
273  ShowFootprint( fp_name );
274 }
275 
276 
277 void DIALOG_CHOOSE_COMPONENT::ShowFootprint( wxString const& aName )
278 {
279  if( !m_fp_view_ctrl )
280  {
281  return;
282  }
283 
284  if( aName == wxEmptyString )
285  {
286  m_fp_view_ctrl->SetStatusText( _( "No footprint specified" ) );
287  }
288  else
289  {
290  LIB_ID lib_id;
291 
292  if( lib_id.Parse( aName ) == -1 && lib_id.IsValid() )
293  {
295  m_fp_view_ctrl->CacheFootprint( lib_id );
296  m_fp_view_ctrl->DisplayFootprint( lib_id );
297  }
298  else
299  {
300  m_fp_view_ctrl->SetStatusText( _( "Invalid footprint specified" ) );
301  }
302  }
303 }
304 
305 
307 {
308  if( !m_fp_sel_ctrl )
309  return;
310 
312 
313  LIB_ALIAS* alias = nullptr;
314 
315  if( aLibId.IsValid() )
316  {
317  try
318  {
319  alias = Prj().SchSymbolLibTable()->LoadSymbol( aLibId );
320  }
321  catch( const IO_ERROR& ioe )
322  {
323  wxLogError( wxString::Format( _( "Error occurred loading symbol %s from library %s."
324  "\n\n%s" ),
325  aLibId.GetLibItemName().wx_str(),
326  aLibId.GetLibNickname().wx_str(),
327  ioe.What() ) );
328  }
329  }
330 
331  if( alias != nullptr )
332  {
333  LIB_PINS temp_pins;
334  LIB_FIELD* fp_field = alias->GetPart()->GetField( FOOTPRINT );
335  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
336 
337  alias->GetPart()->GetPins( temp_pins );
338 
339  m_fp_sel_ctrl->FilterByPinCount( temp_pins.size() );
344  }
345  else
346  {
348  m_fp_sel_ctrl->Disable();
349  }
350 }
351 
352 
353 void DIALOG_CHOOSE_COMPONENT::OnSchViewPaint( wxPaintEvent& aEvent )
354 {
355  int unit = 0;
356  LIB_ID id = m_tree->GetSelectedLibId( &unit );
357 
358  if( !id.IsValid() )
359  {
360  // No symbol to show, display a tooltip
361  RenderPreview( nullptr, unit );
362  return;
363  }
364 
365  LIB_ALIAS* alias = nullptr;
366 
367  try
368  {
369  alias = Prj().SchSymbolLibTable()->LoadSymbol( id );
370  }
371  catch( const IO_ERROR& ioe )
372  {
373  wxLogError( wxString::Format( _( "Error occurred loading symbol %s from library %s."
374  "\n\n%s" ),
375  id.GetLibItemName().wx_str(),
376  id.GetLibNickname().wx_str(),
377  ioe.What() ) );
378  }
379 
380  if( alias == nullptr )
381  return;
382 
383  LIB_PART* part = alias ? alias->GetPart() : nullptr;
384 
385  // Don't draw if we don't have a part to show
386  // just display a tooltip
387  if( !part )
388  {
389  RenderPreview( nullptr, unit );
390  return;
391  }
392 
393  if( alias->IsRoot() )
394  {
395  // just show the part directly
396  RenderPreview( part, unit );
397  }
398  else
399  {
400  // switch out the name temporarily for the alias name
401  wxString tmp( part->GetName() );
402  part->SetName( alias->GetName() );
403 
404  RenderPreview( part, unit );
405 
406  part->SetName( tmp );
407  }
408 }
409 
410 
411 void DIALOG_CHOOSE_COMPONENT::OnFootprintSelected( wxCommandEvent& aEvent )
412 {
413  m_fp_override = aEvent.GetString();
414 
415  m_field_edits.erase(
416  std::remove_if( m_field_edits.begin(), m_field_edits.end(),
417  []( std::pair<int, wxString> const& i ) { return i.first == FOOTPRINT; } ),
418  m_field_edits.end() );
419 
420  m_field_edits.push_back( std::make_pair( FOOTPRINT, m_fp_override ) );
421 
423 }
424 
425 
427 {
428  int unit = 0;
429 
430  LIB_ID id = m_tree->GetSelectedLibId( &unit );
431 
432  m_sch_view_ctrl->Refresh();
433 
434  if( id.IsValid() )
435  {
436  ShowFootprintFor( id );
438  }
439  else
440  {
442  m_fp_view_ctrl->SetStatusText( wxEmptyString );
443 
445  }
446 }
447 
448 
449 void DIALOG_CHOOSE_COMPONENT::OnComponentSelected( wxCommandEvent& aEvent )
450 {
451  if( m_tree->GetSelectedLibId().IsValid() )
452  {
453  // Got a selection. We can't just end the modal dialog here, because
454  // wx leaks some events back to the parent window (in particular, the
455  // MouseUp following a double click).
456  //
457  // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
458  // This isn't really feasible to bypass without a fully custom
459  // wxDataViewCtrl implementation, and even then might not be fully
460  // possible (docs are vague). To get around this, we use a one-shot
461  // timer to schedule the dialog close.
462  //
463  // See DIALOG_CHOOSE_COMPONENT::OnCloseTimer for the other end of this
464  // spaghetti noodle.
466  }
467 }
468 
469 
470 void DIALOG_CHOOSE_COMPONENT::RenderPreview( LIB_PART* aComponent, int aUnit )
471 {
472  wxPaintDC dc( m_sch_view_ctrl );
473 
474  const wxSize dc_size = dc.GetSize();
475 
476  // Avoid rendering when either dimension is zero
477  if( dc_size.x == 0 || dc_size.y == 0 )
478  return;
479 
480  if( !aComponent ) // display a tooltip
481  {
482  wxString tooltip = _( "Double-click here to select a symbol from the library browser" );
483  GRDrawWrappedText( dc, tooltip );
484  return;
485  }
486 
487  GRResetPenAndBrush( &dc );
488 
489  COLOR4D bgColor = m_parent->GetDrawBgColor();
490 
491  dc.SetBackground( wxBrush( bgColor.ToColour() ) );
492  dc.Clear();
493 
494  int unit = aUnit > 0 ? aUnit : 1;
495  int convert = m_deMorganConvert > 0 ? m_deMorganConvert : 1;
496 
497  dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );
498 
499  // Find joint bounding box for everything we are about to draw.
500  EDA_RECT bBox = aComponent->GetUnitBoundingBox( unit, convert );
501  const double xscale = (double) dc_size.x / bBox.GetWidth();
502  const double yscale = (double) dc_size.y / bBox.GetHeight();
503  const double scale = std::min( xscale, yscale ) * 0.85;
504 
505  dc.SetUserScale( scale, scale );
506 
507  wxPoint offset = -bBox.Centre();
508 
509  auto opts = PART_DRAW_OPTIONS::Default();
510  opts.draw_hidden_fields = false;
511  aComponent->Draw( nullptr, &dc, offset, unit, convert, opts );
512 }
FOOTPRINT_SELECT_WIDGET * m_fp_sel_ctrl
bool IsValid() const
Definition: lib_id.h:158
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:218
LIB_FIELD * GetField(int aId)
Return pointer to the requested field.
void Draw(EDA_DRAW_PANEL *aPanel, wxDC *aDc, const wxPoint &aOffset, int aMulti, int aConvert, const PART_DRAW_OPTIONS &aOpts)
Draw part.
const EDA_RECT GetUnitBoundingBox(int aUnit, int aConvert) const
Get the bounding box for the symbol.
Part library alias object definition.
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_player.h:60
void FilterByFootprintFilters(wxArrayString const &aFilters, bool aZeroFilters)
Filter by footprint filter list.
std::vector< std::pair< int, wxString > > m_field_edits
void PopulateFootprintSelector(LIB_ID const &aLibId)
Populate the footprint selector for a given alias.
void GRDrawWrappedText(wxDC &aDC, wxString const &aText)
Draw text centered on a wxDC with wrapping.
Definition: gr_basic.cpp:1302
void OnFootprintSelected(wxCommandEvent &aEvent)
Field object used in symbol libraries.
Definition: lib_field.h:59
void SetDefaultFootprint(wxString const &aFp)
Set the default footprint for a part.
void ShowFootprint(wxString const &aFootprint)
Display the given footprint by name.
int GetHeight() const
Definition: eda_rect.h:118
int Parse(const UTF8 &aId)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:70
wxString GetFullText(int unit=1) const
Return the text of a field.
Definition: lib_field.cpp:356
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void OnIdle(wxIdleEvent &aEvent)
DIALOG_CHOOSE_COMPONENT(SCH_BASE_FRAME *aParent, const wxString &aTitle, CMP_TREE_MODEL_ADAPTER::PTR &aAdapter, int aDeMorganConvert, bool aAllowFieldEdits, bool aShowFootprints)
Create dialog to choose component.
Field Name Module PCB, i.e. "16DIP300".
void OnComponentPreselected(wxCommandEvent &aEvent)
void ClearFilters()
Clear all filters.
bool UpdateList()
Update the contents of the list to match the filters.
void GetPins(LIB_PINS &aList, int aUnit=0, int aConvert=0)
Return a list of pin object pointers from the draw item list.
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
bool IsRoot() const
static FOOTPRINT_LIST * GetInstance(KIWAY &aKiway)
Factory function to return a FOOTPRINT_LIST via Kiway.
wxString wx_str() const
Definition: utf8.cpp:48
void OnInitDialog(wxInitDialogEvent &aEvent)
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
For multi-unit components, if the user selects the component itself rather than picking an individual...
void ClearStatus()
Clear the contents of the status label and hide it.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:118
wxObjectDataPtr< CMP_TREE_MODEL_ADAPTER_BASE > PTR
Reference-counting container for a pointer to CMP_TREE_MODEL_ADAPTER_BASE.
void SetSizeInDU(int x, int y)
Set the dialog to the given dimensions in "dialog units".
Define a library symbol object.
void OnCloseTimer(wxTimerEvent &aEvent)
virtual bool Enable(bool aEnable=true) override
Enable or disable the control for input.
wxPanel * ConstructRightPanel(wxWindow *aParent)
Definition: hash_eda.h:46
wxPoint Centre() const
Definition: eda_rect.h:60
static PART_DRAW_OPTIONS Default()
void SetStatusText(wxString const &aText)
Set the contents of the status label and display it.
COLOR4D GetDrawBgColor() const override
Widget displaying a tree of components with optional search text control and description panel...
wxArrayString & GetFootprints()
int VertPixelsFromDU(int y)
Convert an integer number of dialog units to pixels, vertically.
LIB_PART * GetPart() const
Get the shared LIB_PART.
void EndQuasiModal(int retCode)
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_draw_item.h:60
const int scale
void CacheFootprint(const LIB_ID &aFPID)
Preload a footprint into the cache.
const wxString & GetName() const
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
bool IsInitialized() const
Return whether the widget initialized properly.
wxSplitterWindow * m_splitter_tree_canvas
void DisplayFootprint(const LIB_ID &aFPID)
Set the currently displayed footprint.
static constexpr int DblClickDelay
size_t i
Definition: json11.cpp:597
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
int GetWidth() const
Definition: eda_rect.h:117
void OnSchViewDClick(wxMouseEvent &aEvent)
const wxString & GetName() const
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
To be called after this dialog returns from ShowModal().
virtual void SetName(const wxString &aName)
void Load(KIWAY &aKiway, PROJECT &aProject)
Start loading.
int HorizPixelsFromDU(int x)
Convert an integer number of dialog units to pixels, horizontally.
void ShowFootprintFor(LIB_ID const &aLibId)
Look up the footprint for a given symbol specified in the LIB_ID and display it.
FOOTPRINT_PREVIEW_WIDGET * m_fp_view_ctrl
Definition for part library class.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:101
void OnSchViewPaint(wxPaintEvent &aEvent)
A shim class between EDA_DRAW_FRAME and several derived classes: LIB_EDIT_FRAME, LIB_VIEW_FRAME, and SCH_EDIT_FRAME, and it brings in a common way of handling the provided virtual functions for the derived classes.
void OnComponentSelected(wxCommandEvent &aEvent)
Handle the selection of an item.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
void FilterByPinCount(int aPinCount)
Filter by pin count.
#define min(a, b)
Definition: auxiliary.h:85
void RenderPreview(LIB_PART *aComponent, int aUnit)
Display a given symbol into the schematic symbol preview.
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39