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