]> git.saurik.com Git - wxWidgets.git/blame - src/common/dlgcmn.cpp
Do not send a changed event if a button is clicked for down/up when the control is...
[wxWidgets.git] / src / common / dlgcmn.cpp
CommitLineData
e37feda2 1/////////////////////////////////////////////////////////////////////////////
f1e01716 2// Name: src/common/dlgcmn.cpp
e37feda2
VZ
3// Purpose: common (to all ports) wxDialog functions
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 28.06.99
7// RCS-ID: $Id$
8// Copyright: (c) Vadim Zeitlin
65571936 9// Licence: wxWindows licence
e37feda2
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
e37feda2
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
fdf565fe
WS
27#include "wx/dialog.h"
28
e37feda2 29#ifndef WX_PRECOMP
f6bcfd97 30 #include "wx/button.h"
e37feda2 31 #include "wx/dcclient.h"
9f3a38fc 32 #include "wx/intl.h"
e37feda2 33 #include "wx/settings.h"
9f3a38fc 34 #include "wx/stattext.h"
92afa2b1 35 #include "wx/sizer.h"
7d9f12f3 36 #include "wx/containr.h"
e37feda2
VZ
37#endif
38
897b24cf
WS
39#include "wx/statline.h"
40#include "wx/sysopt.h"
41
5d1b4919
VZ
42#if wxUSE_STATTEXT
43
44// ----------------------------------------------------------------------------
45// wxTextWrapper
46// ----------------------------------------------------------------------------
47
48// this class is used to wrap the text on word boundary: wrapping is done by
49// calling OnStartLine() and OnOutputLine() functions
50class wxTextWrapper
51{
52public:
53 wxTextWrapper() { m_eol = false; }
54
55 // win is used for getting the font, text is the text to wrap, width is the
56 // max line width or -1 to disable wrapping
57 void Wrap(wxWindow *win, const wxString& text, int widthMax);
58
0db7dfb0
VZ
59 // we don't need it, but just to avoid compiler warnings
60 virtual ~wxTextWrapper() { }
61
5d1b4919
VZ
62protected:
63 // line may be empty
64 virtual void OnOutputLine(const wxString& line) = 0;
65
66 // called at the start of every new line (except the very first one)
67 virtual void OnNewLine() { }
68
69private:
70 // call OnOutputLine() and set m_eol to true
71 void DoOutputLine(const wxString& line)
72 {
73 OnOutputLine(line);
74
75 m_eol = true;
76 }
77
78 // this function is a destructive inspector: when it returns true it also
79 // resets the flag to false so calling it again woulnd't return true any
80 // more
81 bool IsStartOfNewLine()
82 {
83 if ( !m_eol )
84 return false;
85
86 m_eol = false;
87
88 return true;
89 }
90
91
92 bool m_eol;
93};
94
95#endif // wxUSE_STATTEXT
96
97// ----------------------------------------------------------------------------
92afa2b1 98// wxDialogBase
5d1b4919 99// ----------------------------------------------------------------------------
e37feda2 100
7d9f12f3 101BEGIN_EVENT_TABLE(wxDialogBase, wxTopLevelWindow)
a9f620da 102 EVT_BUTTON(wxID_ANY, wxDialogBase::OnButton)
2158f4d7
VZ
103
104 EVT_CLOSE(wxDialogBase::OnCloseWindow)
105
0be27418 106 EVT_CHAR_HOOK(wxDialogBase::OnCharHook)
2158f4d7 107
7d9f12f3
VS
108 WX_EVENT_TABLE_CONTROL_CONTAINER(wxDialogBase)
109END_EVENT_TABLE()
110
6c20e8f8 111WX_DELEGATE_TO_CONTROL_CONTAINER(wxDialogBase, wxTopLevelWindow)
7d9f12f3
VS
112
113void wxDialogBase::Init()
114{
115 m_returnCode = 0;
9ceeecb9 116 m_affirmativeId = wxID_OK;
c6ece595
VZ
117 m_escapeId = wxID_ANY;
118
e4b713a2
VZ
119 // the dialogs have this flag on by default to prevent the events from the
120 // dialog controls from reaching the parent frame which is usually
121 // undesirable and can lead to unexpected and hard to find bugs
122 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
123
7d9f12f3 124 m_container.SetContainerWindow(this);
7d9f12f3
VS
125}
126
5d1b4919 127#if wxUSE_STATTEXT
1e6feb95 128
5d1b4919 129void wxTextWrapper::Wrap(wxWindow *win, const wxString& text, int widthMax)
e37feda2 130{
5d1b4919
VZ
131 const wxChar *lastSpace = NULL;
132 wxString line;
133
134 const wxChar *lineStart = text.c_str();
135 for ( const wxChar *p = lineStart; ; p++ )
136 {
137 if ( IsStartOfNewLine() )
138 {
139 OnNewLine();
140
141 lastSpace = NULL;
142 line.clear();
143 lineStart = p;
144 }
145
146 if ( *p == _T('\n') || *p == _T('\0') )
147 {
148 DoOutputLine(line);
149
150 if ( *p == _T('\0') )
151 break;
152 }
153 else // not EOL
154 {
155 if ( *p == _T(' ') )
156 lastSpace = p;
157
158 line += *p;
68379eaf 159
5d1b4919
VZ
160 if ( widthMax >= 0 && lastSpace )
161 {
162 int width;
163 win->GetTextExtent(line, &width, NULL);
68379eaf 164
5d1b4919
VZ
165 if ( width > widthMax )
166 {
167 // remove the last word from this line
168 line.erase(lastSpace - lineStart, p + 1 - lineStart);
169 DoOutputLine(line);
170
171 // go back to the last word of this line which we didn't
172 // output yet
173 p = lastSpace;
174 }
175 }
176 //else: no wrapping at all or impossible to wrap
177 }
178 }
179}
180
cfd1ac21
MW
181class wxTextSizerWrapper : public wxTextWrapper
182{
183public:
184 wxTextSizerWrapper(wxWindow *win)
185 {
186 m_win = win;
187 m_hLine = 0;
188 }
189
190 wxSizer *CreateSizer(const wxString& text, int widthMax)
191 {
192 m_sizer = new wxBoxSizer(wxVERTICAL);
193 Wrap(m_win, text, widthMax);
194 return m_sizer;
195 }
196
197protected:
198 virtual void OnOutputLine(const wxString& line)
199 {
200 if ( !line.empty() )
201 {
202 m_sizer->Add(new wxStaticText(m_win, wxID_ANY, line));
203 }
204 else // empty line, no need to create a control for it
205 {
206 if ( !m_hLine )
207 m_hLine = m_win->GetCharHeight();
208
209 m_sizer->Add(5, m_hLine);
210 }
211 }
212
213private:
214 wxWindow *m_win;
215 wxSizer *m_sizer;
216 int m_hLine;
217};
218
5d1b4919
VZ
219wxSizer *wxDialogBase::CreateTextSizer(const wxString& message)
220{
2b5f62a0
VZ
221 // I admit that this is complete bogus, but it makes
222 // message boxes work for pda screens temporarily..
5d1b4919
VZ
223 int widthMax = -1;
224 const bool is_pda = wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA;
2b5f62a0
VZ
225 if (is_pda)
226 {
5d1b4919 227 widthMax = wxSystemSettings::GetMetric( wxSYS_SCREEN_X ) - 25;
2b5f62a0 228 }
68379eaf 229
5d1b4919
VZ
230 // '&' is used as accel mnemonic prefix in the wxWidgets controls but in
231 // the static messages created by CreateTextSizer() (used by wxMessageBox,
232 // for example), we don't want this special meaning, so we need to quote it
233 wxString text(message);
234 text.Replace(_T("&"), _T("&&"));
68379eaf 235
cfd1ac21 236 wxTextSizerWrapper wrapper(this);
b730516c 237
cfd1ac21
MW
238 return wrapper.CreateSizer(text, widthMax);
239}
5d1b4919 240
cfd1ac21
MW
241class wxLabelWrapper : public wxTextWrapper
242{
243public:
244 void WrapLabel(wxWindow *text, int widthMax)
245 {
246 m_text.clear();
247 Wrap(text, text->GetLabel(), widthMax);
248 text->SetLabel(m_text);
249 }
a350a488 250
cfd1ac21
MW
251protected:
252 virtual void OnOutputLine(const wxString& line)
253 {
254 m_text += line;
255 }
a350a488 256
cfd1ac21
MW
257 virtual void OnNewLine()
258 {
259 m_text += _T('\n');
260 }
68379eaf 261
cfd1ac21
MW
262private:
263 wxString m_text;
264};
68379eaf 265
f20e3df6
VZ
266// NB: don't "factor out" the scope operator, SGI MIPSpro 7.3 (but not 7.4)
267// gets confused if it doesn't immediately follow the class name
dec48aa5 268void
a351409e 269#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
3f8e6923 270wxStaticText::
dec48aa5 271#else
3f8e6923 272wxStaticTextBase::
dec48aa5 273#endif
3f8e6923 274Wrap(int width)
5d1b4919 275{
cfd1ac21 276 wxLabelWrapper wrapper;
5d1b4919 277 wrapper.WrapLabel(this, width);
e37feda2 278}
b730516c 279
5d1b4919 280#endif // wxUSE_STATTEXT
1e6feb95 281
25eb10d2 282wxSizer *wxDialogBase::CreateButtonSizer(long flags)
e37feda2 283{
25eb10d2 284 wxSizer *sizer = NULL;
897b24cf 285
25eb10d2 286#ifdef __SMARTPHONE__
102c0454 287 wxDialog* dialog = (wxDialog*) this;
25eb10d2 288 if ( flags & wxOK )
102c0454 289 dialog->SetLeftMenu(wxID_OK);
102c0454 290
25eb10d2 291 if ( flags & wxCANCEL )
102c0454 292 dialog->SetRightMenu(wxID_CANCEL);
102c0454 293
25eb10d2 294 if ( flags & wxYES )
102c0454 295 dialog->SetLeftMenu(wxID_YES);
897b24cf 296
25eb10d2
VZ
297 if ( flags & wxNO )
298 dialog->SetRightMenu(wxID_NO);
897b24cf
WS
299#else // !__SMARTPHONE__
300
25eb10d2
VZ
301#if wxUSE_BUTTON
302
897b24cf 303#ifdef __POCKETPC__
25eb10d2
VZ
304 // PocketPC guidelines recommend for Ok/Cancel dialogs to use OK button
305 // located inside caption bar and implement Cancel functionality through
306 // Undo outside dialog. As native behaviour this will be default here but
307 // can be replaced with real wxButtons by setting the option below to 1
308 if ( (flags & ~(wxCANCEL|wxNO_DEFAULT)) != wxOK ||
309 wxSystemOptions::GetOptionInt(wxT("wince.dialog.real-ok-cancel")) )
310#endif // __POCKETPC__
897b24cf 311 {
25eb10d2 312 sizer = CreateStdDialogButtonSizer(flags);
897b24cf 313 }
25eb10d2 314#endif // wxUSE_BUTTON
897b24cf 315
25eb10d2 316#endif // __SMARTPHONE__/!__SMARTPHONE__
897b24cf 317
25eb10d2
VZ
318 return sizer;
319}
897b24cf 320
25eb10d2
VZ
321wxSizer *wxDialogBase::CreateSeparatedButtonSizer(long flags)
322{
323 wxSizer *sizer = CreateButtonSizer(flags);
324 if ( !sizer )
325 return NULL;
897b24cf 326
25eb10d2
VZ
327 // Mac Human Interface Guidelines recommend not to use static lines as
328 // grouping elements
329#if wxUSE_STATLINE && !defined(__WXMAC__)
330 wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
331 topsizer->Add(new wxStaticLine(this),
332 wxSizerFlags().Expand().DoubleBorder(wxBOTTOM));
333 topsizer->Add(sizer, wxSizerFlags().Expand());
334 sizer = topsizer;
335#endif // wxUSE_STATLINE
897b24cf 336
897b24cf 337 return sizer;
acf2ac37 338}
68379eaf 339
897b24cf
WS
340#if wxUSE_BUTTON
341
acf2ac37
RR
342wxStdDialogButtonSizer *wxDialogBase::CreateStdDialogButtonSizer( long flags )
343{
344 wxStdDialogButtonSizer *sizer = new wxStdDialogButtonSizer();
897b24cf 345
acf2ac37 346 wxButton *ok = NULL;
acf2ac37
RR
347 wxButton *yes = NULL;
348 wxButton *no = NULL;
52069700 349
25eb10d2
VZ
350 if (flags & wxOK)
351 {
331c1816 352 ok = new wxButton(this, wxID_OK);
52069700 353 sizer->AddButton(ok);
2b5f62a0 354 }
52069700 355
25eb10d2
VZ
356 if (flags & wxCANCEL)
357 {
331c1816 358 wxButton *cancel = new wxButton(this, wxID_CANCEL);
52069700 359 sizer->AddButton(cancel);
b5b49e42 360 }
52069700 361
25eb10d2
VZ
362 if (flags & wxYES)
363 {
331c1816 364 yes = new wxButton(this, wxID_YES);
52069700 365 sizer->AddButton(yes);
e37feda2 366 }
52069700 367
25eb10d2
VZ
368 if (flags & wxNO)
369 {
331c1816 370 no = new wxButton(this, wxID_NO);
52069700 371 sizer->AddButton(no);
e37feda2 372 }
52069700 373
25eb10d2
VZ
374 if (flags & wxHELP)
375 {
331c1816 376 wxButton *help = new wxButton(this, wxID_HELP);
52069700 377 sizer->AddButton(help);
e37feda2 378 }
52069700 379
b730516c
RD
380 if (flags & wxNO_DEFAULT)
381 {
382 if (no)
383 {
384 no->SetDefault();
385 no->SetFocus();
386 }
387 }
388 else
92afa2b1
RR
389 {
390 if (ok)
391 {
392 ok->SetDefault();
393 ok->SetFocus();
394 }
395 else if (yes)
396 {
397 yes->SetDefault();
398 yes->SetFocus();
399 }
400 }
d30814cd 401
9ceeecb9
JS
402 if (flags & wxOK)
403 SetAffirmativeId(wxID_OK);
404 else if (flags & wxYES)
405 SetAffirmativeId(wxID_YES);
b730516c 406
d30814cd
MB
407 sizer->Realize();
408
acf2ac37 409 return sizer;
e37feda2
VZ
410}
411
1e6feb95 412#endif // wxUSE_BUTTON
0be27418 413
551f281b 414// ----------------------------------------------------------------------------
a9f620da 415// standard buttons handling
551f281b
VZ
416// ----------------------------------------------------------------------------
417
a9f620da
VZ
418void wxDialogBase::EndDialog(int rc)
419{
420 if ( IsModal() )
421 EndModal(rc);
422 else
423 Hide();
424}
425
551f281b
VZ
426void wxDialogBase::AcceptAndClose()
427{
428 if ( Validate() && TransferDataFromWindow() )
429 {
a9f620da 430 EndDialog(m_affirmativeId);
551f281b
VZ
431 }
432}
433
434void wxDialogBase::SetAffirmativeId(int affirmativeId)
435{
fabd7a7f 436 m_affirmativeId = affirmativeId;
551f281b
VZ
437}
438
439void wxDialogBase::SetEscapeId(int escapeId)
440{
fabd7a7f 441 m_escapeId = escapeId;
551f281b
VZ
442}
443
0be27418
VZ
444bool wxDialogBase::EmulateButtonClickIfPresent(int id)
445{
dbfa7f33 446#if wxUSE_BUTTON
0be27418
VZ
447 wxButton *btn = wxDynamicCast(FindWindow(id), wxButton);
448
449 if ( !btn || !btn->IsEnabled() || !btn->IsShown() )
450 return false;
451
452 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, id);
453 event.SetEventObject(btn);
454 btn->GetEventHandler()->ProcessEvent(event);
455
456 return true;
dbfa7f33
VS
457#else // !wxUSE_BUTTON
458 wxUnusedVar(id);
459 return false;
460#endif // wxUSE_BUTTON/!wxUSE_BUTTON
0be27418
VZ
461}
462
463bool wxDialogBase::IsEscapeKey(const wxKeyEvent& event)
464{
465 // for most platforms, Esc key is used to close the dialogs
466 return event.GetKeyCode() == WXK_ESCAPE &&
467 event.GetModifiers() == wxMOD_NONE;
468}
469
470void wxDialogBase::OnCharHook(wxKeyEvent& event)
471{
472 if ( event.GetKeyCode() == WXK_ESCAPE )
473 {
474 int idCancel = GetEscapeId();
475 switch ( idCancel )
476 {
477 case wxID_NONE:
478 // don't handle Esc specially at all
479 break;
480
481 case wxID_ANY:
482 // this value is special: it means translate Esc to wxID_CANCEL
483 // but if there is no such button, then fall back to wxID_OK
484 if ( EmulateButtonClickIfPresent(wxID_CANCEL) )
485 return;
153a0b78 486 idCancel = GetAffirmativeId();
0be27418
VZ
487 // fall through
488
489 default:
490 // translate Esc to button press for the button with given id
491 if ( EmulateButtonClickIfPresent(idCancel) )
492 return;
493 }
494 }
495
496 event.Skip();
497}
498
a9f620da 499void wxDialogBase::OnButton(wxCommandEvent& event)
2158f4d7 500{
a9f620da
VZ
501 const int id = event.GetId();
502 if ( id == GetAffirmativeId() )
503 {
504 AcceptAndClose();
505 }
506 else if ( id == wxID_APPLY )
507 {
508 if ( Validate() )
509 TransferDataFromWindow();
2158f4d7 510
a9f620da
VZ
511 // TODO: disable the Apply button until things change again
512 }
513 else if ( id == GetEscapeId() ||
514 (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) )
515 {
516 EndDialog(wxID_CANCEL);
517 }
518 else // not a standard button
519 {
520 event.Skip();
521 }
2158f4d7
VZ
522}
523
a9f620da
VZ
524// ----------------------------------------------------------------------------
525// other event handlers
526// ----------------------------------------------------------------------------
2158f4d7
VZ
527
528void wxDialogBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
529{
530 // We'll send a Cancel message by default, which may close the dialog.
531 // Check for looping if the Cancel event handler calls Close().
532
533 // Note that if a cancel button and handler aren't present in the dialog,
534 // nothing will happen when you close the dialog via the window manager, or
535 // via Close(). We wouldn't want to destroy the dialog by default, since
536 // the dialog may have been created on the stack. However, this does mean
537 // that calling dialog->Close() won't delete the dialog unless the handler
538 // for wxID_CANCEL does so. So use Destroy() if you want to be sure to
539 // destroy the dialog. The default OnCancel (above) simply ends a modal
540 // dialog, and hides a modeless dialog.
541
542 // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global
543 // lists here? don't dare to change it now, but should be done later!
544 static wxList closing;
545
546 if ( closing.Member(this) )
547 return;
548
549 closing.Append(this);
550
551 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
552 cancelEvent.SetEventObject( this );
553 GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog
554
555 closing.DeleteObject(this);
556}
557
558void wxDialogBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
559{
560 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
561 Refresh();
562}