]> git.saurik.com Git - wxWidgets.git/blame - src/common/ctrlcmn.cpp
Fix wxWrapSizer minimal size calculation.
[wxWidgets.git] / src / common / ctrlcmn.cpp
CommitLineData
2d61b48d 1/////////////////////////////////////////////////////////////////////////////
0e2d2986 2// Name: src/common/ctrlcmn.cpp
2d61b48d
VZ
3// Purpose: wxControl common interface
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 26.07.99
7// RCS-ID: $Id$
77ffb593 8// Copyright: (c) wxWidgets team
65571936 9// Licence: wxWindows licence
2d61b48d
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2d61b48d
VZ
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
1e6feb95
VZ
27#if wxUSE_CONTROLS
28
93fbbe07
WS
29#include "wx/control.h"
30
2d61b48d 31#ifndef WX_PRECOMP
02b4f9fd 32 #include "wx/dc.h"
2d61b48d 33 #include "wx/log.h"
0e2d2986 34 #include "wx/radiobut.h"
e267406e 35 #include "wx/statbmp.h"
1e6feb95 36 #include "wx/bitmap.h"
74639764 37 #include "wx/utils.h" // for wxStripMenuCodes()
0534259a 38 #include "wx/settings.h"
0bca0373 39#endif
1e6feb95 40
f36e602b 41const char wxControlNameStr[] = "control";
e7445ff8 42
2d61b48d
VZ
43// ============================================================================
44// implementation
45// ============================================================================
46
799ea011
GD
47wxControlBase::~wxControlBase()
48{
49 // this destructor is required for Darwin
50}
51
1e6feb95
VZ
52bool wxControlBase::Create(wxWindow *parent,
53 wxWindowID id,
54 const wxPoint &pos,
55 const wxSize &size,
56 long style,
ac8d0c11 57 const wxValidator& wxVALIDATOR_PARAM(validator),
1e6feb95
VZ
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
2d61b48d
VZ
70bool 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
c9d59ee7 81 wxCHECK_MSG( parent, false, wxT("all controls must have parents") );
2d61b48d
VZ
82
83 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
c9d59ee7 84 return false;
2d61b48d
VZ
85
86 parent->AddChild(this);
87
c9d59ee7 88 return true;
2d61b48d
VZ
89}
90
2d61b48d
VZ
91void wxControlBase::Command(wxCommandEvent& event)
92{
bfbd6dc1 93 (void)GetEventHandler()->ProcessEvent(event);
2d61b48d 94}
bfbd6dc1
VZ
95
96void wxControlBase::InitCommandEvent(wxCommandEvent& event) const
97{
f48a1159 98 event.SetEventObject(const_cast<wxControlBase *>(this));
bfbd6dc1
VZ
99
100 // event.SetId(GetId()); -- this is usuall done in the event ctor
101
102 switch ( m_clientDataType )
103 {
1e6feb95 104 case wxClientData_Void:
bfbd6dc1
VZ
105 event.SetClientData(GetClientData());
106 break;
107
1e6feb95 108 case wxClientData_Object:
bfbd6dc1
VZ
109 event.SetClientObject(GetClientObject());
110 break;
111
1e6feb95 112 case wxClientData_None:
bfbd6dc1
VZ
113 // nothing to do
114 ;
115 }
116}
117
9f884528
RD
118bool wxControlBase::SetFont(const wxFont& font)
119{
120 InvalidateBestSize();
121 return wxWindow::SetFont(font);
122}
123
a3a4105d
VZ
124// wxControl-specific processing after processing the update event
125void wxControlBase::DoUpdateWindowUI(wxUpdateUIEvent& event)
126{
127 // call inherited
128 wxWindowBase::DoUpdateWindowUI(event);
129
130 // update label
131 if ( event.GetSetText() )
132 {
133 if ( event.GetText() != GetLabel() )
134 SetLabel(event.GetText());
135 }
136
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.
140#if wxUSE_RADIOBTN
141 if ( event.GetSetChecked() )
142 {
143 wxRadioButton *radiobtn = wxDynamicCastThis(wxRadioButton);
144 if ( radiobtn )
145 radiobtn->SetValue(event.GetChecked());
146 }
147#endif // wxUSE_RADIOBTN
148}
149
32ee98eb
FM
150/* static */
151wxString wxControlBase::GetLabelText(const wxString& label)
152{
153 // we don't want strip the TABs here, just the mnemonics
154 return wxStripMenuCodes(label, wxStrip_Mnemonics);
155}
156
39bc0347
VZ
157/* static */
158wxString wxControlBase::RemoveMnemonics(const wxString& str)
159{
32ee98eb 160 // we don't want strip the TABs here, just the mnemonics
39bc0347
VZ
161 return wxStripMenuCodes(str, wxStrip_Mnemonics);
162}
163
5b8b2c84
VZ
164/* static */
165wxString wxControlBase::EscapeMnemonics(const wxString& text)
166{
167 wxString label(text);
168 label.Replace("&", "&&");
169 return label;
170}
171
916eabe6
VZ
172/* static */
173int wxControlBase::FindAccelIndex(const wxString& label, wxString *labelOnly)
174{
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
9a83f860 178 static const wxChar MNEMONIC_PREFIX = wxT('&');
916eabe6
VZ
179
180 if ( labelOnly )
181 {
182 labelOnly->Empty();
183 labelOnly->Alloc(label.length());
184 }
185
186 int indexAccel = -1;
187 for ( wxString::const_iterator pc = label.begin(); pc != label.end(); ++pc )
188 {
189 if ( *pc == MNEMONIC_PREFIX )
190 {
191 ++pc; // skip it
192 if ( pc == label.end() )
193 break;
194 else if ( *pc != MNEMONIC_PREFIX )
195 {
196 if ( indexAccel == -1 )
197 {
198 // remember it (-1 is for MNEMONIC_PREFIX itself
199 indexAccel = pc - label.begin() - 1;
200 }
201 else
202 {
9a83f860 203 wxFAIL_MSG(wxT("duplicate accel char in control label"));
916eabe6
VZ
204 }
205 }
206 }
207
208 if ( labelOnly )
209 {
210 *labelOnly += *pc;
211 }
212 }
213
214 return indexAccel;
215}
216
a78618b0
FM
217wxBorder wxControlBase::GetDefaultBorder() const
218{
219 return wxBORDER_THEME;
220}
221
0534259a
VZ
222/* static */ wxVisualAttributes
223wxControlBase::GetCompositeControlsDefaultAttributes(wxWindowVariant WXUNUSED(variant))
224{
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);
229
230 return attrs;
231}
232
a78618b0
FM
233// ----------------------------------------------------------------------------
234// wxControlBase - ellipsization code
235// ----------------------------------------------------------------------------
236
8ab11e46 237#define wxELLIPSE_REPLACEMENT wxS("...")
5c87527c 238
a78618b0
FM
239/* static and protected */
240wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
8ab11e46
FM
241 wxEllipsizeMode mode, int maxFinalWidthPx,
242 int replacementWidthPx, int marginWidthPx)
a78618b0 243{
8ab11e46 244 wxASSERT_MSG(replacementWidthPx > 0 && marginWidthPx > 0,
a78618b0 245 "Invalid parameters");
a37da0fa
FM
246 wxASSERT_LEVEL_2_MSG(!curLine.Contains('\n'),
247 "Use Ellipsize() instead!");
a78618b0 248
c937bcac
VZ
249 wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" );
250
a78618b0
FM
251 // NOTE: this function assumes that any mnemonic/tab character has already
252 // been handled if it was necessary to handle them (see Ellipsize())
253
8ab11e46 254 if (maxFinalWidthPx <= 0)
a78618b0
FM
255 return wxEmptyString;
256
8ab11e46 257 wxArrayInt charOffsetsPx;
a78618b0
FM
258 size_t len = curLine.length();
259 if (len == 0 ||
8ab11e46 260 !dc.GetPartialTextExtents(curLine, charOffsetsPx))
a78618b0
FM
261 return curLine;
262
8ab11e46 263 wxASSERT(charOffsetsPx.GetCount() == len);
a78618b0 264
a37da0fa
FM
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
8ab11e46
FM
267 size_t totalWidthPx = charOffsetsPx.Last();
268 if ( totalWidthPx <= (size_t)maxFinalWidthPx )
a78618b0
FM
269 return curLine; // we don't need to do any ellipsization!
270
a37da0fa
FM
271 int excessPx = wxMin(totalWidthPx - maxFinalWidthPx +
272 replacementWidthPx +
273 marginWidthPx, // security margin
274 totalWidthPx);
275 wxASSERT(excessPx>0); // excessPx should be in the [1;totalWidthPx] range
a78618b0 276
a37da0fa
FM
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)
a78618b0 281
a37da0fa
FM
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]
284
285 // let's compute the range of characters to remove depending on the ellipsization mode:
a78618b0
FM
286 switch (mode)
287 {
8d6e8e45 288 case wxELLIPSIZE_START:
8ab11e46 289 initialCharToRemove = 0;
a37da0fa
FM
290 for ( nCharsToRemove = 1;
291 nCharsToRemove < len && charOffsetsPx[nCharsToRemove-1] < excessPx;
8ab11e46 292 nCharsToRemove++ )
8d6e8e45
VZ
293 ;
294 break;
a78618b0 295
8d6e8e45 296 case wxELLIPSIZE_MIDDLE:
a78618b0 297 {
a37da0fa
FM
298 // NOTE: the following piece of code works also when len == 1
299
300 // start the removal process from the middle of the string
ce00f59b 301 // i.e. separe the string in three parts:
a37da0fa
FM
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)!
4d3de2c5
VS
306 initialCharToRemove = len/2; // index of the last character to remove; valid range is [0;len-1]
307 size_t endCharToRemove = initialCharToRemove - 1; // initial removal range is empty
a78618b0 308
8ab11e46 309 int removedPx = 0;
3433de6e 310 bool removeFromStart = true;
8ab11e46 311 for ( ; removedPx < excessPx; )
a78618b0 312 {
3433de6e
VS
313 const bool canRemoveFromStart = initialCharToRemove > 0;
314 const bool canRemoveFromEnd = endCharToRemove < len - 1;
315
316 if ( !canRemoveFromStart && !canRemoveFromEnd )
317 {
318 // we need to remove all the characters of the string!
319 break;
320 }
321
322 // Remove from the beginning in even steps and from the end
323 // in odd steps, unless we exhausted one side already:
324 removeFromStart = !removeFromStart;
325 if ( removeFromStart && !canRemoveFromStart )
326 removeFromStart = false;
327 else if ( !removeFromStart && !canRemoveFromEnd )
328 removeFromStart = true;
329
330 if ( removeFromStart )
8d6e8e45 331 {
3433de6e
VS
332 // try to remove the last character of the first part of the string
333
a37da0fa
FM
334 // width of the (initialCharToRemove-1)-th character
335 int widthPx;
336 if (initialCharToRemove >= 2)
337 widthPx = charOffsetsPx[initialCharToRemove-1] - charOffsetsPx[initialCharToRemove-2];
338 else
ce00f59b 339 widthPx = charOffsetsPx[initialCharToRemove-1];
a37da0fa 340 // the (initialCharToRemove-1)-th character is the first char of the string
ce00f59b 341
a37da0fa
FM
342 wxASSERT(widthPx >= 0); // widthPx is zero for e.g. tab characters
343
344 // mark the (initialCharToRemove-1)-th character as removable
8ab11e46 345 initialCharToRemove--;
a37da0fa 346 removedPx += widthPx;
8d6e8e45 347
3433de6e
VS
348 continue; // don't remove anything else
349 }
350 else // !removeFromStart
8d6e8e45 351 {
3433de6e
VS
352 // try to remove the first character of the last part of the string
353
a37da0fa
FM
354 // width of the (endCharToRemove+1)-th character
355 int widthPx = charOffsetsPx[endCharToRemove+1] -
356 charOffsetsPx[endCharToRemove];
357
358 wxASSERT(widthPx >= 0); // widthPx is zero for e.g. tab characters
8d6e8e45 359
a37da0fa
FM
360 // mark the (endCharToRemove+1)-th character as removable
361 endCharToRemove++;
8ab11e46 362 removedPx += widthPx;
8d6e8e45 363
3433de6e 364 continue; // don't remove anything else
8d6e8e45 365 }
a78618b0
FM
366 }
367
a37da0fa 368 nCharsToRemove = endCharToRemove - initialCharToRemove + 1;
a78618b0 369 }
8d6e8e45 370 break;
a78618b0 371
8d6e8e45
VZ
372 case wxELLIPSIZE_END:
373 {
8ab11e46 374 int maxWidthPx = totalWidthPx - excessPx;
a78618b0 375
a37da0fa
FM
376 // go backward from the end of the string toward the start
377 for ( initialCharToRemove = len-1;
378 initialCharToRemove > 0 && charOffsetsPx[initialCharToRemove-1] > maxWidthPx;
379 initialCharToRemove-- )
380 ;
381 nCharsToRemove = len - initialCharToRemove;
a78618b0 382 }
8d6e8e45 383 break;
a78618b0 384
c937bcac 385 case wxELLIPSIZE_NONE:
8d6e8e45
VZ
386 default:
387 wxFAIL_MSG("invalid ellipsize mode");
388 return curLine;
a78618b0
FM
389 }
390
5eed8556 391 wxASSERT(initialCharToRemove <= len-1); // see valid range for initialCharToRemove above
a37da0fa
FM
392 wxASSERT(nCharsToRemove >= 1 && nCharsToRemove <= len-initialCharToRemove); // see valid range for nCharsToRemove above
393
394 // erase nCharsToRemove characters after initialCharToRemove (included);
395 // e.g. if we have the string "foobar" (len = 6)
396 // ^
397 // \--- initialCharToRemove = 2
398 // and nCharsToRemove = 2, then we get "foar"
a78618b0 399 wxString ret(curLine);
a37da0fa 400 ret.erase(initialCharToRemove, nCharsToRemove);
a78618b0 401
a37da0fa
FM
402 int removedPx;
403 if (initialCharToRemove >= 1)
404 removedPx = charOffsetsPx[initialCharToRemove+nCharsToRemove-1] - charOffsetsPx[initialCharToRemove-1];
ce00f59b 405 else
a37da0fa
FM
406 removedPx = charOffsetsPx[initialCharToRemove+nCharsToRemove-1];
407 wxASSERT(removedPx >= excessPx);
408
409 // if there is space for the replacement dots, add them
410 if ((int)totalWidthPx-removedPx+replacementWidthPx < maxFinalWidthPx)
411 ret.insert(initialCharToRemove, wxELLIPSE_REPLACEMENT);
a78618b0
FM
412
413 // if everything was ok, we should have shortened this line
8ab11e46 414 // enough to make it fit in maxFinalWidthPx:
a37da0fa 415 wxASSERT_LEVEL_2(dc.GetTextExtent(ret).GetWidth() <= maxFinalWidthPx);
a78618b0
FM
416
417 return ret;
418}
419
5c87527c
FM
420/* static */
421wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
a78618b0
FM
422 wxEllipsizeMode mode, int maxFinalWidth,
423 int flags)
5c87527c 424{
5c87527c
FM
425 wxString ret;
426
a78618b0
FM
427 // these cannot be cached between different Ellipsize() calls as they can
428 // change because of e.g. a font change; however we calculate them only once
429 // when ellipsizing multiline labels:
5c87527c 430 int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
a78618b0 431 int marginWidth = dc.GetCharWidth();
5c87527c
FM
432
433 // NB: we must handle correctly labels with newlines:
434 wxString curLine;
5c87527c
FM
435 for ( wxString::const_iterator pc = label.begin(); ; ++pc )
436 {
a78618b0 437 if ( pc == label.end() || *pc == wxS('\n') )
5c87527c 438 {
a78618b0
FM
439 curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
440 replacementWidth, marginWidth);
5c87527c
FM
441
442 // add this (ellipsized) row to the rest of the label
443 ret << curLine;
444 if ( pc == label.end() )
445 {
7d27b626 446 // NOTE: this is the return which always exits the function
5c87527c
FM
447 return ret;
448 }
449 else
450 {
451 ret << *pc;
452 curLine.clear();
453 }
454 }
455 // we need to remove mnemonics from the label for correct calculations
355ce7ad 456 else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS) )
5c87527c
FM
457 {
458 // pc+1 is safe: at worst we'll be at end()
459 wxString::const_iterator next = pc + 1;
a78618b0
FM
460 if ( next != label.end() && *next == wxS('&') )
461 curLine += wxS('&'); // && becomes &
5c87527c
FM
462 //else: remove this ampersand
463 }
464 // we need also to expand tabs to properly calc their size
355ce7ad 465 else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_FLAGS_EXPAND_TABS) )
5c87527c
FM
466 {
467 // Windows natively expands the TABs to 6 spaces. Do the same:
a78618b0 468 curLine += wxS(" ");
5c87527c
FM
469 }
470 else
471 {
472 curLine += *pc;
473 }
474 }
475
02b4f9fd 476 // this return would generate a
7d27b626 477 // warning C4702: unreachable code
02b4f9fd 478 // with MSVC since the function always exits from inside the loop
7d27b626 479 //return ret;
5c87527c
FM
480}
481
dc797d8e
JS
482
483
1e6feb95
VZ
484// ----------------------------------------------------------------------------
485// wxStaticBitmap
486// ----------------------------------------------------------------------------
487
488#if wxUSE_STATBMP
489
799ea011
GD
490wxStaticBitmapBase::~wxStaticBitmapBase()
491{
492 // this destructor is required for Darwin
493}
494
b3fcfa4d 495wxSize wxStaticBitmapBase::DoGetBestSize() const
1e6feb95 496{
9f884528 497 wxSize best;
1e6feb95
VZ
498 wxBitmap bmp = GetBitmap();
499 if ( bmp.Ok() )
9f884528
RD
500 best = wxSize(bmp.GetWidth(), bmp.GetHeight());
501 else
502 // this is completely arbitrary
503 best = wxSize(16, 16);
504 CacheBestSize(best);
505 return best;
1e6feb95
VZ
506}
507
508#endif // wxUSE_STATBMP
509
510#endif // wxUSE_CONTROLS