Move wxControl::GetCompositeControlsDefaultAttributes() from MSW to common.
[wxWidgets.git] / src / common / ctrlcmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/ctrlcmn.cpp
3 // Purpose: wxControl common interface
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 26.07.99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_CONTROLS
28
29 #include "wx/control.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/dc.h"
33 #include "wx/log.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"
39 #endif
40
41 const char wxControlNameStr[] = "control";
42
43 // ============================================================================
44 // implementation
45 // ============================================================================
46
47 wxControlBase::~wxControlBase()
48 {
49 // this destructor is required for Darwin
50 }
51
52 bool wxControlBase::Create(wxWindow *parent,
53 wxWindowID id,
54 const wxPoint &pos,
55 const wxSize &size,
56 long style,
57 const wxValidator& wxVALIDATOR_PARAM(validator),
58 const wxString &name)
59 {
60 bool ret = wxWindow::Create(parent, id, pos, size, style, name);
61
62 #if wxUSE_VALIDATORS
63 if ( ret )
64 SetValidator(validator);
65 #endif // wxUSE_VALIDATORS
66
67 return ret;
68 }
69
70 bool wxControlBase::CreateControl(wxWindowBase *parent,
71 wxWindowID id,
72 const wxPoint& pos,
73 const wxSize& size,
74 long style,
75 const wxValidator& validator,
76 const wxString& name)
77 {
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
80 // Windows
81 wxCHECK_MSG( parent, false, wxT("all controls must have parents") );
82
83 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
84 return false;
85
86 parent->AddChild(this);
87
88 return true;
89 }
90
91 /* static */
92 wxString wxControlBase::GetLabelText(const wxString& label)
93 {
94 // we don't want strip the TABs here, just the mnemonics
95 return wxStripMenuCodes(label, wxStrip_Mnemonics);
96 }
97
98 void wxControlBase::Command(wxCommandEvent& event)
99 {
100 (void)GetEventHandler()->ProcessEvent(event);
101 }
102
103 void wxControlBase::InitCommandEvent(wxCommandEvent& event) const
104 {
105 event.SetEventObject((wxControlBase *)this); // const_cast
106
107 // event.SetId(GetId()); -- this is usuall done in the event ctor
108
109 switch ( m_clientDataType )
110 {
111 case wxClientData_Void:
112 event.SetClientData(GetClientData());
113 break;
114
115 case wxClientData_Object:
116 event.SetClientObject(GetClientObject());
117 break;
118
119 case wxClientData_None:
120 // nothing to do
121 ;
122 }
123 }
124
125 bool wxControlBase::SetFont(const wxFont& font)
126 {
127 InvalidateBestSize();
128 return wxWindow::SetFont(font);
129 }
130
131 // wxControl-specific processing after processing the update event
132 void wxControlBase::DoUpdateWindowUI(wxUpdateUIEvent& event)
133 {
134 // call inherited
135 wxWindowBase::DoUpdateWindowUI(event);
136
137 // update label
138 if ( event.GetSetText() )
139 {
140 if ( event.GetText() != GetLabel() )
141 SetLabel(event.GetText());
142 }
143
144 // Unfortunately we don't yet have common base class for
145 // wxRadioButton, so we handle updates of radiobuttons here.
146 // TODO: If once wxRadioButtonBase will exist, move this code there.
147 #if wxUSE_RADIOBTN
148 if ( event.GetSetChecked() )
149 {
150 wxRadioButton *radiobtn = wxDynamicCastThis(wxRadioButton);
151 if ( radiobtn )
152 radiobtn->SetValue(event.GetChecked());
153 }
154 #endif // wxUSE_RADIOBTN
155 }
156
157 /* static */
158 wxString wxControlBase::RemoveMnemonics(const wxString& str)
159 {
160 return wxStripMenuCodes(str, wxStrip_Mnemonics);
161 }
162
163 /* static */
164 wxString wxControlBase::EscapeMnemonics(const wxString& text)
165 {
166 wxString label(text);
167 label.Replace("&", "&&");
168 return label;
169 }
170
171 /* static */
172 int wxControlBase::FindAccelIndex(const wxString& label, wxString *labelOnly)
173 {
174 // the character following MNEMONIC_PREFIX is the accelerator for this
175 // control unless it is MNEMONIC_PREFIX too - this allows to insert
176 // literal MNEMONIC_PREFIX chars into the label
177 static const wxChar MNEMONIC_PREFIX = wxT('&');
178
179 if ( labelOnly )
180 {
181 labelOnly->Empty();
182 labelOnly->Alloc(label.length());
183 }
184
185 int indexAccel = -1;
186 for ( wxString::const_iterator pc = label.begin(); pc != label.end(); ++pc )
187 {
188 if ( *pc == MNEMONIC_PREFIX )
189 {
190 ++pc; // skip it
191 if ( pc == label.end() )
192 break;
193 else if ( *pc != MNEMONIC_PREFIX )
194 {
195 if ( indexAccel == -1 )
196 {
197 // remember it (-1 is for MNEMONIC_PREFIX itself
198 indexAccel = pc - label.begin() - 1;
199 }
200 else
201 {
202 wxFAIL_MSG(wxT("duplicate accel char in control label"));
203 }
204 }
205 }
206
207 if ( labelOnly )
208 {
209 *labelOnly += *pc;
210 }
211 }
212
213 return indexAccel;
214 }
215
216 wxBorder wxControlBase::GetDefaultBorder() const
217 {
218 return wxBORDER_THEME;
219 }
220
221 /* static */ wxVisualAttributes
222 wxControlBase::GetCompositeControlsDefaultAttributes(wxWindowVariant WXUNUSED(variant))
223 {
224 wxVisualAttributes attrs;
225 attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
226 attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
227 attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
228
229 return attrs;
230 }
231
232 // ----------------------------------------------------------------------------
233 // wxControlBase - ellipsization code
234 // ----------------------------------------------------------------------------
235
236 #define wxELLIPSE_REPLACEMENT wxT("...")
237
238 /* static and protected */
239 wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
240 wxEllipsizeMode mode, int maxFinalWidth,
241 int replacementWidth, int marginWidth)
242 {
243 wxASSERT_MSG(replacementWidth > 0 && marginWidth > 0,
244 "Invalid parameters");
245 wxASSERT_MSG(!curLine.Contains('\n'),
246 "Use Ellipsize() instead!");
247
248 // NOTE: this function assumes that any mnemonic/tab character has already
249 // been handled if it was necessary to handle them (see Ellipsize())
250
251 if (maxFinalWidth <= 0)
252 return wxEmptyString;
253
254 wxArrayInt charOffsets;
255 size_t len = curLine.length();
256 if (len == 0 ||
257 !dc.GetPartialTextExtents(curLine, charOffsets))
258 return curLine;
259
260 wxASSERT(charOffsets.GetCount() == len);
261
262 size_t totalWidth = charOffsets.Last();
263 if ( totalWidth <= (size_t)maxFinalWidth )
264 return curLine; // we don't need to do any ellipsization!
265
266 int excessPixels = totalWidth - maxFinalWidth +
267 replacementWidth +
268 marginWidth; // security margin (NEEDED!)
269 wxASSERT(excessPixels>0);
270
271 // remove characters in excess
272 size_t initialChar, // index of first char to erase
273 nChars; // how many chars do we need to erase?
274
275 switch (mode)
276 {
277 case wxELLIPSIZE_START:
278 initialChar = 0;
279 for ( nChars=0;
280 nChars < len && charOffsets[nChars] < excessPixels;
281 nChars++ )
282 ;
283 break;
284
285 case wxELLIPSIZE_MIDDLE:
286 {
287 // the start & end of the removed span of chars
288 initialChar = len/2;
289 size_t endChar = len/2;
290
291 int removed = 0;
292 for ( ; removed < excessPixels; )
293 {
294 if (initialChar > 0)
295 {
296 // width of the initialChar-th character
297 int width = charOffsets[initialChar] -
298 charOffsets[initialChar-1];
299
300 // remove the initialChar-th character
301 removed += width;
302 initialChar--;
303 }
304
305 if (endChar < len - 1 &&
306 removed < excessPixels)
307 {
308 // width of the (endChar+1)-th character
309 int width = charOffsets[endChar+1] -
310 charOffsets[endChar];
311
312 // remove the endChar-th character
313 removed += width;
314 endChar++;
315 }
316
317 if (initialChar == 0 && endChar == len-1)
318 {
319 nChars = len+1;
320 break;
321 }
322 }
323
324 initialChar++;
325 nChars = endChar - initialChar + 1;
326 }
327 break;
328
329 case wxELLIPSIZE_END:
330 {
331 wxASSERT(len > 0);
332
333 int maxWidth = totalWidth - excessPixels;
334 for ( initialChar = 0;
335 initialChar < len && charOffsets[initialChar] < maxWidth;
336 initialChar++ )
337 ;
338
339 if (initialChar == 0)
340 {
341 nChars = len;
342 }
343 else
344 {
345 //initialChar--; // go back one character
346 nChars = len - initialChar;
347 }
348 }
349 break;
350
351 default:
352 wxFAIL_MSG("invalid ellipsize mode");
353 return curLine;
354 }
355
356 wxString ret(curLine);
357 if (nChars >= len)
358 {
359 // need to remove the entire row!
360 ret.clear();
361 }
362 else
363 {
364 // erase nChars characters after initialChar (included):
365 ret.erase(initialChar, nChars+1);
366
367 // if there is space for the replacement dots, add them
368 if (maxFinalWidth > replacementWidth)
369 ret.insert(initialChar, wxELLIPSE_REPLACEMENT);
370 }
371
372 // if everything was ok, we should have shortened this line
373 // enough to make it fit in maxFinalWidth:
374 wxASSERT(dc.GetTextExtent(ret).GetWidth() < maxFinalWidth);
375
376 return ret;
377 }
378
379 /* static */
380 wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
381 wxEllipsizeMode mode, int maxFinalWidth,
382 int flags)
383 {
384 wxString ret;
385
386 // these cannot be cached between different Ellipsize() calls as they can
387 // change because of e.g. a font change; however we calculate them only once
388 // when ellipsizing multiline labels:
389 int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
390 int marginWidth = dc.GetCharWidth();
391
392 // NB: we must handle correctly labels with newlines:
393 wxString curLine;
394 for ( wxString::const_iterator pc = label.begin(); ; ++pc )
395 {
396 if ( pc == label.end() || *pc == wxS('\n') )
397 {
398 curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
399 replacementWidth, marginWidth);
400
401 // add this (ellipsized) row to the rest of the label
402 ret << curLine;
403 if ( pc == label.end() )
404 {
405 // NOTE: this is the return which always exits the function
406 return ret;
407 }
408 else
409 {
410 ret << *pc;
411 curLine.clear();
412 }
413 }
414 // we need to remove mnemonics from the label for correct calculations
415 else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_PROCESS_MNEMONICS) != 0 )
416 {
417 // pc+1 is safe: at worst we'll be at end()
418 wxString::const_iterator next = pc + 1;
419 if ( next != label.end() && *next == wxS('&') )
420 curLine += wxS('&'); // && becomes &
421 //else: remove this ampersand
422 }
423 // we need also to expand tabs to properly calc their size
424 else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_EXPAND_TAB) != 0 )
425 {
426 // Windows natively expands the TABs to 6 spaces. Do the same:
427 curLine += wxS(" ");
428 }
429 else
430 {
431 curLine += *pc;
432 }
433 }
434
435 // this return would generate a
436 // warning C4702: unreachable code
437 // with MSVC since the function always exits from inside the loop
438 //return ret;
439 }
440
441
442
443 // ----------------------------------------------------------------------------
444 // wxStaticBitmap
445 // ----------------------------------------------------------------------------
446
447 #if wxUSE_STATBMP
448
449 wxStaticBitmapBase::~wxStaticBitmapBase()
450 {
451 // this destructor is required for Darwin
452 }
453
454 wxSize wxStaticBitmapBase::DoGetBestSize() const
455 {
456 wxSize best;
457 wxBitmap bmp = GetBitmap();
458 if ( bmp.Ok() )
459 best = wxSize(bmp.GetWidth(), bmp.GetHeight());
460 else
461 // this is completely arbitrary
462 best = wxSize(16, 16);
463 CacheBestSize(best);
464 return best;
465 }
466
467 #endif // wxUSE_STATBMP
468
469 #endif // wxUSE_CONTROLS