1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/ctrlcmn.cpp
3 // Purpose: wxControl common interface
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/control.h"
34 #include "wx/radiobut.h"
35 #include "wx/statbmp.h"
36 #include "wx/bitmap.h"
37 #include "wx/utils.h" // for wxStripMenuCodes()
38 #include "wx/settings.h"
41 #include "wx/private/markupparser.h"
43 const char wxControlNameStr
[] = "control";
45 // ============================================================================
47 // ============================================================================
49 wxControlBase::~wxControlBase()
51 // this destructor is required for Darwin
54 bool wxControlBase::Create(wxWindow
*parent
,
59 const wxValidator
& wxVALIDATOR_PARAM(validator
),
62 bool ret
= wxWindow::Create(parent
, id
, pos
, size
, style
, name
);
66 SetValidator(validator
);
67 #endif // wxUSE_VALIDATORS
72 bool wxControlBase::CreateControl(wxWindowBase
*parent
,
77 const wxValidator
& validator
,
80 // even if it's possible to create controls without parents in some port,
81 // it should surely be discouraged because it doesn't work at all under
83 wxCHECK_MSG( parent
, false, wxT("all controls must have parents") );
85 if ( !CreateBase(parent
, id
, pos
, size
, style
, validator
, name
) )
88 parent
->AddChild(this);
93 void wxControlBase::Command(wxCommandEvent
& event
)
95 (void)GetEventHandler()->ProcessEvent(event
);
98 void wxControlBase::InitCommandEvent(wxCommandEvent
& event
) const
100 event
.SetEventObject(const_cast<wxControlBase
*>(this));
102 // event.SetId(GetId()); -- this is usuall done in the event ctor
104 switch ( m_clientDataType
)
106 case wxClientData_Void
:
107 event
.SetClientData(GetClientData());
110 case wxClientData_Object
:
111 event
.SetClientObject(GetClientObject());
114 case wxClientData_None
:
120 bool wxControlBase::SetFont(const wxFont
& font
)
122 InvalidateBestSize();
123 return wxWindow::SetFont(font
);
126 // wxControl-specific processing after processing the update event
127 void wxControlBase::DoUpdateWindowUI(wxUpdateUIEvent
& event
)
130 wxWindowBase::DoUpdateWindowUI(event
);
133 if ( event
.GetSetText() )
135 if ( event
.GetText() != GetLabel() )
136 SetLabel(event
.GetText());
139 // Unfortunately we don't yet have common base class for
140 // wxRadioButton, so we handle updates of radiobuttons here.
141 // TODO: If once wxRadioButtonBase will exist, move this code there.
143 if ( event
.GetSetChecked() )
145 wxRadioButton
*radiobtn
= wxDynamicCastThis(wxRadioButton
);
147 radiobtn
->SetValue(event
.GetChecked());
149 #endif // wxUSE_RADIOBTN
152 wxSize
wxControlBase::DoGetSizeFromTextSize(int WXUNUSED(xlen
),
153 int WXUNUSED(ylen
)) const
155 return wxSize(-1, -1);
159 wxString
wxControlBase::GetLabelText(const wxString
& label
)
161 // we don't want strip the TABs here, just the mnemonics
162 return wxStripMenuCodes(label
, wxStrip_Mnemonics
);
166 wxString
wxControlBase::RemoveMnemonics(const wxString
& str
)
168 // we don't want strip the TABs here, just the mnemonics
169 return wxStripMenuCodes(str
, wxStrip_Mnemonics
);
173 wxString
wxControlBase::EscapeMnemonics(const wxString
& text
)
175 wxString
label(text
);
176 label
.Replace("&", "&&");
181 int wxControlBase::FindAccelIndex(const wxString
& label
, wxString
*labelOnly
)
183 // the character following MNEMONIC_PREFIX is the accelerator for this
184 // control unless it is MNEMONIC_PREFIX too - this allows to insert
185 // literal MNEMONIC_PREFIX chars into the label
186 static const wxChar MNEMONIC_PREFIX
= wxT('&');
191 labelOnly
->Alloc(label
.length());
195 for ( wxString::const_iterator pc
= label
.begin(); pc
!= label
.end(); ++pc
)
197 if ( *pc
== MNEMONIC_PREFIX
)
200 if ( pc
== label
.end() )
202 else if ( *pc
!= MNEMONIC_PREFIX
)
204 if ( indexAccel
== -1 )
206 // remember it (-1 is for MNEMONIC_PREFIX itself
207 indexAccel
= pc
- label
.begin() - 1;
211 wxFAIL_MSG(wxT("duplicate accel char in control label"));
225 wxBorder
wxControlBase::GetDefaultBorder() const
227 return wxBORDER_THEME
;
230 /* static */ wxVisualAttributes
231 wxControlBase::GetCompositeControlsDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
233 wxVisualAttributes attrs
;
234 attrs
.font
= wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
);
235 attrs
.colFg
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
);
236 attrs
.colBg
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
);
241 // ----------------------------------------------------------------------------
242 // wxControl markup support
243 // ----------------------------------------------------------------------------
248 wxString
wxControlBase::RemoveMarkup(const wxString
& markup
)
250 return wxMarkupParser::Strip(markup
);
253 bool wxControlBase::DoSetLabelMarkup(const wxString
& markup
)
255 const wxString label
= RemoveMarkup(markup
);
256 if ( label
.empty() && !markup
.empty() )
264 #endif // wxUSE_MARKUP
266 // ----------------------------------------------------------------------------
267 // wxControlBase - ellipsization code
268 // ----------------------------------------------------------------------------
270 #define wxELLIPSE_REPLACEMENT wxS("...")
275 struct EllipsizeCalculator
277 EllipsizeCalculator(const wxString
& s
, const wxDC
& dc
,
278 int maxFinalWidthPx
, int replacementWidthPx
)
280 m_initialCharToRemove(0),
282 m_outputNeedsUpdate(true),
285 m_maxFinalWidthPx(maxFinalWidthPx
),
286 m_replacementWidthPx(replacementWidthPx
)
288 m_isOk
= dc
.GetPartialTextExtents(s
, m_charOffsetsPx
);
289 wxASSERT( m_charOffsetsPx
.GetCount() == s
.length() );
292 bool IsOk() const { return m_isOk
; }
294 bool EllipsizationNotNeeded() const
296 // NOTE: charOffsetsPx[n] is the width in pixels of the first n characters (with the last one INCLUDED)
297 // thus charOffsetsPx[len-1] is the total width of the string
298 return m_charOffsetsPx
.Last() <= m_maxFinalWidthPx
;
301 void Init(size_t initialCharToRemove
, size_t nCharsToRemove
)
303 m_initialCharToRemove
= initialCharToRemove
;
304 m_nCharsToRemove
= nCharsToRemove
;
312 void RemoveFromStart()
314 m_initialCharToRemove
--;
318 size_t GetFirstRemoved() const { return m_initialCharToRemove
; }
319 size_t GetLastRemoved() const { return m_initialCharToRemove
+ m_nCharsToRemove
- 1; }
321 const wxString
& GetEllipsizedText()
323 if ( m_outputNeedsUpdate
)
325 wxASSERT(m_initialCharToRemove
<= m_str
.length() - 1); // see valid range for initialCharToRemove above
326 wxASSERT(m_nCharsToRemove
>= 1 && m_nCharsToRemove
<= m_str
.length() - m_initialCharToRemove
); // see valid range for nCharsToRemove above
328 // erase m_nCharsToRemove characters after m_initialCharToRemove (included);
329 // e.g. if we have the string "foobar" (len = 6)
331 // \--- m_initialCharToRemove = 2
332 // and m_nCharsToRemove = 2, then we get "foar"
334 m_output
.replace(m_initialCharToRemove
, m_nCharsToRemove
, wxELLIPSE_REPLACEMENT
);
342 if ( m_nCharsToRemove
== m_str
.length() )
343 return true; // that's the best we could do
345 // Width calculation using partial extents is just an inaccurate
346 // estimate: partial extents have sub-pixel precision and are rounded
347 // by GetPartialTextExtents(); replacing part of the string with "..."
348 // may change them too thanks to changes in ligatures, kerning etc.
350 // The correct algorithm would be to call GetTextExtent() in every step
351 // of ellipsization, but that would be too expensive, especially when
352 // the difference is just a few pixels. So we use partial extents to
353 // estimate string width and only verify it with GetTextExtent() when
356 int estimatedWidth
= m_replacementWidthPx
; // length of "..."
358 // length of text before the removed part:
359 if ( m_initialCharToRemove
> 0 )
360 estimatedWidth
+= m_charOffsetsPx
[m_initialCharToRemove
- 1];
362 // length of text after the removed part:
364 if ( GetLastRemoved() < m_str
.length() )
365 estimatedWidth
+= m_charOffsetsPx
.Last() - m_charOffsetsPx
[GetLastRemoved()];
367 if ( estimatedWidth
> m_maxFinalWidthPx
)
370 return m_dc
.GetTextExtent(GetEllipsizedText()).GetWidth() <= m_maxFinalWidthPx
;
373 // calculation state:
375 // REMEMBER: indexes inside the string have a valid range of [0;len-1] if not otherwise constrained
376 // lengths/counts of characters (e.g. nCharsToRemove) have a
377 // valid range of [0;len] if not otherwise constrained
378 // NOTE: since this point we know we have for sure a non-empty string from which we need
379 // to remove _at least_ one character (thus nCharsToRemove below is constrained to be >= 1)
381 // index of first character to erase, valid range is [0;len-1]:
382 size_t m_initialCharToRemove
;
383 // how many chars do we need to erase? valid range is [0;len-m_initialCharToRemove]
384 size_t m_nCharsToRemove
;
387 bool m_outputNeedsUpdate
;
392 int m_maxFinalWidthPx
;
393 int m_replacementWidthPx
;
394 wxArrayInt m_charOffsetsPx
;
399 } // anonymous namespace
401 /* static and protected */
402 wxString
wxControlBase::DoEllipsizeSingleLine(const wxString
& curLine
, const wxDC
& dc
,
403 wxEllipsizeMode mode
, int maxFinalWidthPx
,
404 int replacementWidthPx
)
406 wxASSERT_MSG(replacementWidthPx
> 0, "Invalid parameters");
407 wxASSERT_LEVEL_2_MSG(!curLine
.Contains('\n'),
408 "Use Ellipsize() instead!");
410 wxASSERT_MSG( mode
!= wxELLIPSIZE_NONE
, "shouldn't be called at all then" );
412 // NOTE: this function assumes that any mnemonic/tab character has already
413 // been handled if it was necessary to handle them (see Ellipsize())
415 if (maxFinalWidthPx
<= 0)
416 return wxEmptyString
;
418 size_t len
= curLine
.length();
422 EllipsizeCalculator
calc(curLine
, dc
, maxFinalWidthPx
, replacementWidthPx
);
427 if ( calc
.EllipsizationNotNeeded() )
430 // let's compute the range of characters to remove depending on the ellipsization mode:
433 case wxELLIPSIZE_START
:
436 while ( !calc
.IsShortEnough() )
437 calc
.RemoveFromEnd();
439 // always show at least one character of the string:
440 if ( calc
.m_nCharsToRemove
== len
)
441 return wxString(wxELLIPSE_REPLACEMENT
) + curLine
[len
-1];
446 case wxELLIPSIZE_MIDDLE
:
448 // NOTE: the following piece of code works also when len == 1
450 // start the removal process from the middle of the string
451 // i.e. separe the string in three parts:
452 // - the first one to preserve, valid range [0;initialCharToRemove-1] or the empty range if initialCharToRemove==0
453 // - the second one to remove, valid range [initialCharToRemove;endCharToRemove]
454 // - the third one to preserve, valid range [endCharToRemove+1;len-1] or the empty range if endCharToRemove==len-1
455 // NOTE: empty range != range [0;0] since the range [0;0] contains 1 character (the zero-th one)!
459 bool removeFromStart
= true;
461 while ( !calc
.IsShortEnough() )
463 const bool canRemoveFromStart
= calc
.GetFirstRemoved() > 0;
464 const bool canRemoveFromEnd
= calc
.GetLastRemoved() < len
- 1;
466 if ( !canRemoveFromStart
&& !canRemoveFromEnd
)
468 // we need to remove all the characters of the string!
472 // Remove from the beginning in even steps and from the end
473 // in odd steps, unless we exhausted one side already:
474 removeFromStart
= !removeFromStart
;
475 if ( removeFromStart
&& !canRemoveFromStart
)
476 removeFromStart
= false;
477 else if ( !removeFromStart
&& !canRemoveFromEnd
)
478 removeFromStart
= true;
480 if ( removeFromStart
)
481 calc
.RemoveFromStart();
483 calc
.RemoveFromEnd();
486 // Always show at least one character of the string.
487 // Additionally, if there's only one character left, prefer
489 if ( calc
.m_nCharsToRemove
== len
||
490 calc
.m_nCharsToRemove
== len
- 1 )
492 return curLine
[0] + wxString(wxELLIPSE_REPLACEMENT
);
497 case wxELLIPSIZE_END
:
499 calc
.Init(len
- 1, 1);
500 while ( !calc
.IsShortEnough() )
501 calc
.RemoveFromStart();
503 // always show at least one character of the string:
504 if ( calc
.m_nCharsToRemove
== len
)
505 return curLine
[0] + wxString(wxELLIPSE_REPLACEMENT
);
510 case wxELLIPSIZE_NONE
:
512 wxFAIL_MSG("invalid ellipsize mode");
516 return calc
.GetEllipsizedText();
520 wxString
wxControlBase::Ellipsize(const wxString
& label
, const wxDC
& dc
,
521 wxEllipsizeMode mode
, int maxFinalWidth
,
526 // these cannot be cached between different Ellipsize() calls as they can
527 // change because of e.g. a font change; however we calculate them only once
528 // when ellipsizing multiline labels:
529 int replacementWidth
= dc
.GetTextExtent(wxELLIPSE_REPLACEMENT
).GetWidth();
531 // NB: we must handle correctly labels with newlines:
533 for ( wxString::const_iterator pc
= label
.begin(); ; ++pc
)
535 if ( pc
== label
.end() || *pc
== wxS('\n') )
537 curLine
= DoEllipsizeSingleLine(curLine
, dc
, mode
, maxFinalWidth
,
540 // add this (ellipsized) row to the rest of the label
542 if ( pc
== label
.end() )
544 // NOTE: this is the return which always exits the function
553 // we need to remove mnemonics from the label for correct calculations
554 else if ( *pc
== wxS('&') && (flags
& wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS
) )
556 // pc+1 is safe: at worst we'll be at end()
557 wxString::const_iterator next
= pc
+ 1;
558 if ( next
!= label
.end() && *next
== wxS('&') )
559 curLine
+= wxS('&'); // && becomes &
560 //else: remove this ampersand
562 // we need also to expand tabs to properly calc their size
563 else if ( *pc
== wxS('\t') && (flags
& wxELLIPSIZE_FLAGS_EXPAND_TABS
) )
565 // Windows natively expands the TABs to 6 spaces. Do the same:
574 // this return would generate a
575 // warning C4702: unreachable code
576 // with MSVC since the function always exits from inside the loop
582 // ----------------------------------------------------------------------------
584 // ----------------------------------------------------------------------------
588 wxStaticBitmapBase::~wxStaticBitmapBase()
590 // this destructor is required for Darwin
593 wxSize
wxStaticBitmapBase::DoGetBestSize() const
596 wxBitmap bmp
= GetBitmap();
598 best
= wxSize(bmp
.GetWidth(), bmp
.GetHeight());
600 // this is completely arbitrary
601 best
= wxSize(16, 16);
606 #endif // wxUSE_STATBMP
608 #endif // wxUSE_CONTROLS