]> git.saurik.com Git - wxWidgets.git/blame - src/common/ctrlcmn.cpp
Correct cell alignment computation for too small column sizes.
[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
74639764
VZ
91/* static */
92wxString 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
2d61b48d
VZ
98void wxControlBase::Command(wxCommandEvent& event)
99{
bfbd6dc1 100 (void)GetEventHandler()->ProcessEvent(event);
2d61b48d 101}
bfbd6dc1
VZ
102
103void 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 {
1e6feb95 111 case wxClientData_Void:
bfbd6dc1
VZ
112 event.SetClientData(GetClientData());
113 break;
114
1e6feb95 115 case wxClientData_Object:
bfbd6dc1
VZ
116 event.SetClientObject(GetClientObject());
117 break;
118
1e6feb95 119 case wxClientData_None:
bfbd6dc1
VZ
120 // nothing to do
121 ;
122 }
123}
124
9f884528
RD
125bool wxControlBase::SetFont(const wxFont& font)
126{
127 InvalidateBestSize();
128 return wxWindow::SetFont(font);
129}
130
a3a4105d
VZ
131// wxControl-specific processing after processing the update event
132void 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
39bc0347
VZ
157/* static */
158wxString wxControlBase::RemoveMnemonics(const wxString& str)
159{
160 return wxStripMenuCodes(str, wxStrip_Mnemonics);
161}
162
5b8b2c84
VZ
163/* static */
164wxString wxControlBase::EscapeMnemonics(const wxString& text)
165{
166 wxString label(text);
167 label.Replace("&", "&&");
168 return label;
169}
170
916eabe6
VZ
171/* static */
172int 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
9a83f860 177 static const wxChar MNEMONIC_PREFIX = wxT('&');
916eabe6
VZ
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 {
9a83f860 202 wxFAIL_MSG(wxT("duplicate accel char in control label"));
916eabe6
VZ
203 }
204 }
205 }
206
207 if ( labelOnly )
208 {
209 *labelOnly += *pc;
210 }
211 }
212
213 return indexAccel;
214}
215
a78618b0
FM
216wxBorder wxControlBase::GetDefaultBorder() const
217{
218 return wxBORDER_THEME;
219}
220
0534259a
VZ
221/* static */ wxVisualAttributes
222wxControlBase::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
a78618b0
FM
232// ----------------------------------------------------------------------------
233// wxControlBase - ellipsization code
234// ----------------------------------------------------------------------------
235
5c87527c
FM
236#define wxELLIPSE_REPLACEMENT wxT("...")
237
a78618b0
FM
238/* static and protected */
239wxString 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!)
91d55088 269 wxASSERT(excessPixels>0);
a78618b0
FM
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 {
8d6e8e45
VZ
277 case wxELLIPSIZE_START:
278 initialChar = 0;
279 for ( nChars=0;
280 nChars < len && charOffsets[nChars] < excessPixels;
281 nChars++ )
282 ;
283 break;
a78618b0 284
8d6e8e45 285 case wxELLIPSIZE_MIDDLE:
a78618b0 286 {
8d6e8e45
VZ
287 // the start & end of the removed span of chars
288 initialChar = len/2;
289 size_t endChar = len/2;
a78618b0 290
8d6e8e45
VZ
291 int removed = 0;
292 for ( ; removed < excessPixels; )
a78618b0 293 {
8d6e8e45
VZ
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 }
a78618b0
FM
322 }
323
8d6e8e45
VZ
324 initialChar++;
325 nChars = endChar - initialChar + 1;
a78618b0 326 }
8d6e8e45 327 break;
a78618b0 328
8d6e8e45
VZ
329 case wxELLIPSIZE_END:
330 {
331 wxASSERT(len > 0);
a78618b0 332
8d6e8e45
VZ
333 int maxWidth = totalWidth - excessPixels;
334 for ( initialChar = 0;
335 initialChar < len && charOffsets[initialChar] < maxWidth;
336 initialChar++ )
337 ;
a78618b0 338
8d6e8e45
VZ
339 if (initialChar == 0)
340 {
341 nChars = len;
342 }
343 else
344 {
345 //initialChar--; // go back one character
346 nChars = len - initialChar;
347 }
a78618b0 348 }
8d6e8e45 349 break;
a78618b0 350
8d6e8e45
VZ
351 default:
352 wxFAIL_MSG("invalid ellipsize mode");
353 return curLine;
a78618b0
FM
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)
91d55088 369 ret.insert(initialChar, wxELLIPSE_REPLACEMENT);
a78618b0
FM
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
5c87527c
FM
379/* static */
380wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
a78618b0
FM
381 wxEllipsizeMode mode, int maxFinalWidth,
382 int flags)
5c87527c 383{
5c87527c
FM
384 wxString ret;
385
a78618b0
FM
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:
5c87527c 389 int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth();
a78618b0 390 int marginWidth = dc.GetCharWidth();
5c87527c
FM
391
392 // NB: we must handle correctly labels with newlines:
393 wxString curLine;
5c87527c
FM
394 for ( wxString::const_iterator pc = label.begin(); ; ++pc )
395 {
a78618b0 396 if ( pc == label.end() || *pc == wxS('\n') )
5c87527c 397 {
a78618b0
FM
398 curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
399 replacementWidth, marginWidth);
5c87527c
FM
400
401 // add this (ellipsized) row to the rest of the label
402 ret << curLine;
403 if ( pc == label.end() )
404 {
7d27b626 405 // NOTE: this is the return which always exits the function
5c87527c
FM
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
a78618b0 415 else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_PROCESS_MNEMONICS) != 0 )
5c87527c
FM
416 {
417 // pc+1 is safe: at worst we'll be at end()
418 wxString::const_iterator next = pc + 1;
a78618b0
FM
419 if ( next != label.end() && *next == wxS('&') )
420 curLine += wxS('&'); // && becomes &
5c87527c
FM
421 //else: remove this ampersand
422 }
423 // we need also to expand tabs to properly calc their size
a78618b0 424 else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_EXPAND_TAB) != 0 )
5c87527c
FM
425 {
426 // Windows natively expands the TABs to 6 spaces. Do the same:
a78618b0 427 curLine += wxS(" ");
5c87527c
FM
428 }
429 else
430 {
431 curLine += *pc;
432 }
433 }
434
02b4f9fd 435 // this return would generate a
7d27b626 436 // warning C4702: unreachable code
02b4f9fd 437 // with MSVC since the function always exits from inside the loop
7d27b626 438 //return ret;
5c87527c
FM
439}
440
dc797d8e
JS
441
442
1e6feb95
VZ
443// ----------------------------------------------------------------------------
444// wxStaticBitmap
445// ----------------------------------------------------------------------------
446
447#if wxUSE_STATBMP
448
799ea011
GD
449wxStaticBitmapBase::~wxStaticBitmapBase()
450{
451 // this destructor is required for Darwin
452}
453
b3fcfa4d 454wxSize wxStaticBitmapBase::DoGetBestSize() const
1e6feb95 455{
9f884528 456 wxSize best;
1e6feb95
VZ
457 wxBitmap bmp = GetBitmap();
458 if ( bmp.Ok() )
9f884528
RD
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;
1e6feb95
VZ
465}
466
467#endif // wxUSE_STATBMP
468
469#endif // wxUSE_CONTROLS