]> git.saurik.com Git - wxWidgets.git/blob - src/common/utilscmn.cpp
Allow non-modal windows shown from modal dialogs to work in wxOSX.
[wxWidgets.git] / src / common / utilscmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/utilscmn.cpp
3 // Purpose: Miscellaneous utility functions and classes
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 29/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Julian Smart
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 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/string.h"
30 #include "wx/utils.h"
31 #include "wx/intl.h"
32 #include "wx/log.h"
33
34 #if wxUSE_GUI
35 #include "wx/window.h"
36 #include "wx/frame.h"
37 #include "wx/menu.h"
38 #include "wx/msgdlg.h"
39 #include "wx/textdlg.h"
40 #include "wx/textctrl.h" // for wxTE_PASSWORD
41 #if wxUSE_ACCEL
42 #include "wx/menuitem.h"
43 #include "wx/accel.h"
44 #endif // wxUSE_ACCEL
45 #endif // wxUSE_GUI
46 #endif // WX_PRECOMP
47
48 #include "wx/apptrait.h"
49
50 #include "wx/process.h"
51 #include "wx/txtstrm.h"
52 #include "wx/uri.h"
53 #include "wx/mimetype.h"
54 #include "wx/config.h"
55
56 #if defined(__WXWINCE__) && wxUSE_DATETIME
57 #include "wx/datetime.h"
58 #endif
59
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64
65 #if !wxONLY_WATCOM_EARLIER_THAN(1,4)
66 #if !(defined(_MSC_VER) && (_MSC_VER > 800))
67 #include <errno.h>
68 #endif
69 #endif
70
71 #if wxUSE_GUI
72 #include "wx/colordlg.h"
73 #include "wx/fontdlg.h"
74 #include "wx/notebook.h"
75 #include "wx/statusbr.h"
76 #endif // wxUSE_GUI
77
78 #ifndef __WXPALMOS5__
79 #ifndef __WXWINCE__
80 #include <time.h>
81 #else
82 #include "wx/msw/wince/time.h"
83 #endif
84 #endif // ! __WXPALMOS5__
85
86 #ifdef __WXMAC__
87 #include "wx/osx/private.h"
88 #endif
89
90 #ifndef __WXPALMOS5__
91 #if !defined(__MWERKS__) && !defined(__WXWINCE__)
92 #include <sys/types.h>
93 #include <sys/stat.h>
94 #endif
95 #endif // ! __WXPALMOS5__
96
97 #if defined(__WXMSW__)
98 #include "wx/msw/private.h"
99 #include "wx/filesys.h"
100 #endif
101
102 #if wxUSE_GUI && defined(__WXGTK__)
103 #include <gtk/gtk.h> // for GTK_XXX_VERSION constants
104 #endif
105
106 #if wxUSE_BASE
107
108 // ============================================================================
109 // implementation
110 // ============================================================================
111
112 // Array used in DecToHex conversion routine.
113 static const wxChar hexArray[] = wxT("0123456789ABCDEF");
114
115 // Convert 2-digit hex number to decimal
116 int wxHexToDec(const wxString& buf)
117 {
118 int firstDigit, secondDigit;
119
120 if (buf.GetChar(0) >= wxT('A'))
121 firstDigit = buf.GetChar(0) - wxT('A') + 10;
122 else
123 firstDigit = buf.GetChar(0) - wxT('0');
124
125 if (buf.GetChar(1) >= wxT('A'))
126 secondDigit = buf.GetChar(1) - wxT('A') + 10;
127 else
128 secondDigit = buf.GetChar(1) - wxT('0');
129
130 return (firstDigit & 0xF) * 16 + (secondDigit & 0xF );
131 }
132
133 // Convert decimal integer to 2-character hex string
134 void wxDecToHex(int dec, wxChar *buf)
135 {
136 int firstDigit = (int)(dec/16.0);
137 int secondDigit = (int)(dec - (firstDigit*16.0));
138 buf[0] = hexArray[firstDigit];
139 buf[1] = hexArray[secondDigit];
140 buf[2] = 0;
141 }
142
143 // Convert decimal integer to 2 characters
144 void wxDecToHex(int dec, char* ch1, char* ch2)
145 {
146 int firstDigit = (int)(dec/16.0);
147 int secondDigit = (int)(dec - (firstDigit*16.0));
148 (*ch1) = (char) hexArray[firstDigit];
149 (*ch2) = (char) hexArray[secondDigit];
150 }
151
152 // Convert decimal integer to 2-character hex string
153 wxString wxDecToHex(int dec)
154 {
155 wxChar buf[3];
156 wxDecToHex(dec, buf);
157 return wxString(buf);
158 }
159
160 // ----------------------------------------------------------------------------
161 // misc functions
162 // ----------------------------------------------------------------------------
163
164 // Return the current date/time
165 wxString wxNow()
166 {
167 #ifdef __WXWINCE__
168 #if wxUSE_DATETIME
169 wxDateTime now = wxDateTime::Now();
170 return now.Format();
171 #else
172 return wxEmptyString;
173 #endif
174 #else
175 time_t now = time(NULL);
176 char *date = ctime(&now);
177 date[24] = '\0';
178 return wxString::FromAscii(date);
179 #endif
180 }
181
182 #if WXWIN_COMPATIBILITY_2_8
183 void wxUsleep(unsigned long milliseconds)
184 {
185 wxMilliSleep(milliseconds);
186 }
187 #endif
188
189 const wxChar *wxGetInstallPrefix()
190 {
191 wxString prefix;
192
193 if ( wxGetEnv(wxT("WXPREFIX"), &prefix) )
194 return prefix.c_str();
195
196 #ifdef wxINSTALL_PREFIX
197 return wxT(wxINSTALL_PREFIX);
198 #else
199 return wxEmptyString;
200 #endif
201 }
202
203 wxString wxGetDataDir()
204 {
205 wxString dir = wxGetInstallPrefix();
206 dir << wxFILE_SEP_PATH << wxT("share") << wxFILE_SEP_PATH << wxT("wx");
207 return dir;
208 }
209
210 bool wxIsPlatformLittleEndian()
211 {
212 // Are we little or big endian? This method is from Harbison & Steele.
213 union
214 {
215 long l;
216 char c[sizeof(long)];
217 } u;
218 u.l = 1;
219
220 return u.c[0] == 1;
221 }
222
223
224 // ----------------------------------------------------------------------------
225 // wxPlatform
226 // ----------------------------------------------------------------------------
227
228 /*
229 * Class to make it easier to specify platform-dependent values
230 */
231
232 wxArrayInt* wxPlatform::sm_customPlatforms = NULL;
233
234 void wxPlatform::Copy(const wxPlatform& platform)
235 {
236 m_longValue = platform.m_longValue;
237 m_doubleValue = platform.m_doubleValue;
238 m_stringValue = platform.m_stringValue;
239 }
240
241 wxPlatform wxPlatform::If(int platform, long value)
242 {
243 if (Is(platform))
244 return wxPlatform(value);
245 else
246 return wxPlatform();
247 }
248
249 wxPlatform wxPlatform::IfNot(int platform, long value)
250 {
251 if (!Is(platform))
252 return wxPlatform(value);
253 else
254 return wxPlatform();
255 }
256
257 wxPlatform& wxPlatform::ElseIf(int platform, long value)
258 {
259 if (Is(platform))
260 m_longValue = value;
261 return *this;
262 }
263
264 wxPlatform& wxPlatform::ElseIfNot(int platform, long value)
265 {
266 if (!Is(platform))
267 m_longValue = value;
268 return *this;
269 }
270
271 wxPlatform wxPlatform::If(int platform, double value)
272 {
273 if (Is(platform))
274 return wxPlatform(value);
275 else
276 return wxPlatform();
277 }
278
279 wxPlatform wxPlatform::IfNot(int platform, double value)
280 {
281 if (!Is(platform))
282 return wxPlatform(value);
283 else
284 return wxPlatform();
285 }
286
287 wxPlatform& wxPlatform::ElseIf(int platform, double value)
288 {
289 if (Is(platform))
290 m_doubleValue = value;
291 return *this;
292 }
293
294 wxPlatform& wxPlatform::ElseIfNot(int platform, double value)
295 {
296 if (!Is(platform))
297 m_doubleValue = value;
298 return *this;
299 }
300
301 wxPlatform wxPlatform::If(int platform, const wxString& value)
302 {
303 if (Is(platform))
304 return wxPlatform(value);
305 else
306 return wxPlatform();
307 }
308
309 wxPlatform wxPlatform::IfNot(int platform, const wxString& value)
310 {
311 if (!Is(platform))
312 return wxPlatform(value);
313 else
314 return wxPlatform();
315 }
316
317 wxPlatform& wxPlatform::ElseIf(int platform, const wxString& value)
318 {
319 if (Is(platform))
320 m_stringValue = value;
321 return *this;
322 }
323
324 wxPlatform& wxPlatform::ElseIfNot(int platform, const wxString& value)
325 {
326 if (!Is(platform))
327 m_stringValue = value;
328 return *this;
329 }
330
331 wxPlatform& wxPlatform::Else(long value)
332 {
333 m_longValue = value;
334 return *this;
335 }
336
337 wxPlatform& wxPlatform::Else(double value)
338 {
339 m_doubleValue = value;
340 return *this;
341 }
342
343 wxPlatform& wxPlatform::Else(const wxString& value)
344 {
345 m_stringValue = value;
346 return *this;
347 }
348
349 void wxPlatform::AddPlatform(int platform)
350 {
351 if (!sm_customPlatforms)
352 sm_customPlatforms = new wxArrayInt;
353 sm_customPlatforms->Add(platform);
354 }
355
356 void wxPlatform::ClearPlatforms()
357 {
358 wxDELETE(sm_customPlatforms);
359 }
360
361 /// Function for testing current platform
362
363 bool wxPlatform::Is(int platform)
364 {
365 #ifdef __WXMSW__
366 if (platform == wxOS_WINDOWS)
367 return true;
368 #endif
369 #ifdef __WXWINCE__
370 if (platform == wxOS_WINDOWS_CE)
371 return true;
372 #endif
373
374 #if 0
375
376 // FIXME: wxWinPocketPC and wxWinSmartPhone are unknown symbols
377
378 #if defined(__WXWINCE__) && defined(__POCKETPC__)
379 if (platform == wxWinPocketPC)
380 return true;
381 #endif
382 #if defined(__WXWINCE__) && defined(__SMARTPHONE__)
383 if (platform == wxWinSmartPhone)
384 return true;
385 #endif
386
387 #endif
388
389 #ifdef __WXGTK__
390 if (platform == wxPORT_GTK)
391 return true;
392 #endif
393 #ifdef __WXMAC__
394 if (platform == wxPORT_MAC)
395 return true;
396 #endif
397 #ifdef __WXX11__
398 if (platform == wxPORT_X11)
399 return true;
400 #endif
401 #ifdef __UNIX__
402 if (platform == wxOS_UNIX)
403 return true;
404 #endif
405 #ifdef __WXMGL__
406 if (platform == wxPORT_MGL)
407 return true;
408 #endif
409 #ifdef __OS2__
410 if (platform == wxOS_OS2)
411 return true;
412 #endif
413 #ifdef __WXPM__
414 if (platform == wxPORT_PM)
415 return true;
416 #endif
417 #ifdef __WXCOCOA__
418 if (platform == wxPORT_MAC)
419 return true;
420 #endif
421
422 if (sm_customPlatforms && sm_customPlatforms->Index(platform) != wxNOT_FOUND)
423 return true;
424
425 return false;
426 }
427
428 // ----------------------------------------------------------------------------
429 // network and user id functions
430 // ----------------------------------------------------------------------------
431
432 // Get Full RFC822 style email address
433 bool wxGetEmailAddress(wxChar *address, int maxSize)
434 {
435 wxString email = wxGetEmailAddress();
436 if ( !email )
437 return false;
438
439 wxStrlcpy(address, email.t_str(), maxSize);
440
441 return true;
442 }
443
444 wxString wxGetEmailAddress()
445 {
446 wxString email;
447
448 wxString host = wxGetFullHostName();
449 if ( !host.empty() )
450 {
451 wxString user = wxGetUserId();
452 if ( !user.empty() )
453 {
454 email << user << wxT('@') << host;
455 }
456 }
457
458 return email;
459 }
460
461 wxString wxGetUserId()
462 {
463 static const int maxLoginLen = 256; // FIXME arbitrary number
464
465 wxString buf;
466 bool ok = wxGetUserId(wxStringBuffer(buf, maxLoginLen), maxLoginLen);
467
468 if ( !ok )
469 buf.Empty();
470
471 return buf;
472 }
473
474 wxString wxGetUserName()
475 {
476 static const int maxUserNameLen = 1024; // FIXME arbitrary number
477
478 wxString buf;
479 bool ok = wxGetUserName(wxStringBuffer(buf, maxUserNameLen), maxUserNameLen);
480
481 if ( !ok )
482 buf.Empty();
483
484 return buf;
485 }
486
487 wxString wxGetHostName()
488 {
489 static const size_t hostnameSize = 257;
490
491 wxString buf;
492 bool ok = wxGetHostName(wxStringBuffer(buf, hostnameSize), hostnameSize);
493
494 if ( !ok )
495 buf.Empty();
496
497 return buf;
498 }
499
500 wxString wxGetFullHostName()
501 {
502 static const size_t hostnameSize = 257;
503
504 wxString buf;
505 bool ok = wxGetFullHostName(wxStringBuffer(buf, hostnameSize), hostnameSize);
506
507 if ( !ok )
508 buf.Empty();
509
510 return buf;
511 }
512
513 wxString wxGetHomeDir()
514 {
515 wxString home;
516 wxGetHomeDir(&home);
517
518 return home;
519 }
520
521 #if 0
522
523 wxString wxGetCurrentDir()
524 {
525 wxString dir;
526 size_t len = 1024;
527 bool ok;
528 do
529 {
530 ok = getcwd(dir.GetWriteBuf(len + 1), len) != NULL;
531 dir.UngetWriteBuf();
532
533 if ( !ok )
534 {
535 if ( errno != ERANGE )
536 {
537 wxLogSysError(wxT("Failed to get current directory"));
538
539 return wxEmptyString;
540 }
541 else
542 {
543 // buffer was too small, retry with a larger one
544 len *= 2;
545 }
546 }
547 //else: ok
548 } while ( !ok );
549
550 return dir;
551 }
552
553 #endif // 0
554
555 // ----------------------------------------------------------------------------
556 // wxExecute
557 // ----------------------------------------------------------------------------
558
559 // wxDoExecuteWithCapture() helper: reads an entire stream into one array
560 //
561 // returns true if ok, false if error
562 #if wxUSE_STREAMS
563 static bool ReadAll(wxInputStream *is, wxArrayString& output)
564 {
565 wxCHECK_MSG( is, false, wxT("NULL stream in wxExecute()?") );
566
567 // the stream could be already at EOF or in wxSTREAM_BROKEN_PIPE state
568 is->Reset();
569
570 wxTextInputStream tis(*is);
571
572 for ( ;; )
573 {
574 wxString line = tis.ReadLine();
575
576 // check for EOF before other errors as it's not really an error
577 if ( is->Eof() )
578 {
579 // add the last, possibly incomplete, line
580 if ( !line.empty() )
581 output.Add(line);
582 break;
583 }
584
585 // any other error is fatal
586 if ( !*is )
587 return false;
588
589 output.Add(line);
590 }
591
592 return true;
593 }
594 #endif // wxUSE_STREAMS
595
596 // this is a private function because it hasn't a clean interface: the first
597 // array is passed by reference, the second by pointer - instead we have 2
598 // public versions of wxExecute() below
599 static long wxDoExecuteWithCapture(const wxString& command,
600 wxArrayString& output,
601 wxArrayString* error,
602 int flags)
603 {
604 // create a wxProcess which will capture the output
605 wxProcess *process = new wxProcess;
606 process->Redirect();
607
608 long rc = wxExecute(command, wxEXEC_SYNC | flags, process);
609
610 #if wxUSE_STREAMS
611 if ( rc != -1 )
612 {
613 if ( !ReadAll(process->GetInputStream(), output) )
614 rc = -1;
615
616 if ( error )
617 {
618 if ( !ReadAll(process->GetErrorStream(), *error) )
619 rc = -1;
620 }
621
622 }
623 #else
624 wxUnusedVar(output);
625 wxUnusedVar(error);
626 #endif // wxUSE_STREAMS/!wxUSE_STREAMS
627
628 delete process;
629
630 return rc;
631 }
632
633 long wxExecute(const wxString& command, wxArrayString& output, int flags)
634 {
635 return wxDoExecuteWithCapture(command, output, NULL, flags);
636 }
637
638 long wxExecute(const wxString& command,
639 wxArrayString& output,
640 wxArrayString& error,
641 int flags)
642 {
643 return wxDoExecuteWithCapture(command, output, &error, flags);
644 }
645
646 // ----------------------------------------------------------------------------
647 // Id functions
648 // ----------------------------------------------------------------------------
649
650 // Id generation
651 static long wxCurrentId = 100;
652
653 long wxNewId()
654 {
655 // skip the part of IDs space that contains hard-coded values:
656 if (wxCurrentId == wxID_LOWEST)
657 wxCurrentId = wxID_HIGHEST + 1;
658
659 return wxCurrentId++;
660 }
661
662 long
663 wxGetCurrentId(void) { return wxCurrentId; }
664
665 void
666 wxRegisterId (long id)
667 {
668 if (id >= wxCurrentId)
669 wxCurrentId = id + 1;
670 }
671
672 // ----------------------------------------------------------------------------
673 // wxQsort, adapted by RR to allow user_data
674 // ----------------------------------------------------------------------------
675
676 /* This file is part of the GNU C Library.
677 Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
678
679 Douglas Schmidt kindly gave permission to relicence the
680 code under the wxWindows licence:
681
682 From: "Douglas C. Schmidt" <schmidt@dre.vanderbilt.edu>
683 To: Robert Roebling <robert.roebling@uni-ulm.de>
684 Subject: Re: qsort licence
685 Date: Mon, 23 Jul 2007 03:44:25 -0500
686 Sender: schmidt@dre.vanderbilt.edu
687 Message-Id: <20070723084426.64F511000A8@tango.dre.vanderbilt.edu>
688
689 Hi Robert,
690
691 > [...] I'm asking if you'd be willing to relicence your code
692 > under the wxWindows licence. [...]
693
694 That's fine with me [...]
695
696 Thanks,
697
698 Doug */
699
700
701 /* Byte-wise swap two items of size SIZE. */
702 #define SWAP(a, b, size) \
703 do \
704 { \
705 register size_t __size = (size); \
706 register char *__a = (a), *__b = (b); \
707 do \
708 { \
709 char __tmp = *__a; \
710 *__a++ = *__b; \
711 *__b++ = __tmp; \
712 } while (--__size > 0); \
713 } while (0)
714
715 /* Discontinue quicksort algorithm when partition gets below this size.
716 This particular magic number was chosen to work best on a Sun 4/260. */
717 #define MAX_THRESH 4
718
719 /* Stack node declarations used to store unfulfilled partition obligations. */
720 typedef struct
721 {
722 char *lo;
723 char *hi;
724 } stack_node;
725
726 /* The next 4 #defines implement a very fast in-line stack abstraction. */
727 #define STACK_SIZE (8 * sizeof(unsigned long int))
728 #define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
729 #define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
730 #define STACK_NOT_EMPTY (stack < top)
731
732
733 /* Order size using quicksort. This implementation incorporates
734 four optimizations discussed in Sedgewick:
735
736 1. Non-recursive, using an explicit stack of pointer that store the
737 next array partition to sort. To save time, this maximum amount
738 of space required to store an array of MAX_INT is allocated on the
739 stack. Assuming a 32-bit integer, this needs only 32 *
740 sizeof(stack_node) == 136 bits. Pretty cheap, actually.
741
742 2. Chose the pivot element using a median-of-three decision tree.
743 This reduces the probability of selecting a bad pivot value and
744 eliminates certain extraneous comparisons.
745
746 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
747 insertion sort to order the MAX_THRESH items within each partition.
748 This is a big win, since insertion sort is faster for small, mostly
749 sorted array segments.
750
751 4. The larger of the two sub-partitions is always pushed onto the
752 stack first, with the algorithm then concentrating on the
753 smaller partition. This *guarantees* no more than log (n)
754 stack size is needed (actually O(1) in this case)! */
755
756 void wxQsort(void *const pbase, size_t total_elems,
757 size_t size, CMPFUNCDATA cmp, const void* user_data)
758 {
759 register char *base_ptr = (char *) pbase;
760 const size_t max_thresh = MAX_THRESH * size;
761
762 if (total_elems == 0)
763 /* Avoid lossage with unsigned arithmetic below. */
764 return;
765
766 if (total_elems > MAX_THRESH)
767 {
768 char *lo = base_ptr;
769 char *hi = &lo[size * (total_elems - 1)];
770 stack_node stack[STACK_SIZE];
771 stack_node *top = stack;
772
773 PUSH (NULL, NULL);
774
775 while (STACK_NOT_EMPTY)
776 {
777 char *left_ptr;
778 char *right_ptr;
779
780 /* Select median value from among LO, MID, and HI. Rearrange
781 LO and HI so the three values are sorted. This lowers the
782 probability of picking a pathological pivot value and
783 skips a comparison for both the LEFT_PTR and RIGHT_PTR. */
784
785 char *mid = lo + size * ((hi - lo) / size >> 1);
786
787 if ((*cmp) ((void *) mid, (void *) lo, user_data) < 0)
788 SWAP (mid, lo, size);
789 if ((*cmp) ((void *) hi, (void *) mid, user_data) < 0)
790 SWAP (mid, hi, size);
791 else
792 goto jump_over;
793 if ((*cmp) ((void *) mid, (void *) lo, user_data) < 0)
794 SWAP (mid, lo, size);
795 jump_over:;
796 left_ptr = lo + size;
797 right_ptr = hi - size;
798
799 /* Here's the famous ``collapse the walls'' section of quicksort.
800 Gotta like those tight inner loops! They are the main reason
801 that this algorithm runs much faster than others. */
802 do
803 {
804 while ((*cmp) ((void *) left_ptr, (void *) mid, user_data) < 0)
805 left_ptr += size;
806
807 while ((*cmp) ((void *) mid, (void *) right_ptr, user_data) < 0)
808 right_ptr -= size;
809
810 if (left_ptr < right_ptr)
811 {
812 SWAP (left_ptr, right_ptr, size);
813 if (mid == left_ptr)
814 mid = right_ptr;
815 else if (mid == right_ptr)
816 mid = left_ptr;
817 left_ptr += size;
818 right_ptr -= size;
819 }
820 else if (left_ptr == right_ptr)
821 {
822 left_ptr += size;
823 right_ptr -= size;
824 break;
825 }
826 }
827 while (left_ptr <= right_ptr);
828
829 /* Set up pointers for next iteration. First determine whether
830 left and right partitions are below the threshold size. If so,
831 ignore one or both. Otherwise, push the larger partition's
832 bounds on the stack and continue sorting the smaller one. */
833
834 if ((size_t) (right_ptr - lo) <= max_thresh)
835 {
836 if ((size_t) (hi - left_ptr) <= max_thresh)
837 /* Ignore both small partitions. */
838 POP (lo, hi);
839 else
840 /* Ignore small left partition. */
841 lo = left_ptr;
842 }
843 else if ((size_t) (hi - left_ptr) <= max_thresh)
844 /* Ignore small right partition. */
845 hi = right_ptr;
846 else if ((right_ptr - lo) > (hi - left_ptr))
847 {
848 /* Push larger left partition indices. */
849 PUSH (lo, right_ptr);
850 lo = left_ptr;
851 }
852 else
853 {
854 /* Push larger right partition indices. */
855 PUSH (left_ptr, hi);
856 hi = right_ptr;
857 }
858 }
859 }
860
861 /* Once the BASE_PTR array is partially sorted by quicksort the rest
862 is completely sorted using insertion sort, since this is efficient
863 for partitions below MAX_THRESH size. BASE_PTR points to the beginning
864 of the array to sort, and END_PTR points at the very last element in
865 the array (*not* one beyond it!). */
866
867 {
868 char *const end_ptr = &base_ptr[size * (total_elems - 1)];
869 char *tmp_ptr = base_ptr;
870 char *thresh = base_ptr + max_thresh;
871 if ( thresh > end_ptr )
872 thresh = end_ptr;
873 register char *run_ptr;
874
875 /* Find smallest element in first threshold and place it at the
876 array's beginning. This is the smallest array element,
877 and the operation speeds up insertion sort's inner loop. */
878
879 for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
880 if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, user_data) < 0)
881 tmp_ptr = run_ptr;
882
883 if (tmp_ptr != base_ptr)
884 SWAP (tmp_ptr, base_ptr, size);
885
886 /* Insertion sort, running from left-hand-side up to right-hand-side. */
887
888 run_ptr = base_ptr + size;
889 while ((run_ptr += size) <= end_ptr)
890 {
891 tmp_ptr = run_ptr - size;
892 while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, user_data) < 0)
893 tmp_ptr -= size;
894
895 tmp_ptr += size;
896 if (tmp_ptr != run_ptr)
897 {
898 char *trav;
899
900 trav = run_ptr + size;
901 while (--trav >= run_ptr)
902 {
903 char c = *trav;
904 char *hi, *lo;
905
906 for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
907 *hi = *lo;
908 *hi = c;
909 }
910 }
911 }
912 }
913 }
914
915 #endif // wxUSE_BASE
916
917
918
919 // ============================================================================
920 // GUI-only functions from now on
921 // ============================================================================
922
923 #if wxUSE_GUI
924
925 // this function is only really implemented for X11-based ports, including GTK1
926 // (GTK2 sets detectable auto-repeat automatically anyhow)
927 #if !(defined(__WXX11__) || defined(__WXMOTIF__) || \
928 (defined(__WXGTK__) && !defined(__WXGTK20__)))
929 bool wxSetDetectableAutoRepeat( bool WXUNUSED(flag) )
930 {
931 return true;
932 }
933 #endif // !X11-based port
934
935 // ----------------------------------------------------------------------------
936 // Launch default browser
937 // ----------------------------------------------------------------------------
938
939 #if defined(__WXMSW__)
940
941 // implemented in a port-specific utils source file:
942 bool wxDoLaunchDefaultBrowser(const wxString& url, const wxString& scheme, int flags);
943
944 #elif defined(__WXX11__) || defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXCOCOA__) || \
945 (defined(__WXOSX__) )
946
947 // implemented in a port-specific utils source file:
948 bool wxDoLaunchDefaultBrowser(const wxString& url, int flags);
949
950 #else
951
952 // a "generic" implementation:
953 bool wxDoLaunchDefaultBrowser(const wxString& url, int flags)
954 {
955 // on other platforms try to use mime types or wxExecute...
956
957 bool ok = false;
958 wxString cmd;
959
960 #if wxUSE_MIMETYPE
961 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(wxT("html"));
962 if ( ft )
963 {
964 wxString mt;
965 ft->GetMimeType(&mt);
966
967 ok = ft->GetOpenCommand(&cmd, wxFileType::MessageParameters(url));
968 delete ft;
969 }
970 #endif // wxUSE_MIMETYPE
971
972 if ( !ok || cmd.empty() )
973 {
974 // fallback to checking for the BROWSER environment variable
975 if ( !wxGetEnv(wxT("BROWSER"), &cmd) || cmd.empty() )
976 cmd << wxT(' ') << url;
977 }
978
979 ok = ( !cmd.empty() && wxExecute(cmd) );
980 if (ok)
981 return ok;
982
983 // no file type for HTML extension
984 wxLogError(_("No default application configured for HTML files."));
985
986 return false;
987 }
988 #endif
989
990 static bool DoLaunchDefaultBrowserHelper(const wxString& urlOrig, int flags)
991 {
992 // NOTE: we don't have to care about the wxBROWSER_NOBUSYCURSOR flag
993 // as it was already handled by wxLaunchDefaultBrowser
994
995 wxUnusedVar(flags);
996
997 wxString url(urlOrig), scheme;
998 wxURI uri(url);
999
1000 // this check is useful to avoid that wxURI recognizes as scheme parts of
1001 // the filename, in case urlOrig is a local filename
1002 // (e.g. "C:\\test.txt" when parsed by wxURI reports a scheme == "C")
1003 bool hasValidScheme = uri.HasScheme() && uri.GetScheme().length() > 1;
1004
1005 #if defined(__WXMSW__)
1006
1007 // NOTE: when testing wxMSW's wxLaunchDefaultBrowser all possible forms
1008 // of the URL/flags should be tested; e.g.:
1009 //
1010 // for (int i=0; i<2; i++)
1011 // {
1012 // // test arguments without a valid URL scheme:
1013 // wxLaunchDefaultBrowser("C:\\test.txt", i==0 ? 0 : wxBROWSER_NEW_WINDOW);
1014 // wxLaunchDefaultBrowser("wxwidgets.org", i==0 ? 0 : wxBROWSER_NEW_WINDOW);
1015 //
1016 // // test arguments with different valid schemes:
1017 // wxLaunchDefaultBrowser("file:/C%3A/test.txt", i==0 ? 0 : wxBROWSER_NEW_WINDOW);
1018 // wxLaunchDefaultBrowser("http://wxwidgets.org", i==0 ? 0 : wxBROWSER_NEW_WINDOW);
1019 // wxLaunchDefaultBrowser("mailto:user@host.org", i==0 ? 0 : wxBROWSER_NEW_WINDOW);
1020 // }
1021 // (assuming you have a C:\test.txt file)
1022
1023 if ( !hasValidScheme )
1024 {
1025 if (wxFileExists(urlOrig) || wxDirExists(urlOrig))
1026 {
1027 scheme = "file";
1028 // do not prepend the file scheme to the URL as ShellExecuteEx() doesn't like it
1029 }
1030 else
1031 {
1032 url.Prepend(wxS("http://"));
1033 scheme = "http";
1034 }
1035 }
1036 else if ( hasValidScheme )
1037 {
1038 scheme = uri.GetScheme();
1039
1040 if ( uri.GetScheme() == "file" )
1041 {
1042 // TODO: extract URLToFileName() to some always compiled in
1043 // function
1044 #if wxUSE_FILESYSTEM
1045 // ShellExecuteEx() doesn't like the "file" scheme when opening local files;
1046 // remove it
1047 url = wxFileSystem::URLToFileName(url).GetFullPath();
1048 #endif // wxUSE_FILESYSTEM
1049 }
1050 }
1051
1052 if (wxDoLaunchDefaultBrowser(url, scheme, flags))
1053 return true;
1054 //else: call wxLogSysError
1055 #else
1056 if ( !hasValidScheme )
1057 {
1058 // set the scheme of url to "http" or "file" if it does not have one
1059 if (wxFileExists(urlOrig) || wxDirExists(urlOrig))
1060 url.Prepend(wxS("file://"));
1061 else
1062 url.Prepend(wxS("http://"));
1063 }
1064
1065 if (wxDoLaunchDefaultBrowser(url, flags))
1066 return true;
1067 //else: call wxLogSysError
1068 #endif
1069
1070 wxLogSysError(_("Failed to open URL \"%s\" in default browser."),
1071 url.c_str());
1072
1073 return false;
1074 }
1075
1076 bool wxLaunchDefaultBrowser(const wxString& url, int flags)
1077 {
1078 // NOTE: as documented, "url" may be both a real well-formed URL
1079 // and a local file name
1080
1081 if ( flags & wxBROWSER_NOBUSYCURSOR )
1082 return DoLaunchDefaultBrowserHelper(url, flags);
1083
1084 wxBusyCursor bc;
1085 return DoLaunchDefaultBrowserHelper(url, flags);
1086 }
1087
1088 // ----------------------------------------------------------------------------
1089 // Menu accelerators related functions
1090 // ----------------------------------------------------------------------------
1091
1092 #if WXWIN_COMPATIBILITY_2_6
1093 wxChar *wxStripMenuCodes(const wxChar *in, wxChar *out)
1094 {
1095 #if wxUSE_MENUS
1096 wxString s = wxMenuItem::GetLabelText(in);
1097 #else
1098 wxString str(in);
1099 wxString s = wxStripMenuCodes(str);
1100 #endif // wxUSE_MENUS
1101 if ( out )
1102 {
1103 // go smash their buffer if it's not big enough - I love char * params
1104 memcpy(out, s.c_str(), s.length() * sizeof(wxChar));
1105 }
1106 else
1107 {
1108 out = new wxChar[s.length() + 1];
1109 wxStrcpy(out, s.c_str());
1110 }
1111
1112 return out;
1113 }
1114 #endif
1115
1116 wxString wxStripMenuCodes(const wxString& in, int flags)
1117 {
1118 wxASSERT_MSG( flags, wxT("this is useless to call without any flags") );
1119
1120 wxString out;
1121
1122 size_t len = in.length();
1123 out.reserve(len);
1124
1125 for ( size_t n = 0; n < len; n++ )
1126 {
1127 wxChar ch = in[n];
1128 if ( (flags & wxStrip_Mnemonics) && ch == wxT('&') )
1129 {
1130 // skip it, it is used to introduce the accel char (or to quote
1131 // itself in which case it should still be skipped): note that it
1132 // can't be the last character of the string
1133 if ( ++n == len )
1134 {
1135 wxLogDebug(wxT("Invalid menu string '%s'"), in.c_str());
1136 }
1137 else
1138 {
1139 // use the next char instead
1140 ch = in[n];
1141 }
1142 }
1143 else if ( (flags & wxStrip_Accel) && ch == wxT('\t') )
1144 {
1145 // everything after TAB is accel string, exit the loop
1146 break;
1147 }
1148
1149 out += ch;
1150 }
1151
1152 return out;
1153 }
1154
1155 // ----------------------------------------------------------------------------
1156 // Window search functions
1157 // ----------------------------------------------------------------------------
1158
1159 /*
1160 * If parent is non-NULL, look through children for a label or title
1161 * matching the specified string. If NULL, look through all top-level windows.
1162 *
1163 */
1164
1165 wxWindow *
1166 wxFindWindowByLabel (const wxString& title, wxWindow * parent)
1167 {
1168 return wxWindow::FindWindowByLabel( title, parent );
1169 }
1170
1171
1172 /*
1173 * If parent is non-NULL, look through children for a name
1174 * matching the specified string. If NULL, look through all top-level windows.
1175 *
1176 */
1177
1178 wxWindow *
1179 wxFindWindowByName (const wxString& name, wxWindow * parent)
1180 {
1181 return wxWindow::FindWindowByName( name, parent );
1182 }
1183
1184 // Returns menu item id or wxNOT_FOUND if none.
1185 int
1186 wxFindMenuItemId(wxFrame *frame,
1187 const wxString& menuString,
1188 const wxString& itemString)
1189 {
1190 #if wxUSE_MENUS
1191 wxMenuBar *menuBar = frame->GetMenuBar ();
1192 if ( menuBar )
1193 return menuBar->FindMenuItem (menuString, itemString);
1194 #else // !wxUSE_MENUS
1195 wxUnusedVar(frame);
1196 wxUnusedVar(menuString);
1197 wxUnusedVar(itemString);
1198 #endif // wxUSE_MENUS/!wxUSE_MENUS
1199
1200 return wxNOT_FOUND;
1201 }
1202
1203 // Try to find the deepest child that contains 'pt'.
1204 // We go backwards, to try to allow for controls that are spacially
1205 // within other controls, but are still siblings (e.g. buttons within
1206 // static boxes). Static boxes are likely to be created _before_ controls
1207 // that sit inside them.
1208 wxWindow* wxFindWindowAtPoint(wxWindow* win, const wxPoint& pt)
1209 {
1210 if (!win->IsShown())
1211 return NULL;
1212
1213 // Hack for wxNotebook case: at least in wxGTK, all pages
1214 // claim to be shown, so we must only deal with the selected one.
1215 #if wxUSE_NOTEBOOK
1216 if (win->IsKindOf(CLASSINFO(wxNotebook)))
1217 {
1218 wxNotebook* nb = (wxNotebook*) win;
1219 int sel = nb->GetSelection();
1220 if (sel >= 0)
1221 {
1222 wxWindow* child = nb->GetPage(sel);
1223 wxWindow* foundWin = wxFindWindowAtPoint(child, pt);
1224 if (foundWin)
1225 return foundWin;
1226 }
1227 }
1228 #endif
1229
1230 wxWindowList::compatibility_iterator node = win->GetChildren().GetLast();
1231 while (node)
1232 {
1233 wxWindow* child = node->GetData();
1234 wxWindow* foundWin = wxFindWindowAtPoint(child, pt);
1235 if (foundWin)
1236 return foundWin;
1237 node = node->GetPrevious();
1238 }
1239
1240 wxPoint pos = win->GetPosition();
1241 wxSize sz = win->GetSize();
1242 if ( !win->IsTopLevel() && win->GetParent() )
1243 {
1244 pos = win->GetParent()->ClientToScreen(pos);
1245 }
1246
1247 wxRect rect(pos, sz);
1248 if (rect.Contains(pt))
1249 return win;
1250
1251 return NULL;
1252 }
1253
1254 wxWindow* wxGenericFindWindowAtPoint(const wxPoint& pt)
1255 {
1256 // Go backwards through the list since windows
1257 // on top are likely to have been appended most
1258 // recently.
1259 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetLast();
1260 while (node)
1261 {
1262 wxWindow* win = node->GetData();
1263 wxWindow* found = wxFindWindowAtPoint(win, pt);
1264 if (found)
1265 return found;
1266 node = node->GetPrevious();
1267 }
1268 return NULL;
1269 }
1270
1271 // ----------------------------------------------------------------------------
1272 // GUI helpers
1273 // ----------------------------------------------------------------------------
1274
1275 /*
1276 * N.B. these convenience functions must be separate from msgdlgg.cpp, textdlgg.cpp
1277 * since otherwise the generic code may be pulled in unnecessarily.
1278 */
1279
1280 #if wxUSE_MSGDLG
1281
1282 int wxMessageBox(const wxString& message, const wxString& caption, long style,
1283 wxWindow *parent, int WXUNUSED(x), int WXUNUSED(y) )
1284 {
1285 // add the appropriate icon unless this was explicitly disabled by use of
1286 // wxICON_NONE
1287 if ( !(style & wxICON_NONE) && !(style & wxICON_MASK) )
1288 {
1289 style |= style & wxYES ? wxICON_QUESTION : wxICON_INFORMATION;
1290 }
1291
1292 wxMessageDialog dialog(parent, message, caption, style);
1293
1294 int ans = dialog.ShowModal();
1295 switch ( ans )
1296 {
1297 case wxID_OK:
1298 return wxOK;
1299 case wxID_YES:
1300 return wxYES;
1301 case wxID_NO:
1302 return wxNO;
1303 case wxID_CANCEL:
1304 return wxCANCEL;
1305 }
1306
1307 wxFAIL_MSG( wxT("unexpected return code from wxMessageDialog") );
1308
1309 return wxCANCEL;
1310 }
1311
1312 void wxInfoMessageBox(wxWindow* parent)
1313 {
1314 // don't translate these strings, they're for diagnostics purposes only
1315 wxString msg;
1316 msg.Printf(wxS("wxWidgets Library (%s port)\n")
1317 wxS("Version %d.%d.%d (Unicode: %s, debug level: %d),\n")
1318 wxS("compiled at %s %s\n\n")
1319 wxS("Runtime version of toolkit used is %d.%d.\n"),
1320 wxPlatformInfo::Get().GetPortIdName(),
1321 wxMAJOR_VERSION,
1322 wxMINOR_VERSION,
1323 wxRELEASE_NUMBER,
1324 #if wxUSE_UNICODE_UTF8
1325 "UTF-8",
1326 #elif wxUSE_UNICODE
1327 "wchar_t",
1328 #else
1329 "none",
1330 #endif
1331 wxDEBUG_LEVEL,
1332 __TDATE__,
1333 __TTIME__,
1334 wxPlatformInfo::Get().GetToolkitMajorVersion(),
1335 wxPlatformInfo::Get().GetToolkitMinorVersion()
1336 );
1337
1338 #ifdef __WXGTK__
1339 msg += wxString::Format("Compile-time GTK+ version is %d.%d.%d.\n",
1340 GTK_MAJOR_VERSION,
1341 GTK_MINOR_VERSION,
1342 GTK_MICRO_VERSION);
1343 #endif // __WXGTK__
1344
1345 msg += wxS("\nCopyright (c) 1995-2010 wxWidgets team");
1346
1347 wxMessageBox(msg, wxT("wxWidgets information"),
1348 wxICON_INFORMATION | wxOK,
1349 parent);
1350 }
1351
1352 #endif // wxUSE_MSGDLG
1353
1354 #if wxUSE_TEXTDLG
1355
1356 wxString wxGetTextFromUser(const wxString& message, const wxString& caption,
1357 const wxString& defaultValue, wxWindow *parent,
1358 wxCoord x, wxCoord y, bool centre )
1359 {
1360 wxString str;
1361 long style = wxTextEntryDialogStyle;
1362
1363 if (centre)
1364 style |= wxCENTRE;
1365 else
1366 style &= ~wxCENTRE;
1367
1368 wxTextEntryDialog dialog(parent, message, caption, defaultValue, style, wxPoint(x, y));
1369
1370 if (dialog.ShowModal() == wxID_OK)
1371 {
1372 str = dialog.GetValue();
1373 }
1374
1375 return str;
1376 }
1377
1378 wxString wxGetPasswordFromUser(const wxString& message,
1379 const wxString& caption,
1380 const wxString& defaultValue,
1381 wxWindow *parent,
1382 wxCoord x, wxCoord y, bool centre )
1383 {
1384 wxString str;
1385 long style = wxTextEntryDialogStyle;
1386
1387 if (centre)
1388 style |= wxCENTRE;
1389 else
1390 style &= ~wxCENTRE;
1391
1392 wxPasswordEntryDialog dialog(parent, message, caption, defaultValue,
1393 style, wxPoint(x, y));
1394 if ( dialog.ShowModal() == wxID_OK )
1395 {
1396 str = dialog.GetValue();
1397 }
1398
1399 return str;
1400 }
1401
1402 #endif // wxUSE_TEXTDLG
1403
1404 #if wxUSE_COLOURDLG
1405
1406 wxColour wxGetColourFromUser(wxWindow *parent,
1407 const wxColour& colInit,
1408 const wxString& caption,
1409 wxColourData *ptrData)
1410 {
1411 // contains serialized representation of wxColourData used the last time
1412 // the dialog was shown: we want to reuse it the next time in order to show
1413 // the same custom colours to the user (and we can't just have static
1414 // wxColourData itself because it's a GUI object and so should be destroyed
1415 // before GUI shutdown and doing it during static cleanup is too late)
1416 static wxString s_strColourData;
1417
1418 wxColourData data;
1419 if ( !ptrData )
1420 {
1421 ptrData = &data;
1422 if ( !s_strColourData.empty() )
1423 {
1424 if ( !data.FromString(s_strColourData) )
1425 {
1426 wxFAIL_MSG( "bug in wxColourData::FromString()?" );
1427 }
1428
1429 #ifdef __WXMSW__
1430 // we don't get back the "choose full" flag value from the native
1431 // dialog and so we can't preserve it between runs, so we decide to
1432 // always use it as it seems better than not using it (user can
1433 // just ignore the extra controls in the dialog but having to click
1434 // a button each time to show them would be very annoying
1435 data.SetChooseFull(true);
1436 #endif // __WXMSW__
1437 }
1438 }
1439
1440 if ( colInit.IsOk() )
1441 {
1442 ptrData->SetColour(colInit);
1443 }
1444
1445 wxColour colRet;
1446 wxColourDialog dialog(parent, ptrData);
1447 if (!caption.empty())
1448 dialog.SetTitle(caption);
1449 if ( dialog.ShowModal() == wxID_OK )
1450 {
1451 *ptrData = dialog.GetColourData();
1452 colRet = ptrData->GetColour();
1453 s_strColourData = ptrData->ToString();
1454 }
1455 //else: leave colRet invalid
1456
1457 return colRet;
1458 }
1459
1460 #endif // wxUSE_COLOURDLG
1461
1462 #if wxUSE_FONTDLG
1463
1464 wxFont wxGetFontFromUser(wxWindow *parent, const wxFont& fontInit, const wxString& caption)
1465 {
1466 wxFontData data;
1467 if ( fontInit.Ok() )
1468 {
1469 data.SetInitialFont(fontInit);
1470 }
1471
1472 wxFont fontRet;
1473 wxFontDialog dialog(parent, data);
1474 if (!caption.empty())
1475 dialog.SetTitle(caption);
1476 if ( dialog.ShowModal() == wxID_OK )
1477 {
1478 fontRet = dialog.GetFontData().GetChosenFont();
1479 }
1480 //else: leave it invalid
1481
1482 return fontRet;
1483 }
1484
1485 #endif // wxUSE_FONTDLG
1486
1487 // ----------------------------------------------------------------------------
1488 // wxSafeYield and supporting functions
1489 // ----------------------------------------------------------------------------
1490
1491 void wxEnableTopLevelWindows(bool enable)
1492 {
1493 wxWindowList::compatibility_iterator node;
1494 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
1495 node->GetData()->Enable(enable);
1496 }
1497
1498 wxWindowDisabler::wxWindowDisabler(bool disable)
1499 {
1500 m_disabled = disable;
1501 if ( disable )
1502 DoDisable();
1503 }
1504
1505 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
1506 {
1507 m_disabled = true;
1508 DoDisable(winToSkip);
1509 }
1510
1511 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
1512 {
1513 // remember the top level windows which were already disabled, so that we
1514 // don't reenable them later
1515 m_winDisabled = NULL;
1516
1517 wxWindowList::compatibility_iterator node;
1518 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
1519 {
1520 wxWindow *winTop = node->GetData();
1521 if ( winTop == winToSkip )
1522 continue;
1523
1524 // we don't need to disable the hidden or already disabled windows
1525 if ( winTop->IsEnabled() && winTop->IsShown() )
1526 {
1527 winTop->Disable();
1528 }
1529 else
1530 {
1531 if ( !m_winDisabled )
1532 {
1533 m_winDisabled = new wxWindowList;
1534 }
1535
1536 m_winDisabled->Append(winTop);
1537 }
1538 }
1539 }
1540
1541 wxWindowDisabler::~wxWindowDisabler()
1542 {
1543 if ( !m_disabled )
1544 return;
1545
1546 wxWindowList::compatibility_iterator node;
1547 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
1548 {
1549 wxWindow *winTop = node->GetData();
1550 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
1551 {
1552 winTop->Enable();
1553 }
1554 //else: had been already disabled, don't reenable
1555 }
1556
1557 delete m_winDisabled;
1558 }
1559
1560 // Yield to other apps/messages and disable user input to all windows except
1561 // the given one
1562 bool wxSafeYield(wxWindow *win, bool onlyIfNeeded)
1563 {
1564 wxWindowDisabler wd(win);
1565
1566 bool rc;
1567 if (onlyIfNeeded)
1568 rc = wxYieldIfNeeded();
1569 else
1570 rc = wxYield();
1571
1572 return rc;
1573 }
1574
1575 // ----------------------------------------------------------------------------
1576 // wxApp::Yield() wrappers for backwards compatibility
1577 // ----------------------------------------------------------------------------
1578
1579 bool wxYield()
1580 {
1581 return wxTheApp && wxTheApp->Yield();
1582 }
1583
1584 bool wxYieldIfNeeded()
1585 {
1586 return wxTheApp && wxTheApp->Yield(true);
1587 }
1588
1589 #endif // wxUSE_GUI