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 const char wxControlNameStr
[] = "control";
43 // ============================================================================
45 // ============================================================================
47 wxControlBase::~wxControlBase()
49 // this destructor is required for Darwin
52 bool wxControlBase::Create(wxWindow
*parent
,
57 const wxValidator
& wxVALIDATOR_PARAM(validator
),
60 bool ret
= wxWindow::Create(parent
, id
, pos
, size
, style
, name
);
64 SetValidator(validator
);
65 #endif // wxUSE_VALIDATORS
70 bool wxControlBase::CreateControl(wxWindowBase
*parent
,
75 const wxValidator
& validator
,
78 // even if it's possible to create controls without parents in some port,
79 // it should surely be discouraged because it doesn't work at all under
81 wxCHECK_MSG( parent
, false, wxT("all controls must have parents") );
83 if ( !CreateBase(parent
, id
, pos
, size
, style
, validator
, name
) )
86 parent
->AddChild(this);
91 void wxControlBase::Command(wxCommandEvent
& event
)
93 (void)GetEventHandler()->ProcessEvent(event
);
96 void wxControlBase::InitCommandEvent(wxCommandEvent
& event
) const
98 event
.SetEventObject((wxControlBase
*)this); // const_cast
100 // event.SetId(GetId()); -- this is usuall done in the event ctor
102 switch ( m_clientDataType
)
104 case wxClientData_Void
:
105 event
.SetClientData(GetClientData());
108 case wxClientData_Object
:
109 event
.SetClientObject(GetClientObject());
112 case wxClientData_None
:
118 bool wxControlBase::SetFont(const wxFont
& font
)
120 InvalidateBestSize();
121 return wxWindow::SetFont(font
);
124 // wxControl-specific processing after processing the update event
125 void wxControlBase::DoUpdateWindowUI(wxUpdateUIEvent
& event
)
128 wxWindowBase::DoUpdateWindowUI(event
);
131 if ( event
.GetSetText() )
133 if ( event
.GetText() != GetLabel() )
134 SetLabel(event
.GetText());
137 // Unfortunately we don't yet have common base class for
138 // wxRadioButton, so we handle updates of radiobuttons here.
139 // TODO: If once wxRadioButtonBase will exist, move this code there.
141 if ( event
.GetSetChecked() )
143 wxRadioButton
*radiobtn
= wxDynamicCastThis(wxRadioButton
);
145 radiobtn
->SetValue(event
.GetChecked());
147 #endif // wxUSE_RADIOBTN
151 wxString
wxControlBase::GetLabelText(const wxString
& label
)
153 // we don't want strip the TABs here, just the mnemonics
154 return wxStripMenuCodes(label
, wxStrip_Mnemonics
);
158 wxString
wxControlBase::RemoveMnemonics(const wxString
& str
)
160 // we don't want strip the TABs here, just the mnemonics
161 return wxStripMenuCodes(str
, wxStrip_Mnemonics
);
165 wxString
wxControlBase::EscapeMnemonics(const wxString
& text
)
167 wxString
label(text
);
168 label
.Replace("&", "&&");
173 int wxControlBase::FindAccelIndex(const wxString
& label
, wxString
*labelOnly
)
175 // the character following MNEMONIC_PREFIX is the accelerator for this
176 // control unless it is MNEMONIC_PREFIX too - this allows to insert
177 // literal MNEMONIC_PREFIX chars into the label
178 static const wxChar MNEMONIC_PREFIX
= wxT('&');
183 labelOnly
->Alloc(label
.length());
187 for ( wxString::const_iterator pc
= label
.begin(); pc
!= label
.end(); ++pc
)
189 if ( *pc
== MNEMONIC_PREFIX
)
192 if ( pc
== label
.end() )
194 else if ( *pc
!= MNEMONIC_PREFIX
)
196 if ( indexAccel
== -1 )
198 // remember it (-1 is for MNEMONIC_PREFIX itself
199 indexAccel
= pc
- label
.begin() - 1;
203 wxFAIL_MSG(wxT("duplicate accel char in control label"));
217 wxBorder
wxControlBase::GetDefaultBorder() const
219 return wxBORDER_THEME
;
222 /* static */ wxVisualAttributes
223 wxControlBase::GetCompositeControlsDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
225 wxVisualAttributes attrs
;
226 attrs
.font
= wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
);
227 attrs
.colFg
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
);
228 attrs
.colBg
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
);
233 // ----------------------------------------------------------------------------
234 // wxControlBase - ellipsization code
235 // ----------------------------------------------------------------------------
237 #define wxELLIPSE_REPLACEMENT wxS("...")
239 /* static and protected */
240 wxString
wxControlBase::DoEllipsizeSingleLine(const wxString
& curLine
, const wxDC
& dc
,
241 wxEllipsizeMode mode
, int maxFinalWidthPx
,
242 int replacementWidthPx
, int marginWidthPx
)
244 wxASSERT_MSG(replacementWidthPx
> 0 && marginWidthPx
> 0,
245 "Invalid parameters");
246 wxASSERT_LEVEL_2_MSG(!curLine
.Contains('\n'),
247 "Use Ellipsize() instead!");
249 wxASSERT_MSG( mode
!= wxELLIPSIZE_NONE
, "shouldn't be called at all then" );
251 // NOTE: this function assumes that any mnemonic/tab character has already
252 // been handled if it was necessary to handle them (see Ellipsize())
254 if (maxFinalWidthPx
<= 0)
255 return wxEmptyString
;
257 wxArrayInt charOffsetsPx
;
258 size_t len
= curLine
.length();
260 !dc
.GetPartialTextExtents(curLine
, charOffsetsPx
))
263 wxASSERT(charOffsetsPx
.GetCount() == len
);
265 // NOTE: charOffsetsPx[n] is the width in pixels of the first n characters (with the last one INCLUDED)
266 // thus charOffsetsPx[len-1] is the total width of the string
267 size_t totalWidthPx
= charOffsetsPx
.Last();
268 if ( totalWidthPx
<= (size_t)maxFinalWidthPx
)
269 return curLine
; // we don't need to do any ellipsization!
271 int excessPx
= wxMin(totalWidthPx
- maxFinalWidthPx
+
273 marginWidthPx
, // security margin
275 wxASSERT(excessPx
>0); // excessPx should be in the [1;totalWidthPx] range
277 // REMEMBER: indexes inside the string have a valid range of [0;len-1] if not otherwise constrained
278 // lengths/counts of characters (e.g. nCharsToRemove) have a valid range of [0;len] if not otherwise constrained
279 // NOTE: since this point we know we have for sure a non-empty string from which we need
280 // to remove _at least_ one character (thus nCharsToRemove below is constrained to be >= 1)
282 size_t initialCharToRemove
, // index of first character to erase, valid range is [0;len-1]
283 nCharsToRemove
; // how many chars do we need to erase? valid range is [1;len-initialCharToRemove]
285 // let's compute the range of characters to remove depending on the ellipsization mode:
288 case wxELLIPSIZE_START
:
289 initialCharToRemove
= 0;
290 for ( nCharsToRemove
= 1;
291 nCharsToRemove
< len
&& charOffsetsPx
[nCharsToRemove
-1] < excessPx
;
296 case wxELLIPSIZE_MIDDLE
:
298 // NOTE: the following piece of code works also when len == 1
300 // start the removal process from the middle of the string
301 // i.e. separe the string in three parts:
302 // - the first one to preserve, valid range [0;initialCharToRemove-1] or the empty range if initialCharToRemove==0
303 // - the second one to remove, valid range [initialCharToRemove;endCharToRemove]
304 // - the third one to preserve, valid range [endCharToRemove+1;len-1] or the empty range if endCharToRemove==len-1
305 // NOTE: empty range != range [0;0] since the range [0;0] contains 1 character (the zero-th one)!
306 initialCharToRemove
= len
/2;
307 size_t endCharToRemove
= len
/2; // index of the last character to remove; valid range is [0;len-1]
310 for ( ; removedPx
< excessPx
; )
312 // try to remove the last character of the first part of the string
313 if (initialCharToRemove
> 0)
315 // width of the (initialCharToRemove-1)-th character
317 if (initialCharToRemove
>= 2)
318 widthPx
= charOffsetsPx
[initialCharToRemove
-1] - charOffsetsPx
[initialCharToRemove
-2];
320 widthPx
= charOffsetsPx
[initialCharToRemove
-1];
321 // the (initialCharToRemove-1)-th character is the first char of the string
323 wxASSERT(widthPx
>= 0); // widthPx is zero for e.g. tab characters
325 // mark the (initialCharToRemove-1)-th character as removable
326 initialCharToRemove
--;
327 removedPx
+= widthPx
;
330 // try to remove the first character of the last part of the string
331 if (endCharToRemove
< len
- 1 &&
332 removedPx
< excessPx
)
334 // width of the (endCharToRemove+1)-th character
335 int widthPx
= charOffsetsPx
[endCharToRemove
+1] -
336 charOffsetsPx
[endCharToRemove
];
338 wxASSERT(widthPx
>= 0); // widthPx is zero for e.g. tab characters
340 // mark the (endCharToRemove+1)-th character as removable
342 removedPx
+= widthPx
;
345 if (initialCharToRemove
== 0 && endCharToRemove
== len
-1)
347 // we need to remove all the characters of the string!
352 nCharsToRemove
= endCharToRemove
- initialCharToRemove
+ 1;
356 case wxELLIPSIZE_END
:
358 int maxWidthPx
= totalWidthPx
- excessPx
;
360 // go backward from the end of the string toward the start
361 for ( initialCharToRemove
= len
-1;
362 initialCharToRemove
> 0 && charOffsetsPx
[initialCharToRemove
-1] > maxWidthPx
;
363 initialCharToRemove
-- )
365 nCharsToRemove
= len
- initialCharToRemove
;
369 case wxELLIPSIZE_NONE
:
371 wxFAIL_MSG("invalid ellipsize mode");
376 #pragma message disable unscomzer
377 // suppress warnings on comparison of unsigned numbers
379 wxASSERT(initialCharToRemove
>= 0 && initialCharToRemove
<= len
-1); // see valid range for initialCharToRemove above
381 #pragma message enable unscomzer
382 // suppress warnings on comparison of unsigned numbers
384 wxASSERT(nCharsToRemove
>= 1 && nCharsToRemove
<= len
-initialCharToRemove
); // see valid range for nCharsToRemove above
386 // erase nCharsToRemove characters after initialCharToRemove (included);
387 // e.g. if we have the string "foobar" (len = 6)
389 // \--- initialCharToRemove = 2
390 // and nCharsToRemove = 2, then we get "foar"
391 wxString
ret(curLine
);
392 ret
.erase(initialCharToRemove
, nCharsToRemove
);
395 if (initialCharToRemove
>= 1)
396 removedPx
= charOffsetsPx
[initialCharToRemove
+nCharsToRemove
-1] - charOffsetsPx
[initialCharToRemove
-1];
398 removedPx
= charOffsetsPx
[initialCharToRemove
+nCharsToRemove
-1];
399 wxASSERT(removedPx
>= excessPx
);
401 // if there is space for the replacement dots, add them
402 if ((int)totalWidthPx
-removedPx
+replacementWidthPx
< maxFinalWidthPx
)
403 ret
.insert(initialCharToRemove
, wxELLIPSE_REPLACEMENT
);
405 // if everything was ok, we should have shortened this line
406 // enough to make it fit in maxFinalWidthPx:
407 wxASSERT_LEVEL_2(dc
.GetTextExtent(ret
).GetWidth() <= maxFinalWidthPx
);
413 wxString
wxControlBase::Ellipsize(const wxString
& label
, const wxDC
& dc
,
414 wxEllipsizeMode mode
, int maxFinalWidth
,
419 // these cannot be cached between different Ellipsize() calls as they can
420 // change because of e.g. a font change; however we calculate them only once
421 // when ellipsizing multiline labels:
422 int replacementWidth
= dc
.GetTextExtent(wxELLIPSE_REPLACEMENT
).GetWidth();
423 int marginWidth
= dc
.GetCharWidth();
425 // NB: we must handle correctly labels with newlines:
427 for ( wxString::const_iterator pc
= label
.begin(); ; ++pc
)
429 if ( pc
== label
.end() || *pc
== wxS('\n') )
431 curLine
= DoEllipsizeSingleLine(curLine
, dc
, mode
, maxFinalWidth
,
432 replacementWidth
, marginWidth
);
434 // add this (ellipsized) row to the rest of the label
436 if ( pc
== label
.end() )
438 // NOTE: this is the return which always exits the function
447 // we need to remove mnemonics from the label for correct calculations
448 else if ( *pc
== wxS('&') && (flags
& wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS
) )
450 // pc+1 is safe: at worst we'll be at end()
451 wxString::const_iterator next
= pc
+ 1;
452 if ( next
!= label
.end() && *next
== wxS('&') )
453 curLine
+= wxS('&'); // && becomes &
454 //else: remove this ampersand
456 // we need also to expand tabs to properly calc their size
457 else if ( *pc
== wxS('\t') && (flags
& wxELLIPSIZE_FLAGS_EXPAND_TABS
) )
459 // Windows natively expands the TABs to 6 spaces. Do the same:
468 // this return would generate a
469 // warning C4702: unreachable code
470 // with MSVC since the function always exits from inside the loop
476 // ----------------------------------------------------------------------------
478 // ----------------------------------------------------------------------------
482 wxStaticBitmapBase::~wxStaticBitmapBase()
484 // this destructor is required for Darwin
487 wxSize
wxStaticBitmapBase::DoGetBestSize() const
490 wxBitmap bmp
= GetBitmap();
492 best
= wxSize(bmp
.GetWidth(), bmp
.GetHeight());
494 // this is completely arbitrary
495 best
= wxSize(16, 16);
500 #endif // wxUSE_STATBMP
502 #endif // wxUSE_CONTROLS