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