Add ellipsization support to wxDataViewCtrl.
[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 wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" );
249
250 // NOTE: this function assumes that any mnemonic/tab character has already
251 // been handled if it was necessary to handle them (see Ellipsize())
252
253 if (maxFinalWidth <= 0)
254 return wxEmptyString;
255
256 wxArrayInt charOffsets;
257 size_t len = curLine.length();
258 if (len == 0 ||
259 !dc.GetPartialTextExtents(curLine, charOffsets))
260 return curLine;
261
262 wxASSERT(charOffsets.GetCount() == len);
263
264 size_t totalWidth = charOffsets.Last();
265 if ( totalWidth <= (size_t)maxFinalWidth )
266 return curLine; // we don't need to do any ellipsization!
267
268 int excessPixels = totalWidth - maxFinalWidth +
269 replacementWidth +
270 marginWidth; // security margin (NEEDED!)
271 wxASSERT(excessPixels>0);
272
273 // remove characters in excess
274 size_t initialChar, // index of first char to erase
275 nChars; // how many chars do we need to erase?
276
277 switch (mode)
278 {
279 case wxELLIPSIZE_START:
280 initialChar = 0;
281 for ( nChars=0;
282 nChars < len && charOffsets[nChars] < excessPixels;
283 nChars++ )
284 ;
285 break;
286
287 case wxELLIPSIZE_MIDDLE:
288 {
289 // the start & end of the removed span of chars
290 initialChar = len/2;
291 size_t endChar = len/2;
292
293 int removed = 0;
294 for ( ; removed < excessPixels; )
295 {
296 if (initialChar > 0)
297 {
298 // width of the initialChar-th character
299 int width = charOffsets[initialChar] -
300 charOffsets[initialChar-1];
301
302 // remove the initialChar-th character
303 removed += width;
304 initialChar--;
305 }
306
307 if (endChar < len - 1 &&
308 removed < excessPixels)
309 {
310 // width of the (endChar+1)-th character
311 int width = charOffsets[endChar+1] -
312 charOffsets[endChar];
313
314 // remove the endChar-th character
315 removed += width;
316 endChar++;
317 }
318
319 if (initialChar == 0 && endChar == len-1)
320 {
321 nChars = len+1;
322 break;
323 }
324 }
325
326 initialChar++;
327 nChars = endChar - initialChar + 1;
328 }
329 break;
330
331 case wxELLIPSIZE_END:
332 {
333 wxASSERT(len > 0);
334
335 int maxWidth = totalWidth - excessPixels;
336 for ( initialChar = 0;
337 initialChar < len && charOffsets[initialChar] < maxWidth;
338 initialChar++ )
339 ;
340
341 if (initialChar == 0)
342 {
343 nChars = len;
344 }
345 else
346 {
347 //initialChar--; // go back one character
348 nChars = len - initialChar;
349 }
350 }
351 break;
352
353 case wxELLIPSIZE_NONE:
354 default:
355 wxFAIL_MSG("invalid ellipsize mode");
356 return curLine;
357 }
358
359 wxString ret(curLine);
360 if (nChars >= len)
361 {
362 // need to remove the entire row!
363 ret.clear();
364 }
365 else
366 {
367 // erase nChars characters after initialChar (included):
368 ret.erase(initialChar, nChars+1);
369
370 // if there is space for the replacement dots, add them
371 if (maxFinalWidth > replacementWidth)
372 ret.insert(initialChar, wxELLIPSE_REPLACEMENT);
373 }
374
375 // if everything was ok, we should have shortened this line
376 // enough to make it fit in maxFinalWidth:
377 wxASSERT(dc.GetTextExtent(ret).GetWidth() < maxFinalWidth);
378
379 return ret;
380 }
381
382 /* static */
383 wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
384 wxEllipsizeMode mode, int maxFinalWidth,
385 int flags)
386 {
387 wxString ret;
388
389 // these cannot be cached between different Ellipsize() calls as they can
390 // change because of e.g. a font change; however we calculate them only once
391 // when ellipsizing multiline labels:
392 int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
393 int marginWidth = dc.GetCharWidth();
394
395 // NB: we must handle correctly labels with newlines:
396 wxString curLine;
397 for ( wxString::const_iterator pc = label.begin(); ; ++pc )
398 {
399 if ( pc == label.end() || *pc == wxS('\n') )
400 {
401 curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
402 replacementWidth, marginWidth);
403
404 // add this (ellipsized) row to the rest of the label
405 ret << curLine;
406 if ( pc == label.end() )
407 {
408 // NOTE: this is the return which always exits the function
409 return ret;
410 }
411 else
412 {
413 ret << *pc;
414 curLine.clear();
415 }
416 }
417 // we need to remove mnemonics from the label for correct calculations
418 else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS) )
419 {
420 // pc+1 is safe: at worst we'll be at end()
421 wxString::const_iterator next = pc + 1;
422 if ( next != label.end() && *next == wxS('&') )
423 curLine += wxS('&'); // && becomes &
424 //else: remove this ampersand
425 }
426 // we need also to expand tabs to properly calc their size
427 else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_FLAGS_EXPAND_TABS) )
428 {
429 // Windows natively expands the TABs to 6 spaces. Do the same:
430 curLine += wxS(" ");
431 }
432 else
433 {
434 curLine += *pc;
435 }
436 }
437
438 // this return would generate a
439 // warning C4702: unreachable code
440 // with MSVC since the function always exits from inside the loop
441 //return ret;
442 }
443
444
445
446 // ----------------------------------------------------------------------------
447 // wxStaticBitmap
448 // ----------------------------------------------------------------------------
449
450 #if wxUSE_STATBMP
451
452 wxStaticBitmapBase::~wxStaticBitmapBase()
453 {
454 // this destructor is required for Darwin
455 }
456
457 wxSize wxStaticBitmapBase::DoGetBestSize() const
458 {
459 wxSize best;
460 wxBitmap bmp = GetBitmap();
461 if ( bmp.Ok() )
462 best = wxSize(bmp.GetWidth(), bmp.GetHeight());
463 else
464 // this is completely arbitrary
465 best = wxSize(16, 16);
466 CacheBestSize(best);
467 return best;
468 }
469
470 #endif // wxUSE_STATBMP
471
472 #endif // wxUSE_CONTROLS