]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/app.cpp
?lk
[wxWidgets.git] / src / gtk / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "app.h"
12 #endif
13
14 #include "wx/app.h"
15 #include "wx/gdicmn.h"
16 #include "wx/utils.h"
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #include "wx/memory.h"
20 #include "wx/font.h"
21 #include "wx/settings.h"
22 #include "wx/dialog.h"
23
24 #if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26 #endif
27
28 #include "wx/module.h"
29 #include "wx/image.h"
30
31 #if wxUSE_THREADS
32 #include "wx/thread.h"
33 #endif
34
35 #include <unistd.h>
36
37 #include <glib.h>
38 #include <gdk/gdk.h>
39 #include <gtk/gtk.h>
40
41 #include "wx/gtk/win_gtk.h"
42
43 //-----------------------------------------------------------------------------
44 // global data
45 //-----------------------------------------------------------------------------
46
47 wxApp *wxTheApp = (wxApp *) NULL;
48 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50 extern wxResourceCache *wxTheResourceCache;
51 extern bool g_isIdle;
52
53 unsigned char g_palette[64*3] =
54 {
55 0x0, 0x0, 0x0,
56 0xff, 0xff, 0xff,
57 0xff, 0x0, 0x0,
58 0xff, 0xff, 0x0,
59 0x0, 0xff, 0x0,
60 0x0, 0x0, 0xff,
61 0x0, 0xff, 0xff,
62 0x99, 0x99, 0x99,
63 0xff, 0x88, 0x0,
64 0x88, 0x0, 0x0,
65 0x0, 0x88, 0x88,
66 0x88, 0x88, 0x0,
67 0xff, 0xcc, 0x97,
68 0xbb, 0xbb, 0xbb,
69 0x9f, 0x6b, 0x42,
70 0x55, 0x55, 0x55,
71 0xdd, 0xdd, 0xdd,
72 0x77, 0x77, 0x77,
73 0x33, 0x33, 0x33,
74 0xcc, 0x0, 0x0,
75 0xff, 0x44, 0x0,
76 0xff, 0xcc, 0x0,
77 0xcc, 0xcc, 0x0,
78 0x60, 0x60, 0x0,
79 0x0, 0x43, 0x0,
80 0x0, 0x7f, 0x0,
81 0x0, 0xcc, 0x0,
82 0x0, 0x44, 0x44,
83 0x0, 0x0, 0x44,
84 0x0, 0x0, 0x88,
85 0xef, 0xb1, 0x7b,
86 0xdf, 0x98, 0x5f,
87 0xbf, 0x87, 0x56,
88 0x7f, 0x57, 0x26,
89 0x5f, 0x39, 0xc,
90 0x3f, 0x1c, 0x0,
91 0x21, 0x0, 0x0,
92 0x0, 0x43, 0x87,
93 0x2d, 0x70, 0xaf,
94 0x5a, 0x9e, 0xd7,
95 0x87, 0xcc, 0xff,
96 0xff, 0xe0, 0xba,
97 0x21, 0x43, 0xf,
98 0x3d, 0x5d, 0x25,
99 0x59, 0x78, 0x3a,
100 0x75, 0x93, 0x4f,
101 0x91, 0xae, 0x64,
102 0xad, 0xc8, 0x7a,
103 0xf0, 0xa8, 0xef,
104 0xd0, 0x88, 0xd0,
105 0xaf, 0x66, 0xaf,
106 0x8e, 0x44, 0x8e,
107 0x6d, 0x22, 0x6d,
108 0x4b, 0x0, 0x4b,
109 0xff, 0xc0, 0xbc,
110 0xff, 0x93, 0x91,
111 0xff, 0x66, 0x67,
112 0xd8, 0xf2, 0xbf,
113 0xff, 0xc9, 0x68,
114 0xff, 0x96, 0x67,
115 0xa5, 0x60, 0xff,
116 0x51, 0xff, 0x99,
117 0x3f, 0xa5, 0x63,
118 0x98, 0x90, 0x67
119 };
120
121 //-----------------------------------------------------------------------------
122 // local functions
123 //-----------------------------------------------------------------------------
124
125 extern void wxFlushResources();
126
127 /* forward declaration */
128 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
129 void wxapp_install_idle_handler();
130
131 #if wxUSE_THREADS
132 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
133 #endif
134
135 //-----------------------------------------------------------------------------
136 // wxExit
137 //-----------------------------------------------------------------------------
138
139 void wxExit()
140 {
141 gtk_main_quit();
142 }
143
144 //-----------------------------------------------------------------------------
145 // wxYield
146 //-----------------------------------------------------------------------------
147
148 bool wxYield()
149 {
150 bool has_idle = (wxTheApp->m_idleTag != 0);
151
152 if (has_idle)
153 {
154 /* We need to temporarily remove idle callbacks or the loop will
155 never finish. */
156 gtk_idle_remove( wxTheApp->m_idleTag );
157 wxTheApp->m_idleTag = 0;
158 }
159
160 while (gtk_events_pending())
161 gtk_main_iteration();
162
163 /* it's necessary to call ProcessIdle() to update the frames sizes which
164 might have been changed (it also will update other things set from
165 OnUpdateUI() which is a nice (and desired) side effect) */
166 while (wxTheApp->ProcessIdle()) { }
167
168 if (has_idle)
169 {
170 /* re-add idle handler */
171 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
172 }
173
174 return TRUE;
175 }
176
177 //-----------------------------------------------------------------------------
178 // wxWakeUpIdle
179 //-----------------------------------------------------------------------------
180
181 void wxWakeUpIdle()
182 {
183 if (g_isIdle)
184 wxapp_install_idle_handler();
185 }
186
187 //-----------------------------------------------------------------------------
188 // local functions
189 //-----------------------------------------------------------------------------
190
191 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
192 {
193 if (!wxTheApp) return TRUE;
194
195 #if (GTK_MINOR_VERSION > 0)
196 /* when getting called from GDK's idle handler we
197 are no longer within GDK's grab on the GUI
198 thread so we must lock it here ourselves */
199 GDK_THREADS_ENTER ();
200 #endif
201
202 /* sent idle event to all who request them */
203 while (wxTheApp->ProcessIdle()) { }
204
205 /* we don't want any more idle events until the next event is
206 sent to wxGTK */
207 gtk_idle_remove( wxTheApp->m_idleTag );
208 wxTheApp->m_idleTag = 0;
209
210 /* indicate that we are now in idle mode - even so deeply
211 in idle mode that we don't get any idle events anymore.
212 this is like wxMSW where an idle event is sent only
213 once each time after the event queue has been completely
214 emptied */
215 g_isIdle = TRUE;
216
217 #if (GTK_MINOR_VERSION > 0)
218 /* release lock again */
219 GDK_THREADS_LEAVE ();
220 #endif
221
222 return TRUE;
223 }
224
225 void wxapp_install_idle_handler()
226 {
227 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
228
229 /* this routine gets called by all event handlers
230 indicating that the idle is over. */
231
232 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
233
234 g_isIdle = FALSE;
235 }
236
237 #if wxUSE_THREADS
238
239 void wxapp_install_thread_wakeup()
240 {
241 if (wxTheApp->m_wakeUpTimerTag) return;
242
243 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 100, wxapp_wakeup_timerout_callback, (gpointer) NULL );
244 }
245
246 void wxapp_uninstall_thread_wakeup()
247 {
248 if (!wxTheApp->m_wakeUpTimerTag) return;
249
250 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
251 wxTheApp->m_wakeUpTimerTag = 0;
252 }
253
254 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
255 {
256 wxapp_uninstall_thread_wakeup();
257
258 #if (GTK_MINOR_VERSION > 0)
259 // when getting called from GDK's time-out handler
260 // we are no longer within GDK's grab on the GUI
261 // thread so we must lock it here ourselves
262 GDK_THREADS_ENTER ();
263 #endif
264
265 // unblock other threads wishing to do some GUI things
266 wxMutexGuiLeave();
267
268 // wake up other threads
269 wxUsleep( 1 );
270
271 // block other thread again
272 wxMutexGuiEnter();
273
274 #if (GTK_MINOR_VERSION > 0)
275 // release lock again
276 GDK_THREADS_LEAVE ();
277 #endif
278
279 wxapp_install_thread_wakeup();
280
281 return TRUE;
282 }
283
284 #endif // wxUSE_THREADS
285
286 //-----------------------------------------------------------------------------
287 // wxApp
288 //-----------------------------------------------------------------------------
289
290 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
291
292 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
293 EVT_IDLE(wxApp::OnIdle)
294 END_EVENT_TABLE()
295
296 wxApp::wxApp()
297 {
298 wxTheApp = this;
299
300 m_topWindow = (wxWindow *) NULL;
301 m_exitOnFrameDelete = TRUE;
302
303 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
304
305 #if wxUSE_THREADS
306 m_wakeUpTimerTag = 0;
307 wxapp_install_thread_wakeup();
308 #endif
309
310 m_colorCube = (unsigned char*) NULL;
311 }
312
313 wxApp::~wxApp()
314 {
315 if (m_idleTag) gtk_idle_remove( m_idleTag );
316
317 #if wxUSE_THREADS
318 wxapp_uninstall_thread_wakeup();
319 #endif
320
321 if (m_colorCube) free(m_colorCube);
322 }
323
324 bool wxApp::OnInitGui()
325 {
326 GdkVisual *visual = gdk_visual_get_system();
327
328 /* on some machines, the default visual is just 256 colours, so
329 we make sure we get the best. this can sometimes be wasteful,
330 of course, but what do these guys pay $30.000 for? */
331 /*
332 if (gdk_visual_get_best() != gdk_visual_get_system())
333 {
334 GdkVisual* vis = gdk_visual_get_best();
335 gtk_widget_set_default_visual( vis );
336
337 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
338 gtk_widget_set_default_colormap( colormap );
339
340 visual = vis;
341 }
342 */
343
344 /* Nothing to do for 15, 16, 24, 32 bit displays */
345 if (visual->depth > 8) return TRUE;
346
347 /* this initiates the standard palette as defined by GdkImlib
348 in the GNOME libraries. it ensures that all GNOME applications
349 use the same 64 colormap entries on 8-bit displays so you
350 can use several rather graphics-heavy applications at the
351 same time.
352 NOTE: this doesn't really seem to work this way... */
353
354 /*
355 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
356
357 for (int i = 0; i < 64; i++)
358 {
359 GdkColor col;
360 col.red = g_palette[i*3 + 0] << 8;
361 col.green = g_palette[i*3 + 1] << 8;
362 col.blue = g_palette[i*3 + 2] << 8;
363 col.pixel = 0;
364
365 gdk_color_alloc( cmap, &col );
366 }
367
368 gtk_widget_set_default_colormap( cmap );
369 */
370
371 /* initialize color cube for 8-bit color reduction dithering */
372
373 GdkColormap *cmap = gtk_widget_get_default_colormap();
374
375 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
376
377 for (int r = 0; r < 32; r++)
378 {
379 for (int g = 0; g < 32; g++)
380 {
381 for (int b = 0; b < 32; b++)
382 {
383 int rr = (r << 3) | (r >> 2);
384 int gg = (g << 3) | (g >> 2);
385 int bb = (b << 3) | (b >> 2);
386
387 int index = -1;
388
389 GdkColor *colors = cmap->colors;
390 if (colors)
391 {
392 int max = 3 * 65536;
393
394 for (int i = 0; i < cmap->size; i++)
395 {
396 int rdiff = ((rr << 8) - colors[i].red);
397 int gdiff = ((gg << 8) - colors[i].green);
398 int bdiff = ((bb << 8) - colors[i].blue);
399 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
400 if (sum < max)
401 {
402 index = i; max = sum;
403 }
404 }
405 }
406 else
407 {
408 #if (GTK_MINOR_VERSION > 0)
409 /* assume 8-bit true or static colors. this really
410 exists. */
411 GdkVisual* vis = gdk_colormap_get_visual( cmap );
412 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
413 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
414 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
415 #else
416 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
417 #endif
418 }
419 m_colorCube[ (r*1024) + (g*32) + b ] = index;
420 }
421 }
422 }
423
424 return TRUE;
425 }
426
427 bool wxApp::ProcessIdle()
428 {
429 wxIdleEvent event;
430 event.SetEventObject( this );
431 ProcessEvent( event );
432
433 return event.MoreRequested();
434 }
435
436 void wxApp::OnIdle( wxIdleEvent &event )
437 {
438 static bool s_inOnIdle = FALSE;
439
440 /* Avoid recursion (via ProcessEvent default case) */
441 if (s_inOnIdle)
442 return;
443
444 s_inOnIdle = TRUE;
445
446 /* Resend in the main thread events which have been prepared in other
447 threads */
448 ProcessPendingEvents();
449
450 /* 'Garbage' collection of windows deleted with Close(). */
451 DeletePendingObjects();
452
453 /* flush the logged messages if any */
454 #if wxUSE_LOG
455 wxLog *log = wxLog::GetActiveTarget();
456 if (log != NULL && log->HasPendingMessages())
457 log->Flush();
458 #endif // wxUSE_LOG
459
460 /* Send OnIdle events to all windows */
461 bool needMore = SendIdleEvents();
462
463 if (needMore)
464 event.RequestMore(TRUE);
465
466 s_inOnIdle = FALSE;
467 }
468
469 bool wxApp::SendIdleEvents()
470 {
471 bool needMore = FALSE;
472
473 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
474 while (node)
475 {
476 wxWindow* win = node->GetData();
477 if (SendIdleEvents(win))
478 needMore = TRUE;
479 node = node->GetNext();
480 }
481
482 return needMore;
483 }
484
485 bool wxApp::SendIdleEvents( wxWindow* win )
486 {
487 bool needMore = FALSE;
488
489 wxIdleEvent event;
490 event.SetEventObject(win);
491
492 win->ProcessEvent(event);
493
494 win->OnInternalIdle();
495
496 if (event.MoreRequested())
497 needMore = TRUE;
498
499 wxNode* node = win->GetChildren().First();
500 while (node)
501 {
502 wxWindow* win = (wxWindow*) node->Data();
503 if (SendIdleEvents(win))
504 needMore = TRUE;
505
506 node = node->Next();
507 }
508 return needMore ;
509 }
510
511 int wxApp::MainLoop()
512 {
513 gtk_main();
514 return 0;
515 }
516
517 void wxApp::ExitMainLoop()
518 {
519 gtk_main_quit();
520 }
521
522 bool wxApp::Initialized()
523 {
524 return m_initialized;
525 }
526
527 bool wxApp::Pending()
528 {
529 return (gtk_events_pending() > 0);
530 }
531
532 void wxApp::Dispatch()
533 {
534 gtk_main_iteration();
535 }
536
537 void wxApp::DeletePendingObjects()
538 {
539 wxNode *node = wxPendingDelete.First();
540 while (node)
541 {
542 wxObject *obj = (wxObject *)node->Data();
543
544 delete obj;
545
546 if (wxPendingDelete.Find(obj))
547 delete node;
548
549 node = wxPendingDelete.First();
550 }
551 }
552
553 bool wxApp::Initialize()
554 {
555 wxBuffer = new wxChar[BUFSIZ + 512];
556
557 wxClassInfo::InitializeClasses();
558
559 wxSystemSettings::Init();
560
561 // GL: I'm annoyed ... I don't know where to put this and I don't want to
562 // create a module for that as it's part of the core.
563 #if wxUSE_THREADS
564 wxPendingEvents = new wxList();
565 wxPendingEventsLocker = new wxCriticalSection();
566 #endif
567
568 /*
569 wxTheFontNameDirectory = new wxFontNameDirectory;
570 wxTheFontNameDirectory->Initialize();
571 */
572
573 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
574 wxTheColourDatabase->Initialize();
575
576 wxInitializeStockLists();
577 wxInitializeStockObjects();
578
579 #if wxUSE_WX_RESOURCES
580 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
581
582 wxInitializeResourceSystem();
583 #endif
584
585 wxModule::RegisterModules();
586 if (!wxModule::InitializeModules()) return FALSE;
587
588 return TRUE;
589 }
590
591 void wxApp::CleanUp()
592 {
593 wxModule::CleanUpModules();
594
595 #if wxUSE_WX_RESOURCES
596 wxFlushResources();
597
598 if (wxTheResourceCache)
599 delete wxTheResourceCache;
600 wxTheResourceCache = (wxResourceCache*) NULL;
601
602 wxCleanUpResourceSystem();
603 #endif
604
605 if (wxTheColourDatabase)
606 delete wxTheColourDatabase;
607 wxTheColourDatabase = (wxColourDatabase*) NULL;
608
609 /*
610 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
611 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
612 */
613
614 wxDeleteStockObjects();
615
616 wxDeleteStockLists();
617
618 delete wxTheApp;
619 wxTheApp = (wxApp*) NULL;
620
621 // GL: I'm annoyed ... I don't know where to put this and I don't want to
622 // create a module for that as it's part of the core.
623 #if wxUSE_THREADS
624 delete wxPendingEvents;
625 delete wxPendingEventsLocker;
626 #endif
627
628 wxSystemSettings::Done();
629
630 delete[] wxBuffer;
631
632 wxClassInfo::CleanUpClasses();
633
634 // check for memory leaks
635 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
636 if (wxDebugContext::CountObjectsLeft() > 0)
637 {
638 wxLogDebug(wxT("There were memory leaks.\n"));
639 wxDebugContext::Dump();
640 wxDebugContext::PrintStatistics();
641 }
642 #endif // Debug
643
644 #if wxUSE_LOG
645 // do this as the very last thing because everything else can log messages
646 wxLog::DontCreateOnDemand();
647
648 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
649 if (oldLog)
650 delete oldLog;
651 #endif // wxUSE_LOG
652 }
653
654 //-----------------------------------------------------------------------------
655 // wxEntry
656 //-----------------------------------------------------------------------------
657
658 int wxEntry( int argc, char *argv[] )
659 {
660 gtk_set_locale();
661
662 #if wxUSE_WCHAR_T
663 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
664 #else
665 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
666 #endif
667
668 gtk_init( &argc, &argv );
669
670 wxSetDetectableAutoRepeat( TRUE );
671
672 if (!wxApp::Initialize())
673 return -1;
674
675 if (!wxTheApp)
676 {
677 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
678 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
679
680 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
681
682 wxObject *test_app = app_ini();
683
684 wxTheApp = (wxApp*) test_app;
685 }
686
687 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
688
689 wxTheApp->argc = argc;
690 #if wxUSE_UNICODE
691 wxTheApp->argv = new wxChar*[argc+1];
692 int mb_argc = 0;
693 while (mb_argc < argc) {
694 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
695 mb_argc++;
696 }
697 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
698 #else
699 wxTheApp->argv = argv;
700 #endif
701
702 wxString name(wxFileNameFromPath(argv[0]));
703 wxStripExtension( name );
704 wxTheApp->SetAppName( name );
705
706 int retValue = 0;
707
708 if ( !wxTheApp->OnInitGui() )
709 retValue = -1;
710
711 // Here frames insert themselves automatically into wxTopLevelWindows by
712 // getting created in OnInit().
713 if ( retValue == 0 )
714 {
715 if ( !wxTheApp->OnInit() )
716 retValue = -1;
717 }
718
719 if ( retValue == 0 )
720 {
721 /* delete pending toplevel windows (typically a single
722 dialog) so that, if there isn't any left, we don't
723 call OnRun() */
724 wxTheApp->DeletePendingObjects();
725
726 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
727
728 if (wxTheApp->Initialized())
729 {
730 retValue = wxTheApp->OnRun();
731
732 wxWindow *topWindow = wxTheApp->GetTopWindow();
733 if (topWindow)
734 {
735 /* Forcibly delete the window. */
736 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
737 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
738 {
739 topWindow->Close( TRUE );
740 wxTheApp->DeletePendingObjects();
741 }
742 else
743 {
744 delete topWindow;
745 wxTheApp->SetTopWindow( (wxWindow*) NULL );
746 }
747 }
748 wxTheApp->OnExit();
749 }
750 }
751
752 #if wxUSE_LOG
753 // flush the logged messages if any
754 wxLog *log = wxLog::GetActiveTarget();
755 if (log != NULL && log->HasPendingMessages())
756 log->Flush();
757
758 // continuing to use user defined log target is unsafe from now on because
759 // some resources may be already unavailable, so replace it by something
760 // more safe
761 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
762 if ( oldlog )
763 delete oldlog;
764 #endif // wxUSE_LOG
765
766 wxApp::CleanUp();
767
768 return retValue;
769 }
770
771 #include "wx/gtk/info.xpm"
772 #include "wx/gtk/error.xpm"
773 #include "wx/gtk/question.xpm"
774 #include "wx/gtk/warning.xpm"
775
776 wxIcon
777 wxApp::GetStdIcon(int which) const
778 {
779 switch(which)
780 {
781 case wxICON_INFORMATION:
782 return wxIcon(info_xpm);
783
784 case wxICON_QUESTION:
785 return wxIcon(question_xpm);
786
787 case wxICON_EXCLAMATION:
788 return wxIcon(warning_xpm);
789
790 default:
791 wxFAIL_MSG(wxT("requested non existent standard icon"));
792 // still fall through
793
794 case wxICON_HAND:
795 return wxIcon(error_xpm);
796 }
797 }