split wxControl::Ellipsize() in two functions for better readability of the code...
[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 #endif
39
40 const char wxControlNameStr[] = "control";
41
42 // ============================================================================
43 // implementation
44 // ============================================================================
45
46 wxControlBase::~wxControlBase()
47 {
48 // this destructor is required for Darwin
49 }
50
51 bool wxControlBase::Create(wxWindow *parent,
52 wxWindowID id,
53 const wxPoint &pos,
54 const wxSize &size,
55 long style,
56 const wxValidator& wxVALIDATOR_PARAM(validator),
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
69 bool 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
80 wxCHECK_MSG( parent, false, wxT("all controls must have parents") );
81
82 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
83 return false;
84
85 parent->AddChild(this);
86
87 return true;
88 }
89
90 /* static */
91 wxString 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
97 void wxControlBase::Command(wxCommandEvent& event)
98 {
99 (void)GetEventHandler()->ProcessEvent(event);
100 }
101
102 void 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 {
110 case wxClientData_Void:
111 event.SetClientData(GetClientData());
112 break;
113
114 case wxClientData_Object:
115 event.SetClientObject(GetClientObject());
116 break;
117
118 case wxClientData_None:
119 // nothing to do
120 ;
121 }
122 }
123
124 bool wxControlBase::SetFont(const wxFont& font)
125 {
126 InvalidateBestSize();
127 return wxWindow::SetFont(font);
128 }
129
130 // wxControl-specific processing after processing the update event
131 void 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
156 /* static */
157 wxString wxControlBase::RemoveMnemonics(const wxString& str)
158 {
159 return wxStripMenuCodes(str, wxStrip_Mnemonics);
160 }
161
162 /* static */
163 wxString wxControlBase::EscapeMnemonics(const wxString& text)
164 {
165 wxString label(text);
166 label.Replace("&", "&&");
167 return label;
168 }
169
170 /* static */
171 int 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
215 wxBorder wxControlBase::GetDefaultBorder() const
216 {
217 return wxBORDER_THEME;
218 }
219
220 // ----------------------------------------------------------------------------
221 // wxControlBase - ellipsization code
222 // ----------------------------------------------------------------------------
223
224 #define wxELLIPSE_REPLACEMENT wxT("...")
225
226 /* static and protected */
227 wxString 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!)
257
258 // remove characters in excess
259 size_t initialChar, // index of first char to erase
260 nChars; // how many chars do we need to erase?
261
262 switch (mode)
263 {
264 case wxELLIPSIZE_START:
265 initialChar = 0;
266 for (nChars=0;
267 nChars < len && charOffsets[nChars] < excessPixels;
268 nChars++)
269 ;
270 break;
271
272 case wxELLIPSIZE_MIDDLE:
273 {
274 // the start & end of the removed span of chars
275 initialChar = len/2;
276 size_t endChar = len/2;
277
278 int removed = 0;
279 for ( ; removed < excessPixels; )
280 {
281 if (initialChar > 0)
282 {
283 // width of the initialChar-th character
284 int width = charOffsets[initialChar] -
285 charOffsets[initialChar-1];
286
287 // remove the initialChar-th character
288 removed += width;
289 initialChar--;
290 }
291
292 if (endChar < len - 1 &&
293 removed < excessPixels)
294 {
295 // width of the (endChar+1)-th character
296 int width = charOffsets[endChar+1] -
297 charOffsets[endChar];
298
299 // remove the endChar-th character
300 removed += width;
301 endChar++;
302 }
303
304 if (initialChar == 0 && endChar == len-1)
305 {
306 nChars = len+1;
307 break;
308 }
309 }
310
311 initialChar++;
312 nChars = endChar - initialChar + 1;
313 }
314 break;
315
316 case wxELLIPSIZE_END:
317 {
318 wxASSERT(len > 0);
319
320 int maxWidth = totalWidth - excessPixels;
321 for (initialChar=0;
322 initialChar < len &&
323 charOffsets[initialChar] < maxWidth;
324 initialChar++)
325 ;
326
327 if (initialChar == 0)
328 {
329 nChars = len;
330 }
331 else
332 {
333 //initialChar--; // go back one character
334 nChars = len - initialChar;
335 }
336 }
337 break;
338
339 default:
340 wxFAIL_MSG("invalid ellipsize mode");
341 }
342
343 wxString ret(curLine);
344 if (nChars >= len)
345 {
346 // need to remove the entire row!
347 ret.clear();
348 }
349 else
350 {
351 // erase nChars characters after initialChar (included):
352 ret.erase(initialChar, nChars+1);
353
354 // if there is space for the replacement dots, add them
355 if (maxFinalWidth > replacementWidth)
356 ret.append(wxELLIPSE_REPLACEMENT);//.insert(initialChar, wxELLIPSE_REPLACEMENT);
357 }
358
359 // if everything was ok, we should have shortened this line
360 // enough to make it fit in maxFinalWidth:
361 wxASSERT(dc.GetTextExtent(ret).GetWidth() < maxFinalWidth);
362
363 return ret;
364 }
365
366 /* static */
367 wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
368 wxEllipsizeMode mode, int maxFinalWidth,
369 int flags)
370 {
371 wxString ret;
372
373 // these cannot be cached between different Ellipsize() calls as they can
374 // change because of e.g. a font change; however we calculate them only once
375 // when ellipsizing multiline labels:
376 int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
377 int marginWidth = dc.GetCharWidth();
378
379 // NB: we must handle correctly labels with newlines:
380 wxString curLine;
381 for ( wxString::const_iterator pc = label.begin(); ; ++pc )
382 {
383 if ( pc == label.end() || *pc == wxS('\n') )
384 {
385 curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
386 replacementWidth, marginWidth);
387
388 // add this (ellipsized) row to the rest of the label
389 ret << curLine;
390 if ( pc == label.end() )
391 {
392 // NOTE: this is the return which always exits the function
393 return ret;
394 }
395 else
396 {
397 ret << *pc;
398 curLine.clear();
399 }
400 }
401 // we need to remove mnemonics from the label for correct calculations
402 else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_PROCESS_MNEMONICS) != 0 )
403 {
404 // pc+1 is safe: at worst we'll be at end()
405 wxString::const_iterator next = pc + 1;
406 if ( next != label.end() && *next == wxS('&') )
407 curLine += wxS('&'); // && becomes &
408 //else: remove this ampersand
409 }
410 // we need also to expand tabs to properly calc their size
411 else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_EXPAND_TAB) != 0 )
412 {
413 // Windows natively expands the TABs to 6 spaces. Do the same:
414 curLine += wxS(" ");
415 }
416 else
417 {
418 curLine += *pc;
419 }
420 }
421
422 // this return would generate a
423 // warning C4702: unreachable code
424 // with MSVC since the function always exits from inside the loop
425 //return ret;
426 }
427
428
429
430 // ----------------------------------------------------------------------------
431 // wxStaticBitmap
432 // ----------------------------------------------------------------------------
433
434 #if wxUSE_STATBMP
435
436 wxStaticBitmapBase::~wxStaticBitmapBase()
437 {
438 // this destructor is required for Darwin
439 }
440
441 wxSize wxStaticBitmapBase::DoGetBestSize() const
442 {
443 wxSize best;
444 wxBitmap bmp = GetBitmap();
445 if ( bmp.Ok() )
446 best = wxSize(bmp.GetWidth(), bmp.GetHeight());
447 else
448 // this is completely arbitrary
449 best = wxSize(16, 16);
450 CacheBestSize(best);
451 return best;
452 }
453
454 #endif // wxUSE_STATBMP
455
456 #endif // wxUSE_CONTROLS