]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/window.cpp
Don't do the wx cleanup if wxPython didn't do the initialization.
[wxWidgets.git] / src / gtk1 / window.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/window.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
11 #ifdef __GNUG__
12 #pragma implementation "window.h"
13 #endif
14
15 #ifdef __VMS
16 #define XWarpPointer XWARPPOINTER
17 #endif
18
19 #include "wx/defs.h"
20 #include "wx/window.h"
21 #include "wx/dcclient.h"
22 #include "wx/frame.h"
23 #include "wx/app.h"
24 #include "wx/layout.h"
25 #include "wx/utils.h"
26 #include "wx/dialog.h"
27 #include "wx/msgdlg.h"
28 #include "wx/module.h"
29
30 #if wxUSE_DRAG_AND_DROP
31 #include "wx/dnd.h"
32 #endif
33
34 #if wxUSE_TOOLTIPS
35 #include "wx/tooltip.h"
36 #endif
37
38 #if wxUSE_CARET
39 #include "wx/caret.h"
40 #endif // wxUSE_CARET
41
42 #if wxUSE_TEXTCTRL
43 #include "wx/textctrl.h"
44 #endif
45
46 #include "wx/menu.h"
47 #include "wx/statusbr.h"
48 #include "wx/intl.h"
49 #include "wx/settings.h"
50 #include "wx/log.h"
51
52 #ifdef __WXDEBUG__
53 #include "wx/thread.h"
54 #endif
55
56 #include <math.h>
57
58 #include "wx/gtk/private.h"
59 #include <gdk/gdkprivate.h>
60 #include <gdk/gdkkeysyms.h>
61 #include <gdk/gdkx.h>
62
63 #include <gtk/gtk.h>
64 #include <gtk/gtkprivate.h>
65
66 #include "wx/gtk/win_gtk.h"
67
68 #ifdef __WXGTK20__
69 #define SET_CONTAINER_FOCUS(w, d) gtk_widget_child_focus((w), (d))
70 #else
71 #define SET_CONTAINER_FOCUS(w, d) gtk_container_focus(GTK_CONTAINER(w), (d))
72 #endif
73
74 #ifdef __WXGTK20__
75 #ifdef HAVE_XIM
76 #undef HAVE_XIM
77 #endif
78 #endif
79
80 #ifdef __WXGTK20__
81 extern GtkContainerClass *pizza_parent_class;
82 #endif
83
84 //-----------------------------------------------------------------------------
85 // documentation on internals
86 //-----------------------------------------------------------------------------
87
88 /*
89 I have been asked several times about writing some documentation about
90 the GTK port of wxWindows, especially its internal structures. Obviously,
91 you cannot understand wxGTK without knowing a little about the GTK, but
92 some more information about what the wxWindow, which is the base class
93 for all other window classes, does seems required as well.
94
95 I)
96
97 What does wxWindow do? It contains the common interface for the following
98 jobs of its descendants:
99
100 1) Define the rudimentary behaviour common to all window classes, such as
101 resizing, intercepting user input (so as to make it possible to use these
102 events for special purposes in a derived class), window names etc.
103
104 2) Provide the possibility to contain and manage children, if the derived
105 class is allowed to contain children, which holds true for those window
106 classes which do not display a native GTK widget. To name them, these
107 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
108 work classes are a special case and are handled a bit differently from
109 the rest. The same holds true for the wxNotebook class.
110
111 3) Provide the possibility to draw into a client area of a window. This,
112 too, only holds true for classes that do not display a native GTK widget
113 as above.
114
115 4) Provide the entire mechanism for scrolling widgets. This actual inter-
116 face for this is usually in wxScrolledWindow, but the GTK implementation
117 is in this class.
118
119 5) A multitude of helper or extra methods for special purposes, such as
120 Drag'n'Drop, managing validators etc.
121
122 6) Display a border (sunken, raised, simple or none).
123
124 Normally one might expect, that one wxWindows window would always correspond
125 to one GTK widget. Under GTK, there is no such allround widget that has all
126 the functionality. Moreover, the GTK defines a client area as a different
127 widget from the actual widget you are handling. Last but not least some
128 special classes (e.g. wxFrame) handle different categories of widgets and
129 still have the possibility to draw something in the client area.
130 It was therefore required to write a special purpose GTK widget, that would
131 represent a client area in the sense of wxWindows capable to do the jobs
132 2), 3) and 4). I have written this class and it resides in win_gtk.c of
133 this directory.
134
135 All windows must have a widget, with which they interact with other under-
136 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
137 thw wxWindow class has a member variable called m_widget which holds a
138 pointer to this widget. When the window class represents a GTK native widget,
139 this is (in most cases) the only GTK widget the class manages. E.g. the
140 wxStatitText class handles only a GtkLabel widget a pointer to which you
141 can find in m_widget (defined in wxWindow)
142
143 When the class has a client area for drawing into and for containing children
144 it has to handle the client area widget (of the type GtkPizza, defined in
145 win_gtk.c), but there could be any number of widgets, handled by a class
146 The common rule for all windows is only, that the widget that interacts with
147 the rest of GTK must be referenced in m_widget and all other widgets must be
148 children of this widget on the GTK level. The top-most widget, which also
149 represents the client area, must be in the m_wxwindow field and must be of
150 the type GtkPizza.
151
152 As I said, the window classes that display a GTK native widget only have
153 one widget, so in the case of e.g. the wxButton class m_widget holds a
154 pointer to a GtkButton widget. But windows with client areas (for drawing
155 and children) have a m_widget field that is a pointer to a GtkScrolled-
156 Window and a m_wxwindow field that is pointer to a GtkPizza and this
157 one is (in the GTK sense) a child of the GtkScrolledWindow.
158
159 If the m_wxwindow field is set, then all input to this widget is inter-
160 cepted and sent to the wxWindows class. If not, all input to the widget
161 that gets pointed to by m_widget gets intercepted and sent to the class.
162
163 II)
164
165 The design of scrolling in wxWindows is markedly different from that offered
166 by the GTK itself and therefore we cannot simply take it as it is. In GTK,
167 clicking on a scrollbar belonging to scrolled window will inevitably move
168 the window. In wxWindows, the scrollbar will only emit an event, send this
169 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
170 which actually moves the window and its subchildren. Note that GtkPizza
171 memorizes how much it has been scrolled but that wxWindows forgets this
172 so that the two coordinates systems have to be kept in synch. This is done
173 in various places using the pizza->xoffset and pizza->yoffset values.
174
175 III)
176
177 Singularily the most broken code in GTK is the code that is supposes to
178 inform subwindows (child windows) about new positions. Very often, duplicate
179 events are sent without changes in size or position, equally often no
180 events are sent at all (All this is due to a bug in the GtkContainer code
181 which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
182 GTK's own system and it simply waits for size events for toplevel windows
183 and then iterates down the respective size events to all window. This has
184 the disadvantage, that windows might get size events before the GTK widget
185 actually has the reported size. This doesn't normally pose any problem, but
186 the OpenGl drawing routines rely on correct behaviour. Therefore, I have
187 added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
188 i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
189 window that is used for OpenGl output really has that size (as reported by
190 GTK).
191
192 IV)
193
194 If someone at some point of time feels the immense desire to have a look at,
195 change or attempt to optimse the Refresh() logic, this person will need an
196 intimate understanding of what a "draw" and what an "expose" events are and
197 what there are used for, in particular when used in connection with GTK's
198 own windowless widgets. Beware.
199
200 V)
201
202 Cursors, too, have been a constant source of pleasure. The main difficulty
203 is that a GdkWindow inherits a cursor if the programmer sets a new cursor
204 for the parent. To prevent this from doing too much harm, I use idle time
205 to set the cursor over and over again, starting from the toplevel windows
206 and ending with the youngest generation (speaking of parent and child windows).
207 Also don't forget that cursors (like much else) are connected to GdkWindows,
208 not GtkWidgets and that the "window" field of a GtkWidget might very well
209 point to the GdkWindow of the parent widget (-> "window less widget") and
210 that the two obviously have very different meanings.
211
212 */
213
214 //-----------------------------------------------------------------------------
215 // data
216 //-----------------------------------------------------------------------------
217
218 extern wxList wxPendingDelete;
219 extern bool g_blockEventsOnDrag;
220 extern bool g_blockEventsOnScroll;
221 extern wxCursor g_globalCursor;
222
223 static GdkGC *g_eraseGC = NULL;
224
225 // mouse capture state: the window which has it and if the mouse is currently
226 // inside it
227 static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL;
228 static bool g_captureWindowHasMouse = FALSE;
229
230 /* extern */ wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL;
231
232 // the last window which had the focus - this is normally never NULL (except
233 // if we never had focus at all) as even when g_focusWindow is NULL it still
234 // keeps its previous value
235 static wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL;
236
237 // the frame that is currently active (i.e. its child has focus). It is
238 // used to generate wxActivateEvents
239 static wxWindowGTK *g_activeFrame = (wxWindowGTK*) NULL;
240 static bool g_activeFrameLostFocus = FALSE;
241
242 // If a window get the focus set but has not been realized
243 // yet, defer setting the focus to idle time.
244 wxWindowGTK *g_delayedFocus = (wxWindowGTK*) NULL;
245
246 // if we detect that the app has got/lost the focus, we set this variable to
247 // either TRUE or FALSE and an activate event will be sent during the next
248 // OnIdle() call and it is reset to -1: this value means that we shouldn't
249 // send any activate events at all
250 static int g_sendActivateEvent = -1;
251
252 /* hack: we need something to pass to gtk_menu_popup, so we store the time of
253 the last click here */
254 static guint32 gs_timeLastClick = 0;
255
256 extern bool g_mainThreadLocked;
257
258 //-----------------------------------------------------------------------------
259 // debug
260 //-----------------------------------------------------------------------------
261
262 #ifndef __WXGTK20__
263 #define DISABLE_STYLE_IF_BROKEN_THEME 1
264 #endif
265
266 #ifdef __WXDEBUG__
267
268 #if wxUSE_THREADS
269 # define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
270 #else
271 # define DEBUG_MAIN_THREAD
272 #endif
273
274 static gint gtk_debug_focus_in_callback( GtkWidget *WXUNUSED(widget),
275 GdkEvent *WXUNUSED(event),
276 const wxChar *WXUNUSED(name) )
277 {
278 /*
279 static bool s_done = FALSE;
280 if ( !s_done )
281 {
282 wxLog::AddTraceMask("focus");
283 s_done = TRUE;
284 }
285 wxLogTrace(wxT("FOCUS NOW AT: %s"), name);
286 */
287
288 return FALSE;
289 }
290
291 void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *window )
292 {
293 // suppress warnings about gtk_debug_focus_in_callback being unused with
294 // this "if ( 0 )"
295 if ( 0 )
296 {
297 wxString tmp = name;
298 tmp += wxT(" FROM ");
299 tmp += window;
300
301 wxChar *s = new wxChar[tmp.Length()+1];
302
303 wxStrcpy( s, tmp );
304
305 gtk_signal_connect( GTK_OBJECT(widget), "focus_in_event",
306 GTK_SIGNAL_FUNC(gtk_debug_focus_in_callback), (gpointer)s );
307 }
308 }
309
310 #else
311 #define DEBUG_MAIN_THREAD
312 #endif // Debug
313
314 //-----------------------------------------------------------------------------
315 // missing gdk functions
316 //-----------------------------------------------------------------------------
317
318 void
319 gdk_window_warp_pointer (GdkWindow *window,
320 gint x,
321 gint y)
322 {
323 #ifndef __WXGTK20__
324 GdkWindowPrivate *priv;
325 #endif
326
327 if (!window)
328 window = GDK_ROOT_PARENT();
329
330 #ifdef __WXGTK20__
331 if (!GDK_WINDOW_DESTROYED(window))
332 {
333 XWarpPointer (GDK_WINDOW_XDISPLAY(window),
334 None, /* not source window -> move from anywhere */
335 GDK_WINDOW_XID(window), /* dest window */
336 0, 0, 0, 0, /* not source window -> move from anywhere */
337 x, y );
338 }
339 #else
340 priv = (GdkWindowPrivate*) window;
341
342 if (!priv->destroyed)
343 {
344 XWarpPointer (priv->xdisplay,
345 None, /* not source window -> move from anywhere */
346 priv->xwindow, /* dest window */
347 0, 0, 0, 0, /* not source window -> move from anywhere */
348 x, y );
349 }
350 #endif
351 }
352
353 //-----------------------------------------------------------------------------
354 // idle system
355 //-----------------------------------------------------------------------------
356
357 extern void wxapp_install_idle_handler();
358 extern bool g_isIdle;
359
360 //-----------------------------------------------------------------------------
361 // local code (see below)
362 //-----------------------------------------------------------------------------
363
364 // returns the child of win which currently has focus or NULL if not found
365 //
366 // Note: can't be static, needed by textctrl.cpp.
367 wxWindow *wxFindFocusedChild(wxWindowGTK *win)
368 {
369 wxWindow *winFocus = wxWindowGTK::FindFocus();
370 if ( !winFocus )
371 return (wxWindow *)NULL;
372
373 if ( winFocus == win )
374 return (wxWindow *)win;
375
376 for ( wxWindowList::Node *node = win->GetChildren().GetFirst();
377 node;
378 node = node->GetNext() )
379 {
380 wxWindow *child = wxFindFocusedChild(node->GetData());
381 if ( child )
382 return child;
383 }
384
385 return (wxWindow *)NULL;
386 }
387
388 // Returns toplevel grandparent of given window:
389 static wxWindowGTK* wxGetTopLevelParent(wxWindowGTK *win)
390 {
391 wxWindowGTK *p = win;
392 while (p && !p->IsTopLevel())
393 p = p->GetParent();
394 return p;
395 }
396
397 static void draw_frame( GtkWidget *widget, wxWindowGTK *win )
398 {
399 // wxUniversal widgets draw the borders and scrollbars themselves
400 #ifndef __WXUNIVERSAL__
401 if (!win->m_hasVMT)
402 return;
403
404 int dw = 0;
405 int dh = 0;
406
407 if (win->m_hasScrolling)
408 {
409 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(widget);
410
411 GtkRequisition vscroll_req;
412 vscroll_req.width = 2;
413 vscroll_req.height = 2;
414 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
415 (scroll_window->vscrollbar, &vscroll_req );
416
417 GtkRequisition hscroll_req;
418 hscroll_req.width = 2;
419 hscroll_req.height = 2;
420 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
421 (scroll_window->hscrollbar, &hscroll_req );
422
423 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(widget) );
424
425 if (scroll_window->vscrollbar_visible)
426 {
427 dw += vscroll_req.width;
428 dw += scroll_class->scrollbar_spacing;
429 }
430
431 if (scroll_window->hscrollbar_visible)
432 {
433 dh += hscroll_req.height;
434 dh += scroll_class->scrollbar_spacing;
435 }
436 }
437
438 int dx = 0;
439 int dy = 0;
440 if (GTK_WIDGET_NO_WINDOW (widget))
441 {
442 dx += widget->allocation.x;
443 dy += widget->allocation.y;
444 }
445
446 if (win->HasFlag(wxRAISED_BORDER))
447 {
448 gtk_draw_shadow( widget->style,
449 widget->window,
450 GTK_STATE_NORMAL,
451 GTK_SHADOW_OUT,
452 dx, dy,
453 widget->allocation.width-dw, widget->allocation.height-dh );
454 return;
455 }
456
457 if (win->HasFlag(wxSUNKEN_BORDER))
458 {
459 gtk_draw_shadow( widget->style,
460 widget->window,
461 GTK_STATE_NORMAL,
462 GTK_SHADOW_IN,
463 dx, dy,
464 widget->allocation.width-dw, widget->allocation.height-dh );
465 return;
466 }
467
468 if (win->HasFlag(wxSIMPLE_BORDER))
469 {
470 GdkGC *gc;
471 gc = gdk_gc_new( widget->window );
472 gdk_gc_set_foreground( gc, &widget->style->black );
473 gdk_draw_rectangle( widget->window, gc, FALSE,
474 dx, dy,
475 widget->allocation.width-dw-1, widget->allocation.height-dh-1 );
476 gdk_gc_unref( gc );
477 return;
478 }
479 #endif // __WXUNIVERSAL__
480 }
481
482 //-----------------------------------------------------------------------------
483 // "expose_event" of m_widget
484 //-----------------------------------------------------------------------------
485
486 gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindowGTK *win )
487 {
488 if (gdk_event->count > 0) return FALSE;
489
490 draw_frame( widget, win );
491
492 #ifdef __WXGTK20__
493
494 (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event);
495
496 #endif
497 return TRUE;
498 }
499
500 //-----------------------------------------------------------------------------
501 // "draw" of m_widget
502 //-----------------------------------------------------------------------------
503
504 #ifndef __WXGTK20__
505
506 static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNUSED(rect), wxWindowGTK *win )
507 {
508 draw_frame( widget, win );
509 }
510
511 #endif // GTK+ < 2.0
512
513 //-----------------------------------------------------------------------------
514 // "size_request" of m_widget
515 //-----------------------------------------------------------------------------
516
517 static void gtk_window_size_request_callback( GtkWidget *widget, GtkRequisition *requisition, wxWindow *win )
518 {
519 int w,h;
520 win->GetSize( &w, &h );
521 if (w < 2) w = 2;
522 if (h < 2) h = 2;
523
524 requisition->height = h;
525 requisition->width = w;
526 }
527
528 //-----------------------------------------------------------------------------
529 // "expose_event" of m_wxwindow
530 //-----------------------------------------------------------------------------
531
532 static int gtk_window_expose_callback( GtkWidget *widget,
533 GdkEventExpose *gdk_event,
534 wxWindow *win )
535 {
536 DEBUG_MAIN_THREAD
537
538 if (g_isIdle)
539 wxapp_install_idle_handler();
540
541 #if 0
542 if (win->GetName())
543 {
544 wxPrintf( wxT("OnExpose from ") );
545 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
546 wxPrintf( win->GetClassInfo()->GetClassName() );
547 wxPrintf( wxT(" %d %d %d %d\n"), (int)gdk_event->area.x,
548 (int)gdk_event->area.y,
549 (int)gdk_event->area.width,
550 (int)gdk_event->area.height );
551 }
552 #endif
553
554 #ifndef __WXUNIVERSAL__
555 GtkPizza *pizza = GTK_PIZZA (widget);
556
557 if (win->GetThemeEnabled())
558 {
559 wxWindow *parent = win->GetParent();
560 while (parent && !parent->IsTopLevel())
561 parent = parent->GetParent();
562 if (!parent)
563 parent = win;
564
565 gtk_paint_flat_box (parent->m_widget->style,
566 pizza->bin_window,
567 GTK_STATE_NORMAL,
568 GTK_SHADOW_NONE,
569 &gdk_event->area,
570 parent->m_widget,
571 (char *)"base",
572 0, 0, -1, -1);
573 }
574 #endif
575
576 win->GetUpdateRegion().Union( gdk_event->area.x,
577 gdk_event->area.y,
578 gdk_event->area.width,
579 gdk_event->area.height );
580
581 // Actual redrawing takes place in idle time.
582 win->Update();
583
584 #ifdef __WXGTK20__
585
586 (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event);
587
588 #endif
589
590 return TRUE;
591 }
592
593 //-----------------------------------------------------------------------------
594 // "event" of m_wxwindow
595 //-----------------------------------------------------------------------------
596
597 // GTK thinks it is clever and filters out a certain amount of "unneeded"
598 // expose events. We need them, of course, so we override the main event
599 // procedure in GtkWidget by giving our own handler for all system events.
600 // There, we look for expose events ourselves whereas all other events are
601 // handled normally.
602
603 gint gtk_window_event_event_callback( GtkWidget *widget,
604 GdkEventExpose *event,
605 wxWindow *win )
606 {
607 if (event->type == GDK_EXPOSE)
608 {
609 gint ret = gtk_window_expose_callback( widget, event, win );
610 return ret;
611 }
612
613 return FALSE;
614 }
615
616 //-----------------------------------------------------------------------------
617 // "draw" of m_wxwindow
618 //-----------------------------------------------------------------------------
619
620 #ifndef __WXGTK20__
621
622 // This callback is a complete replacement of the gtk_pizza_draw() function,
623 // which is disabled.
624
625 static void gtk_window_draw_callback( GtkWidget *widget,
626 GdkRectangle *rect,
627 wxWindow *win )
628 {
629 DEBUG_MAIN_THREAD
630
631 if (g_isIdle)
632 wxapp_install_idle_handler();
633
634 // The wxNO_FULL_REPAINT_ON_RESIZE flag only works if
635 // there are no child windows.
636 if ((win->HasFlag(wxNO_FULL_REPAINT_ON_RESIZE)) &&
637 (win->GetChildren().GetCount() == 0))
638 {
639 return;
640 }
641
642 #if 0
643 if (win->GetName())
644 {
645 wxPrintf( wxT("OnDraw from ") );
646 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
647 wxPrintf( win->GetClassInfo()->GetClassName() );
648 wxPrintf( wxT(" %d %d %d %d\n"), (int)rect->x,
649 (int)rect->y,
650 (int)rect->width,
651 (int)rect->height );
652 }
653 #endif
654
655 #ifndef __WXUNIVERSAL__
656 GtkPizza *pizza = GTK_PIZZA (widget);
657
658 if (win->GetThemeEnabled())
659 {
660 wxWindow *parent = win->GetParent();
661 while (parent && !parent->IsTopLevel())
662 parent = parent->GetParent();
663 if (!parent)
664 parent = win;
665
666 gtk_paint_flat_box (parent->m_widget->style,
667 pizza->bin_window,
668 GTK_STATE_NORMAL,
669 GTK_SHADOW_NONE,
670 rect,
671 parent->m_widget,
672 (char *)"base",
673 0, 0, -1, -1);
674 }
675
676
677 if (!(GTK_WIDGET_APP_PAINTABLE (widget)) &&
678 (pizza->clear_on_draw))
679 {
680 gdk_window_clear_area( pizza->bin_window,
681 rect->x, rect->y, rect->width, rect->height);
682 }
683 #endif
684
685 win->GetUpdateRegion().Union( rect->x, rect->y, rect->width, rect->height );
686
687 // Actual redrawing takes place in idle time.
688
689 win->Update();
690
691 #ifndef __WXUNIVERSAL__
692 // Redraw child widgets
693 GList *children = pizza->children;
694 while (children)
695 {
696 GtkPizzaChild *child = (GtkPizzaChild*) children->data;
697 children = children->next;
698
699 GdkRectangle child_area;
700 if (gtk_widget_intersect (child->widget, rect, &child_area))
701 {
702 gtk_widget_draw (child->widget, &child_area /* (GdkRectangle*) NULL*/ );
703 }
704 }
705 #endif
706 }
707
708 #endif
709
710 //-----------------------------------------------------------------------------
711 // "key_press_event" from any window
712 //-----------------------------------------------------------------------------
713
714 // set WXTRACE to this to see the key event codes on the console
715 #define TRACE_KEYS _T("keyevent")
716
717 // translates an X key symbol to WXK_XXX value
718 //
719 // if isChar is true it means that the value returned will be used for EVT_CHAR
720 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
721 // for example, while if it is false it means that the value is going to be
722 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
723 // WXK_NUMPAD_DIVIDE
724 static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar)
725 {
726 long key_code;
727
728 switch ( keysym )
729 {
730 // Shift, Control and Alt don't generate the CHAR events at all
731 case GDK_Shift_L:
732 case GDK_Shift_R:
733 key_code = isChar ? 0 : WXK_SHIFT;
734 break;
735 case GDK_Control_L:
736 case GDK_Control_R:
737 key_code = isChar ? 0 : WXK_CONTROL;
738 break;
739 case GDK_Meta_L:
740 case GDK_Meta_R:
741 case GDK_Alt_L:
742 case GDK_Alt_R:
743 case GDK_Super_L:
744 case GDK_Super_R:
745 key_code = isChar ? 0 : WXK_ALT;
746 break;
747
748 // neither do the toggle modifies
749 case GDK_Scroll_Lock:
750 key_code = isChar ? 0 : WXK_SCROLL;
751 break;
752
753 case GDK_Caps_Lock:
754 key_code = isChar ? 0 : WXK_CAPITAL;
755 break;
756
757 case GDK_Num_Lock:
758 key_code = isChar ? 0 : WXK_NUMLOCK;
759 break;
760
761
762 // various other special keys
763 case GDK_Menu:
764 key_code = WXK_MENU;
765 break;
766
767 case GDK_Help:
768 key_code = WXK_HELP;
769 break;
770
771 case GDK_BackSpace:
772 key_code = WXK_BACK;
773 break;
774
775 case GDK_ISO_Left_Tab:
776 case GDK_Tab:
777 key_code = WXK_TAB;
778 break;
779
780 case GDK_Linefeed:
781 case GDK_Return:
782 key_code = WXK_RETURN;
783 break;
784
785 case GDK_Clear:
786 key_code = WXK_CLEAR;
787 break;
788
789 case GDK_Pause:
790 key_code = WXK_PAUSE;
791 break;
792
793 case GDK_Select:
794 key_code = WXK_SELECT;
795 break;
796
797 case GDK_Print:
798 key_code = WXK_PRINT;
799 break;
800
801 case GDK_Execute:
802 key_code = WXK_EXECUTE;
803 break;
804
805 case GDK_Escape:
806 key_code = WXK_ESCAPE;
807 break;
808
809 // cursor and other extended keyboard keys
810 case GDK_Delete:
811 key_code = WXK_DELETE;
812 break;
813
814 case GDK_Home:
815 key_code = WXK_HOME;
816 break;
817
818 case GDK_Left:
819 key_code = WXK_LEFT;
820 break;
821
822 case GDK_Up:
823 key_code = WXK_UP;
824 break;
825
826 case GDK_Right:
827 key_code = WXK_RIGHT;
828 break;
829
830 case GDK_Down:
831 key_code = WXK_DOWN;
832 break;
833
834 case GDK_Prior: // == GDK_Page_Up
835 key_code = WXK_PRIOR;
836 break;
837
838 case GDK_Next: // == GDK_Page_Down
839 key_code = WXK_NEXT;
840 break;
841
842 case GDK_End:
843 key_code = WXK_END;
844 break;
845
846 case GDK_Begin:
847 key_code = WXK_HOME;
848 break;
849
850 case GDK_Insert:
851 key_code = WXK_INSERT;
852 break;
853
854
855 // numpad keys
856 case GDK_KP_0:
857 case GDK_KP_1:
858 case GDK_KP_2:
859 case GDK_KP_3:
860 case GDK_KP_4:
861 case GDK_KP_5:
862 case GDK_KP_6:
863 case GDK_KP_7:
864 case GDK_KP_8:
865 case GDK_KP_9:
866 key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0;
867 break;
868
869 case GDK_KP_Space:
870 key_code = isChar ? ' ' : WXK_NUMPAD_SPACE;
871 break;
872
873 case GDK_KP_Tab:
874 key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB;
875 break;
876
877 case GDK_KP_Enter:
878 key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER;
879 break;
880
881 case GDK_KP_F1:
882 key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1;
883 break;
884
885 case GDK_KP_F2:
886 key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2;
887 break;
888
889 case GDK_KP_F3:
890 key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3;
891 break;
892
893 case GDK_KP_F4:
894 key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4;
895 break;
896
897 case GDK_KP_Home:
898 key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME;
899 break;
900
901 case GDK_KP_Left:
902 key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT;
903 break;
904
905 case GDK_KP_Up:
906 key_code = isChar ? WXK_UP : WXK_NUMPAD_UP;
907 break;
908
909 case GDK_KP_Right:
910 key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT;
911 break;
912
913 case GDK_KP_Down:
914 key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN;
915 break;
916
917 case GDK_KP_Prior: // == GDK_KP_Page_Up
918 key_code = isChar ? WXK_PRIOR : WXK_NUMPAD_PRIOR;
919 break;
920
921 case GDK_KP_Next: // == GDK_KP_Page_Down
922 key_code = isChar ? WXK_NEXT : WXK_NUMPAD_NEXT;
923 break;
924
925 case GDK_KP_End:
926 key_code = isChar ? WXK_END : WXK_NUMPAD_END;
927 break;
928
929 case GDK_KP_Begin:
930 key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN;
931 break;
932
933 case GDK_KP_Insert:
934 key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT;
935 break;
936
937 case GDK_KP_Delete:
938 key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE;
939 break;
940
941 case GDK_KP_Equal:
942 key_code = isChar ? '=' : WXK_NUMPAD_EQUAL;
943 break;
944
945 case GDK_KP_Multiply:
946 key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY;
947 break;
948
949 case GDK_KP_Add:
950 key_code = isChar ? '+' : WXK_NUMPAD_ADD;
951 break;
952
953 case GDK_KP_Separator:
954 // FIXME: what is this?
955 key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR;
956 break;
957
958 case GDK_KP_Subtract:
959 key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT;
960 break;
961
962 case GDK_KP_Decimal:
963 key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL;
964 break;
965
966 case GDK_KP_Divide:
967 key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE;
968 break;
969
970
971 // function keys
972 case GDK_F1:
973 case GDK_F2:
974 case GDK_F3:
975 case GDK_F4:
976 case GDK_F5:
977 case GDK_F6:
978 case GDK_F7:
979 case GDK_F8:
980 case GDK_F9:
981 case GDK_F10:
982 case GDK_F11:
983 case GDK_F12:
984 key_code = WXK_F1 + keysym - GDK_F1;
985 break;
986
987 default:
988 key_code = 0;
989 }
990
991 return key_code;
992 }
993
994 static inline bool wxIsAsciiKeysym(KeySym ks)
995 {
996 return ks < 256;
997 }
998
999 static bool
1000 wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
1001 wxWindowGTK *win,
1002 GdkEventKey *gdk_event)
1003 {
1004 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
1005 // but only event->keyval which is quite useless to us, so remember
1006 // the last character from GDK_KEY_PRESS and reuse it as last resort
1007 //
1008 // NB: should be MT-safe as we're always called from the main thread only
1009 static struct
1010 {
1011 KeySym keysym;
1012 long keycode;
1013 } s_lastKeyPress = { 0, 0 };
1014
1015 KeySym keysym = gdk_event->keyval;
1016
1017 wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %d"),
1018 event.GetEventType() == wxEVT_KEY_UP ? _T("release")
1019 : _T("press"),
1020 keysym);
1021
1022 long key_code = wxTranslateKeySymToWXKey(keysym, FALSE /* !isChar */);
1023
1024 if ( !key_code )
1025 {
1026 // do we have the translation or is it a plain ASCII character?
1027 if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) )
1028 {
1029 // we should use keysym if it is ASCII as X does some translations
1030 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
1031 // which we don't want here (but which we do use for OnChar())
1032 if ( !wxIsAsciiKeysym(keysym) )
1033 {
1034 keysym = (KeySym)gdk_event->string[0];
1035 }
1036
1037 // we want to always get the same key code when the same key is
1038 // pressed regardless of the state of the modifies, i.e. on a
1039 // standard US keyboard pressing '5' or '%' ('5' key with
1040 // Shift) should result in the same key code in OnKeyDown():
1041 // '5' (although OnChar() will get either '5' or '%').
1042 //
1043 // to do it we first translate keysym to keycode (== scan code)
1044 // and then back but always using the lower register
1045 Display *dpy = (Display *)wxGetDisplay();
1046 KeyCode keycode = XKeysymToKeycode(dpy, keysym);
1047
1048 wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode);
1049
1050 KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
1051
1052 // use the normalized, i.e. lower register, keysym if we've
1053 // got one
1054 key_code = keysymNormalized ? keysymNormalized : keysym;
1055
1056 // as explained above, we want to have lower register key codes
1057 // normally but for the letter keys we want to have the upper ones
1058 //
1059 // NB: don't use XConvertCase() here, we want to do it for letters
1060 // only
1061 key_code = toupper(key_code);
1062 }
1063 else // non ASCII key, what to do?
1064 {
1065 // by default, ignore it
1066 key_code = 0;
1067
1068 // but if we have cached information from the last KEY_PRESS
1069 if ( gdk_event->type == GDK_KEY_RELEASE )
1070 {
1071 // then reuse it
1072 if ( keysym == s_lastKeyPress.keysym )
1073 {
1074 key_code = s_lastKeyPress.keycode;
1075 }
1076 }
1077 }
1078
1079 if ( gdk_event->type == GDK_KEY_PRESS )
1080 {
1081 // remember it to be reused for KEY_UP event later
1082 s_lastKeyPress.keysym = keysym;
1083 s_lastKeyPress.keycode = key_code;
1084 }
1085 }
1086
1087 wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %d"), key_code);
1088
1089 // sending unknown key events doesn't really make sense
1090 if ( !key_code )
1091 return FALSE;
1092
1093 // now fill all the other fields
1094 int x = 0;
1095 int y = 0;
1096 GdkModifierType state;
1097 if (gdk_event->window)
1098 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1099
1100 event.SetTimestamp( gdk_event->time );
1101 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
1102 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
1103 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
1104 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
1105 event.m_keyCode = key_code;
1106 event.m_scanCode = gdk_event->keyval;
1107 event.m_rawCode = (wxUint32) gdk_event->keyval;
1108 event.m_rawFlags = 0;
1109 event.m_x = x;
1110 event.m_y = y;
1111 event.SetEventObject( win );
1112
1113 return TRUE;
1114 }
1115
1116 static gint gtk_window_key_press_callback( GtkWidget *widget,
1117 GdkEventKey *gdk_event,
1118 wxWindow *win )
1119 {
1120 DEBUG_MAIN_THREAD
1121
1122 if (g_isIdle)
1123 wxapp_install_idle_handler();
1124
1125 if (!win->m_hasVMT)
1126 return FALSE;
1127 if (g_blockEventsOnDrag)
1128 return FALSE;
1129
1130 wxKeyEvent event( wxEVT_KEY_DOWN );
1131 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1132 {
1133 // unknown key pressed, ignore (the event would be useless anyhow)
1134 return FALSE;
1135 }
1136
1137 bool ret = win->GetEventHandler()->ProcessEvent( event );
1138
1139 #if wxUSE_ACCEL
1140 if (!ret)
1141 {
1142 wxWindowGTK *ancestor = win;
1143 while (ancestor)
1144 {
1145 int command = ancestor->GetAcceleratorTable()->GetCommand( event );
1146 if (command != -1)
1147 {
1148 wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
1149 ret = ancestor->GetEventHandler()->ProcessEvent( command_event );
1150 break;
1151 }
1152 if (ancestor->IsTopLevel())
1153 break;
1154 ancestor = ancestor->GetParent();
1155 }
1156 }
1157 #endif // wxUSE_ACCEL
1158
1159 /* Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1160 will only be sent if it is not in an accelerator table. */
1161 if ( !ret )
1162 {
1163 KeySym keysym = gdk_event->keyval;
1164 long key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */);
1165 if ( !key_code )
1166 {
1167 if ( gdk_event->length == 1 )
1168 {
1169 key_code = (unsigned char)gdk_event->string[0];
1170 }
1171 else if ( wxIsAsciiKeysym(keysym) )
1172 {
1173 // ASCII key
1174 key_code = (unsigned char)keysym;
1175 }
1176 }
1177
1178 if ( key_code )
1179 {
1180 wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code);
1181
1182 // reuse the same event object, just change its type and use the
1183 // translated keycode instead of the raw one
1184 event.SetEventType(wxEVT_CHAR);
1185 event.m_keyCode = key_code;
1186
1187 ret = win->GetEventHandler()->ProcessEvent( event );
1188 }
1189 }
1190
1191 /* win is a control: tab can be propagated up */
1192 if ( !ret &&
1193 ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
1194 // VZ: testing for wxTE_PROCESS_TAB shouldn't be done here the control may
1195 // have this style, yet choose not to process this particular TAB in which
1196 // case TAB must still work as a navigational character
1197 #if 0
1198 !win->HasFlag(wxTE_PROCESS_TAB) &&
1199 #endif // 0
1200 win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
1201 {
1202 wxNavigationKeyEvent new_event;
1203 new_event.SetEventObject( win->GetParent() );
1204 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
1205 new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
1206 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
1207 new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) );
1208 new_event.SetCurrentFocus( win );
1209 ret = win->GetParent()->GetEventHandler()->ProcessEvent( new_event );
1210 }
1211
1212 /* generate wxID_CANCEL if <esc> has been pressed (typically in dialogs) */
1213 if ( !ret &&
1214 (gdk_event->keyval == GDK_Escape) )
1215 {
1216 wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL);
1217 new_event.SetEventObject( win );
1218 ret = win->GetEventHandler()->ProcessEvent( new_event );
1219 }
1220
1221 /* Doesn't work. */
1222 #if 0 // (GTK_MINOR_VERSION > 0)
1223 /* Pressing F10 will activate the menu bar of the top frame. */
1224 if ( (!ret) &&
1225 (gdk_event->keyval == GDK_F10) )
1226 {
1227 wxWindowGTK *ancestor = win;
1228 while (ancestor)
1229 {
1230 if (wxIsKindOf(ancestor,wxFrame))
1231 {
1232 wxFrame *frame = (wxFrame*) ancestor;
1233 wxMenuBar *menubar = frame->GetMenuBar();
1234 if (menubar)
1235 {
1236 wxNode *node = menubar->GetMenus().First();
1237 if (node)
1238 {
1239 wxMenu *firstMenu = (wxMenu*) node->Data();
1240 gtk_menu_item_select( GTK_MENU_ITEM(firstMenu->m_owner) );
1241 ret = TRUE;
1242 break;
1243 }
1244 }
1245 }
1246 ancestor = ancestor->GetParent();
1247 }
1248 }
1249 #endif // 0
1250
1251 if (ret)
1252 {
1253 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
1254 return TRUE;
1255 }
1256
1257 return FALSE;
1258 }
1259
1260 //-----------------------------------------------------------------------------
1261 // "key_release_event" from any window
1262 //-----------------------------------------------------------------------------
1263
1264 static gint gtk_window_key_release_callback( GtkWidget *widget,
1265 GdkEventKey *gdk_event,
1266 wxWindowGTK *win )
1267 {
1268 DEBUG_MAIN_THREAD
1269
1270 if (g_isIdle)
1271 wxapp_install_idle_handler();
1272
1273 if (!win->m_hasVMT)
1274 return FALSE;
1275
1276 if (g_blockEventsOnDrag)
1277 return FALSE;
1278
1279 wxKeyEvent event( wxEVT_KEY_UP );
1280 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1281 {
1282 // unknown key pressed, ignore (the event would be useless anyhow
1283 return FALSE;
1284 }
1285
1286 if ( !win->GetEventHandler()->ProcessEvent( event ) )
1287 return FALSE;
1288
1289 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" );
1290 return TRUE;
1291 }
1292
1293 // ============================================================================
1294 // the mouse events
1295 // ============================================================================
1296
1297 // init wxMouseEvent with the info from gdk_event
1298 #define InitMouseEvent(win, event, gdk_event) \
1299 { \
1300 event.SetTimestamp( gdk_event->time ); \
1301 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); \
1302 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); \
1303 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); \
1304 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); \
1305 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK); \
1306 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK); \
1307 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK); \
1308 \
1309 wxPoint pt = win->GetClientAreaOrigin(); \
1310 event.m_x = (wxCoord)gdk_event->x - pt.x; \
1311 event.m_y = (wxCoord)gdk_event->y - pt.y; \
1312 }
1313
1314 // ----------------------------------------------------------------------------
1315 // mouse event processing helper
1316 // ----------------------------------------------------------------------------
1317
1318 static void AdjustEventButtonState(wxMouseEvent& event)
1319 {
1320 // GDK reports the old state of the button for a button press event, but
1321 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1322 // for a LEFT_DOWN event, not FALSE, so we will invert
1323 // left/right/middleDown for the corresponding click events
1324
1325 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1326 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1327 (event.GetEventType() == wxEVT_LEFT_UP))
1328 {
1329 event.m_leftDown = !event.m_leftDown;
1330 return;
1331 }
1332
1333 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1334 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1335 (event.GetEventType() == wxEVT_MIDDLE_UP))
1336 {
1337 event.m_middleDown = !event.m_middleDown;
1338 return;
1339 }
1340
1341 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1342 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1343 (event.GetEventType() == wxEVT_RIGHT_UP))
1344 {
1345 event.m_rightDown = !event.m_rightDown;
1346 return;
1347 }
1348 }
1349
1350 //-----------------------------------------------------------------------------
1351 // "button_press_event"
1352 //-----------------------------------------------------------------------------
1353
1354 static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win )
1355 {
1356 DEBUG_MAIN_THREAD
1357
1358 if (g_isIdle)
1359 wxapp_install_idle_handler();
1360
1361 /*
1362 wxPrintf( wxT("1) OnButtonPress from ") );
1363 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1364 wxPrintf( win->GetClassInfo()->GetClassName() );
1365 wxPrintf( wxT(".\n") );
1366 */
1367 if (!win->m_hasVMT) return FALSE;
1368 if (g_blockEventsOnDrag) return TRUE;
1369 if (g_blockEventsOnScroll) return TRUE;
1370
1371 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1372
1373 if (win->m_wxwindow && (g_focusWindow != win) && win->AcceptsFocus())
1374 {
1375 gtk_widget_grab_focus( win->m_wxwindow );
1376 /*
1377 wxPrintf( wxT("GrabFocus from ") );
1378 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1379 wxPrintf( win->GetClassInfo()->GetClassName() );
1380 wxPrintf( wxT(".\n") );
1381 */
1382 }
1383
1384 wxEventType event_type = wxEVT_NULL;
1385
1386 if (gdk_event->button == 1)
1387 {
1388 switch (gdk_event->type)
1389 {
1390 case GDK_BUTTON_PRESS: event_type = wxEVT_LEFT_DOWN; break;
1391 case GDK_2BUTTON_PRESS: event_type = wxEVT_LEFT_DCLICK; break;
1392 default: break;
1393 }
1394 }
1395 else if (gdk_event->button == 2)
1396 {
1397 switch (gdk_event->type)
1398 {
1399 case GDK_BUTTON_PRESS: event_type = wxEVT_MIDDLE_DOWN; break;
1400 case GDK_2BUTTON_PRESS: event_type = wxEVT_MIDDLE_DCLICK; break;
1401 default: break;
1402 }
1403 }
1404 else if (gdk_event->button == 3)
1405 {
1406 switch (gdk_event->type)
1407 {
1408 case GDK_BUTTON_PRESS: event_type = wxEVT_RIGHT_DOWN; break;
1409 case GDK_2BUTTON_PRESS: event_type = wxEVT_RIGHT_DCLICK; break;
1410 default: break;
1411 }
1412 }
1413
1414 if ( event_type == wxEVT_NULL )
1415 {
1416 // unknown mouse button or click type
1417 return FALSE;
1418 }
1419
1420 wxMouseEvent event( event_type );
1421 InitMouseEvent( win, event, gdk_event );
1422
1423 AdjustEventButtonState(event);
1424
1425 // wxListBox actually get mouse events from the item
1426
1427 if (win->m_isListBox)
1428 {
1429 event.m_x += widget->allocation.x;
1430 event.m_y += widget->allocation.y;
1431 }
1432
1433 // Some control don't have their own X window and thus cannot get
1434 // any events.
1435
1436 if (!g_captureWindow)
1437 {
1438 wxCoord x = event.m_x;
1439 wxCoord y = event.m_y;
1440 if (win->m_wxwindow)
1441 {
1442 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1443 x += pizza->xoffset;
1444 y += pizza->yoffset;
1445 }
1446
1447 wxNode *node = win->GetChildren().First();
1448 while (node)
1449 {
1450 wxWindowGTK *child = (wxWindowGTK*)node->Data();
1451
1452 node = node->Next();
1453 if (!child->IsShown())
1454 continue;
1455
1456 if (child->m_isStaticBox)
1457 {
1458 // wxStaticBox is transparent in the box itself
1459 int xx1 = child->m_x;
1460 int yy1 = child->m_y;
1461 int xx2 = child->m_x + child->m_width;
1462 int yy2 = child->m_x + child->m_height;
1463
1464 // left
1465 if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
1466 // right
1467 ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
1468 // top
1469 ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
1470 // bottom
1471 ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
1472 {
1473 win = child;
1474 event.m_x -= child->m_x;
1475 event.m_y -= child->m_y;
1476 break;
1477 }
1478
1479 }
1480 else
1481 {
1482 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1483 (child->m_x <= x) &&
1484 (child->m_y <= y) &&
1485 (child->m_x+child->m_width >= x) &&
1486 (child->m_y+child->m_height >= y))
1487 {
1488 win = child;
1489 event.m_x -= child->m_x;
1490 event.m_y -= child->m_y;
1491 break;
1492 }
1493 }
1494 }
1495 }
1496
1497 event.SetEventObject( win );
1498
1499 gs_timeLastClick = gdk_event->time;
1500
1501 /*
1502 wxPrintf( wxT("2) OnButtonPress from ") );
1503 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1504 wxPrintf( win->GetClassInfo()->GetClassName() );
1505 wxPrintf( wxT(".\n") );
1506 */
1507
1508 if (win->GetEventHandler()->ProcessEvent( event ))
1509 {
1510 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_press_event" );
1511 return TRUE;
1512 }
1513
1514 return FALSE;
1515 }
1516
1517 //-----------------------------------------------------------------------------
1518 // "button_release_event"
1519 //-----------------------------------------------------------------------------
1520
1521 static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win )
1522 {
1523 DEBUG_MAIN_THREAD
1524
1525 if (g_isIdle)
1526 wxapp_install_idle_handler();
1527
1528 if (!win->m_hasVMT) return FALSE;
1529 if (g_blockEventsOnDrag) return FALSE;
1530 if (g_blockEventsOnScroll) return FALSE;
1531
1532 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1533
1534 /*
1535 printf( "OnButtonRelease from " );
1536 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1537 printf( win->GetClassInfo()->GetClassName() );
1538 printf( ".\n" );
1539 */
1540
1541 wxEventType event_type = wxEVT_NULL;
1542
1543 switch (gdk_event->button)
1544 {
1545 case 1: event_type = wxEVT_LEFT_UP; break;
1546 case 2: event_type = wxEVT_MIDDLE_UP; break;
1547 case 3: event_type = wxEVT_RIGHT_UP; break;
1548 default: return FALSE;
1549 }
1550
1551 wxMouseEvent event( event_type );
1552 InitMouseEvent( win, event, gdk_event );
1553
1554 AdjustEventButtonState(event);
1555
1556 // wxListBox actually get mouse events from the item
1557
1558 if (win->m_isListBox)
1559 {
1560 event.m_x += widget->allocation.x;
1561 event.m_y += widget->allocation.y;
1562 }
1563
1564 // Some control don't have their own X window and thus cannot get
1565 // any events.
1566
1567 if (!g_captureWindow)
1568 {
1569 wxCoord x = event.m_x;
1570 wxCoord y = event.m_y;
1571 if (win->m_wxwindow)
1572 {
1573 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1574 x += pizza->xoffset;
1575 y += pizza->yoffset;
1576 }
1577
1578 wxNode *node = win->GetChildren().First();
1579 while (node)
1580 {
1581 wxWindowGTK *child = (wxWindowGTK*)node->Data();
1582
1583 node = node->Next();
1584 if (!child->IsShown())
1585 continue;
1586
1587 if (child->m_isStaticBox)
1588 {
1589 // wxStaticBox is transparent in the box itself
1590 int xx1 = child->m_x;
1591 int yy1 = child->m_y;
1592 int xx2 = child->m_x + child->m_width;
1593 int yy2 = child->m_x + child->m_height;
1594
1595 // left
1596 if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
1597 // right
1598 ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
1599 // top
1600 ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
1601 // bottom
1602 ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
1603 {
1604 win = child;
1605 event.m_x -= child->m_x;
1606 event.m_y -= child->m_y;
1607 break;
1608 }
1609
1610 }
1611 else
1612 {
1613 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1614 (child->m_x <= x) &&
1615 (child->m_y <= y) &&
1616 (child->m_x+child->m_width >= x) &&
1617 (child->m_y+child->m_height >= y))
1618 {
1619 win = child;
1620 event.m_x -= child->m_x;
1621 event.m_y -= child->m_y;
1622 break;
1623 }
1624 }
1625 }
1626 }
1627
1628 event.SetEventObject( win );
1629
1630 if (win->GetEventHandler()->ProcessEvent( event ))
1631 {
1632 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_release_event" );
1633 return TRUE;
1634 }
1635
1636 return FALSE;
1637 }
1638
1639 //-----------------------------------------------------------------------------
1640 // "motion_notify_event"
1641 //-----------------------------------------------------------------------------
1642
1643 static gint gtk_window_motion_notify_callback( GtkWidget *widget,
1644 GdkEventMotion *gdk_event,
1645 wxWindowGTK *win )
1646 {
1647 DEBUG_MAIN_THREAD
1648
1649 if (g_isIdle)
1650 wxapp_install_idle_handler();
1651
1652 if (!win->m_hasVMT) return FALSE;
1653 if (g_blockEventsOnDrag) return FALSE;
1654 if (g_blockEventsOnScroll) return FALSE;
1655
1656 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1657
1658 if (gdk_event->is_hint)
1659 {
1660 int x = 0;
1661 int y = 0;
1662 GdkModifierType state;
1663 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1664 gdk_event->x = x;
1665 gdk_event->y = y;
1666 }
1667
1668 /*
1669 printf( "OnMotion from " );
1670 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1671 printf( win->GetClassInfo()->GetClassName() );
1672 printf( ".\n" );
1673 */
1674
1675 wxMouseEvent event( wxEVT_MOTION );
1676 InitMouseEvent(win, event, gdk_event);
1677
1678 if ( g_captureWindow )
1679 {
1680 // synthetize a mouse enter or leave event if needed
1681 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1682 bool hasMouse = winUnderMouse == gdk_event->window;
1683 if ( hasMouse != g_captureWindowHasMouse )
1684 {
1685 // the mouse changed window
1686 g_captureWindowHasMouse = hasMouse;
1687
1688 wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1689 : wxEVT_LEAVE_WINDOW);
1690 InitMouseEvent(win, event, gdk_event);
1691 event.SetEventObject(win);
1692 win->GetEventHandler()->ProcessEvent(event);
1693 }
1694 }
1695 else // no capture
1696 {
1697 // Some control don't have their own X window and thus cannot get
1698 // any events.
1699
1700 wxCoord x = event.m_x;
1701 wxCoord y = event.m_y;
1702 if (win->m_wxwindow)
1703 {
1704 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1705 x += pizza->xoffset;
1706 y += pizza->yoffset;
1707 }
1708
1709 wxNode *node = win->GetChildren().First();
1710 while (node)
1711 {
1712 wxWindowGTK *child = (wxWindowGTK*)node->Data();
1713
1714 node = node->Next();
1715 if (!child->IsShown())
1716 continue;
1717
1718 if (child->m_isStaticBox)
1719 {
1720 // wxStaticBox is transparent in the box itself
1721 int xx1 = child->m_x;
1722 int yy1 = child->m_y;
1723 int xx2 = child->m_x + child->m_width;
1724 int yy2 = child->m_x + child->m_height;
1725
1726 // left
1727 if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
1728 // right
1729 ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
1730 // top
1731 ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
1732 // bottom
1733 ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
1734 {
1735 win = child;
1736 event.m_x -= child->m_x;
1737 event.m_y -= child->m_y;
1738 break;
1739 }
1740
1741 }
1742 else
1743 {
1744 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1745 (child->m_x <= x) &&
1746 (child->m_y <= y) &&
1747 (child->m_x+child->m_width >= x) &&
1748 (child->m_y+child->m_height >= y))
1749 {
1750 win = child;
1751 event.m_x -= child->m_x;
1752 event.m_y -= child->m_y;
1753 break;
1754 }
1755 }
1756 }
1757 }
1758
1759 event.SetEventObject( win );
1760
1761 if (win->GetEventHandler()->ProcessEvent( event ))
1762 {
1763 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" );
1764 return TRUE;
1765 }
1766
1767 return FALSE;
1768 }
1769
1770 //-----------------------------------------------------------------------------
1771 // "focus_in_event"
1772 //-----------------------------------------------------------------------------
1773
1774 static gint gtk_window_focus_in_callback( GtkWidget *widget,
1775 GdkEvent *WXUNUSED(event),
1776 wxWindow *win )
1777 {
1778 DEBUG_MAIN_THREAD
1779
1780 if (g_isIdle)
1781 wxapp_install_idle_handler();
1782
1783 if (!win->m_hasVMT) return FALSE;
1784 if (g_blockEventsOnDrag) return FALSE;
1785
1786 switch ( g_sendActivateEvent )
1787 {
1788 case -1:
1789 // we've got focus from outside, synthetize wxActivateEvent
1790 g_sendActivateEvent = 1;
1791 break;
1792
1793 case 0:
1794 // another our window just lost focus, it was already ours before
1795 // - don't send any wxActivateEvent
1796 g_sendActivateEvent = -1;
1797 break;
1798 }
1799
1800 g_focusWindowLast =
1801 g_focusWindow = win;
1802
1803 #if 0
1804 printf( "OnSetFocus 2 from %s\n", win->GetName().c_str() );
1805 #endif
1806
1807 // Notify the parent keeping track of focus for the kbd navigation
1808 // purposes that we got it.
1809 wxChildFocusEvent eventFocus(win);
1810 (void)win->GetEventHandler()->ProcessEvent(eventFocus);
1811
1812 #ifdef HAVE_XIM
1813 if (win->m_ic)
1814 gdk_im_begin(win->m_ic, win->m_wxwindow->window);
1815 #endif
1816
1817 #if wxUSE_CARET
1818 // caret needs to be informed about focus change
1819 wxCaret *caret = win->GetCaret();
1820 if ( caret )
1821 {
1822 caret->OnSetFocus();
1823 }
1824 #endif // wxUSE_CARET
1825
1826 g_activeFrameLostFocus = FALSE;
1827
1828 wxWindowGTK *active = wxGetTopLevelParent(win);
1829 if ( active != g_activeFrame )
1830 {
1831 if ( g_activeFrame )
1832 {
1833 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from focus_in)"), g_activeFrame);
1834 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
1835 event.SetEventObject(g_activeFrame);
1836 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1837 }
1838
1839 wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), active);
1840 g_activeFrame = active;
1841 wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId());
1842 event.SetEventObject(g_activeFrame);
1843 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1844
1845 // Don't send focus events in addition to activate
1846 // if (win == g_activeFrame)
1847 // return TRUE;
1848 }
1849
1850
1851 wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() );
1852 event.SetEventObject( win );
1853
1854 if (win->GetEventHandler()->ProcessEvent( event ))
1855 {
1856 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
1857 return TRUE;
1858 }
1859
1860 return FALSE;
1861 }
1862
1863 //-----------------------------------------------------------------------------
1864 // "focus_out_event"
1865 //-----------------------------------------------------------------------------
1866
1867 static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
1868 {
1869 DEBUG_MAIN_THREAD
1870
1871 if (g_isIdle)
1872 wxapp_install_idle_handler();
1873
1874 if (!win->m_hasVMT) return FALSE;
1875 if (g_blockEventsOnDrag) return FALSE;
1876
1877 #if 0
1878 wxLogDebug( wxT("OnKillFocus from %s"), win->GetName().c_str() );
1879 #endif
1880
1881 if ( !g_activeFrameLostFocus && g_activeFrame )
1882 {
1883 // VZ: commenting this out because it does happen (although not easy
1884 // to reproduce, I only see it when using wxMiniFrame and not
1885 // always) and makes using Mahogany quite annoying
1886 #if 0
1887 wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame,
1888 wxT("unfocusing window that hasn't gained focus properly") )
1889 #endif // 0
1890
1891 g_activeFrameLostFocus = TRUE;
1892 }
1893
1894 // if the focus goes out of our app alltogether, OnIdle() will send
1895 // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
1896 // g_sendActivateEvent to -1
1897 g_sendActivateEvent = 0;
1898
1899 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1900 if ( winFocus )
1901 win = winFocus;
1902
1903 g_focusWindow = (wxWindowGTK *)NULL;
1904
1905 #ifdef HAVE_XIM
1906 if (win->m_ic)
1907 gdk_im_end();
1908 #endif
1909
1910 #if wxUSE_CARET
1911 // caret needs to be informed about focus change
1912 wxCaret *caret = win->GetCaret();
1913 if ( caret )
1914 {
1915 caret->OnKillFocus();
1916 }
1917 #endif // wxUSE_CARET
1918
1919 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1920 event.SetEventObject( win );
1921
1922 if (win->GetEventHandler()->ProcessEvent( event ))
1923 {
1924 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
1925 return TRUE;
1926 }
1927
1928 return FALSE;
1929 }
1930
1931 //-----------------------------------------------------------------------------
1932 // "enter_notify_event"
1933 //-----------------------------------------------------------------------------
1934
1935 static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win )
1936 {
1937 DEBUG_MAIN_THREAD
1938
1939 if (g_isIdle)
1940 wxapp_install_idle_handler();
1941
1942 if (!win->m_hasVMT) return FALSE;
1943 if (g_blockEventsOnDrag) return FALSE;
1944
1945 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1946
1947 wxMouseEvent event( wxEVT_ENTER_WINDOW );
1948 event.SetTimestamp( gdk_event->time );
1949 event.SetEventObject( win );
1950
1951 int x = 0;
1952 int y = 0;
1953 GdkModifierType state = (GdkModifierType)0;
1954
1955 gdk_window_get_pointer( widget->window, &x, &y, &state );
1956
1957 InitMouseEvent(win, event, gdk_event);
1958 wxPoint pt = win->GetClientAreaOrigin();
1959 event.m_x = x + pt.x;
1960 event.m_y = y + pt.y;
1961
1962 if (win->GetEventHandler()->ProcessEvent( event ))
1963 {
1964 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "enter_notify_event" );
1965 return TRUE;
1966 }
1967
1968 return FALSE;
1969 }
1970
1971 //-----------------------------------------------------------------------------
1972 // "leave_notify_event"
1973 //-----------------------------------------------------------------------------
1974
1975 static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win )
1976 {
1977 DEBUG_MAIN_THREAD
1978
1979 if (g_isIdle)
1980 wxapp_install_idle_handler();
1981
1982 if (!win->m_hasVMT) return FALSE;
1983 if (g_blockEventsOnDrag) return FALSE;
1984
1985 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1986
1987 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
1988 event.SetTimestamp( gdk_event->time );
1989 event.SetEventObject( win );
1990
1991 int x = 0;
1992 int y = 0;
1993 GdkModifierType state = (GdkModifierType)0;
1994
1995 gdk_window_get_pointer( widget->window, &x, &y, &state );
1996
1997 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
1998 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
1999 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
2000 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
2001 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
2002 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
2003 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
2004
2005 wxPoint pt = win->GetClientAreaOrigin();
2006 event.m_x = x + pt.x;
2007 event.m_y = y + pt.y;
2008
2009 if (win->GetEventHandler()->ProcessEvent( event ))
2010 {
2011 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "leave_notify_event" );
2012 return TRUE;
2013 }
2014
2015 return FALSE;
2016 }
2017
2018 //-----------------------------------------------------------------------------
2019 // "value_changed" from m_vAdjust
2020 //-----------------------------------------------------------------------------
2021
2022 static void gtk_window_vscroll_callback( GtkAdjustment *adjust,
2023 SCROLLBAR_CBACK_ARG
2024 wxWindowGTK *win )
2025 {
2026 DEBUG_MAIN_THREAD
2027
2028 if (g_isIdle)
2029 wxapp_install_idle_handler();
2030
2031 if (g_blockEventsOnDrag) return;
2032
2033 if (!win->m_hasVMT) return;
2034
2035 float diff = adjust->value - win->m_oldVerticalPos;
2036 if (fabs(diff) < 0.2) return;
2037
2038 win->m_oldVerticalPos = adjust->value;
2039
2040 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget));
2041
2042 int value = (int)(adjust->value+0.5);
2043
2044 wxScrollWinEvent event( command, value, wxVERTICAL );
2045 event.SetEventObject( win );
2046 win->GetEventHandler()->ProcessEvent( event );
2047 }
2048
2049 //-----------------------------------------------------------------------------
2050 // "value_changed" from m_hAdjust
2051 //-----------------------------------------------------------------------------
2052
2053 static void gtk_window_hscroll_callback( GtkAdjustment *adjust,
2054 SCROLLBAR_CBACK_ARG
2055 wxWindowGTK *win )
2056 {
2057 DEBUG_MAIN_THREAD
2058
2059 if (g_isIdle)
2060 wxapp_install_idle_handler();
2061
2062 if (g_blockEventsOnDrag) return;
2063 if (!win->m_hasVMT) return;
2064
2065 float diff = adjust->value - win->m_oldHorizontalPos;
2066 if (fabs(diff) < 0.2) return;
2067
2068 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget));
2069
2070 win->m_oldHorizontalPos = adjust->value;
2071
2072 int value = (int)(adjust->value+0.5);
2073
2074 wxScrollWinEvent event( command, value, wxHORIZONTAL );
2075 event.SetEventObject( win );
2076 win->GetEventHandler()->ProcessEvent( event );
2077 }
2078
2079 //-----------------------------------------------------------------------------
2080 // "button_press_event" from scrollbar
2081 //-----------------------------------------------------------------------------
2082
2083 static gint gtk_scrollbar_button_press_callback( GtkRange *widget,
2084 GdkEventButton *gdk_event,
2085 wxWindowGTK *win)
2086 {
2087 DEBUG_MAIN_THREAD
2088
2089 if (g_isIdle)
2090 wxapp_install_idle_handler();
2091
2092
2093 g_blockEventsOnScroll = TRUE;
2094
2095 // FIXME: there is no 'slider' field in GTK+ 2.0 any more
2096 #ifndef __WXGTK20__
2097 win->m_isScrolling = (gdk_event->window == widget->slider);
2098 #endif
2099
2100 return FALSE;
2101 }
2102
2103 //-----------------------------------------------------------------------------
2104 // "button_release_event" from scrollbar
2105 //-----------------------------------------------------------------------------
2106
2107 static gint gtk_scrollbar_button_release_callback( GtkRange *widget,
2108 GdkEventButton *WXUNUSED(gdk_event),
2109 wxWindowGTK *win)
2110 {
2111 DEBUG_MAIN_THREAD
2112
2113 // don't test here as we can release the mouse while being over
2114 // a different window than the slider
2115 //
2116 // if (gdk_event->window != widget->slider) return FALSE;
2117
2118 g_blockEventsOnScroll = FALSE;
2119
2120 if (win->m_isScrolling)
2121 {
2122 wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE;
2123 int value = -1;
2124 int dir = -1;
2125
2126 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget);
2127 if (widget == GTK_RANGE(scrolledWindow->hscrollbar))
2128 {
2129 value = (int)(win->m_hAdjust->value+0.5);
2130 dir = wxHORIZONTAL;
2131 }
2132 if (widget == GTK_RANGE(scrolledWindow->vscrollbar))
2133 {
2134 value = (int)(win->m_vAdjust->value+0.5);
2135 dir = wxVERTICAL;
2136 }
2137
2138 wxScrollWinEvent event( command, value, dir );
2139 event.SetEventObject( win );
2140 win->GetEventHandler()->ProcessEvent( event );
2141 }
2142
2143 win->m_isScrolling = FALSE;
2144
2145 return FALSE;
2146 }
2147
2148 // ----------------------------------------------------------------------------
2149 // this wxWindowBase function is implemented here (in platform-specific file)
2150 // because it is static and so couldn't be made virtual
2151 // ----------------------------------------------------------------------------
2152
2153 wxWindow *wxWindowBase::FindFocus()
2154 {
2155 // the cast is necessary when we compile in wxUniversal mode
2156 return (wxWindow *)g_focusWindow;
2157 }
2158
2159 //-----------------------------------------------------------------------------
2160 // "realize" from m_widget
2161 //-----------------------------------------------------------------------------
2162
2163 /* We cannot set colours and fonts before the widget has
2164 been realized, so we do this directly after realization. */
2165
2166 static gint
2167 gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win )
2168 {
2169 DEBUG_MAIN_THREAD
2170
2171 if (g_isIdle)
2172 wxapp_install_idle_handler();
2173
2174 if (win->m_delayedBackgroundColour)
2175 win->GtkSetBackgroundColour( win->GetBackgroundColour() );
2176
2177 if (win->m_delayedForegroundColour)
2178 win->GtkSetForegroundColour( win->GetForegroundColour() );
2179
2180 wxWindowCreateEvent event( win );
2181 event.SetEventObject( win );
2182 win->GetEventHandler()->ProcessEvent( event );
2183
2184 return FALSE;
2185 }
2186
2187 //-----------------------------------------------------------------------------
2188 // "size_allocate"
2189 //-----------------------------------------------------------------------------
2190
2191 static
2192 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2193 GtkAllocation *WXUNUSED(alloc),
2194 wxWindow *win )
2195 {
2196 if (g_isIdle)
2197 wxapp_install_idle_handler();
2198
2199 if (!win->m_hasScrolling) return;
2200
2201 int client_width = 0;
2202 int client_height = 0;
2203 win->GetClientSize( &client_width, &client_height );
2204 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2205 return;
2206
2207 win->m_oldClientWidth = client_width;
2208 win->m_oldClientHeight = client_height;
2209
2210 if (!win->m_nativeSizeEvent)
2211 {
2212 wxSizeEvent event( win->GetSize(), win->GetId() );
2213 event.SetEventObject( win );
2214 win->GetEventHandler()->ProcessEvent( event );
2215 }
2216 }
2217
2218
2219 #ifdef HAVE_XIM
2220 #define WXUNUSED_UNLESS_XIM(param) param
2221 #else
2222 #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param)
2223 #endif
2224
2225 /* Resize XIM window */
2226
2227 static
2228 void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget),
2229 GtkAllocation* WXUNUSED_UNLESS_XIM(alloc),
2230 wxWindowGTK* WXUNUSED_UNLESS_XIM(win) )
2231 {
2232 if (g_isIdle)
2233 wxapp_install_idle_handler();
2234
2235 #ifdef HAVE_XIM
2236 if (!win->m_ic)
2237 return;
2238
2239 if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION)
2240 {
2241 gint width, height;
2242
2243 gdk_window_get_size (widget->window, &width, &height);
2244 win->m_icattr->preedit_area.width = width;
2245 win->m_icattr->preedit_area.height = height;
2246 gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA);
2247 }
2248 #endif // HAVE_XIM
2249 }
2250
2251 //-----------------------------------------------------------------------------
2252 // "realize" from m_wxwindow
2253 //-----------------------------------------------------------------------------
2254
2255 /* Initialize XIM support */
2256
2257 static gint
2258 gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget),
2259 wxWindowGTK * WXUNUSED_UNLESS_XIM(win) )
2260 {
2261 if (g_isIdle)
2262 wxapp_install_idle_handler();
2263
2264 #ifdef HAVE_XIM
2265 if (win->m_ic) return FALSE;
2266 if (!widget) return FALSE;
2267 if (!gdk_im_ready()) return FALSE;
2268
2269 win->m_icattr = gdk_ic_attr_new();
2270 if (!win->m_icattr) return FALSE;
2271
2272 gint width, height;
2273 GdkEventMask mask;
2274 GdkColormap *colormap;
2275 GdkICAttr *attr = win->m_icattr;
2276 unsigned attrmask = GDK_IC_ALL_REQ;
2277 GdkIMStyle style;
2278 GdkIMStyle supported_style = (GdkIMStyle)
2279 (GDK_IM_PREEDIT_NONE |
2280 GDK_IM_PREEDIT_NOTHING |
2281 GDK_IM_PREEDIT_POSITION |
2282 GDK_IM_STATUS_NONE |
2283 GDK_IM_STATUS_NOTHING);
2284
2285 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2286 supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION);
2287
2288 attr->style = style = gdk_im_decide_style (supported_style);
2289 attr->client_window = widget->window;
2290
2291 if ((colormap = gtk_widget_get_colormap (widget)) !=
2292 gtk_widget_get_default_colormap ())
2293 {
2294 attrmask |= GDK_IC_PREEDIT_COLORMAP;
2295 attr->preedit_colormap = colormap;
2296 }
2297
2298 attrmask |= GDK_IC_PREEDIT_FOREGROUND;
2299 attrmask |= GDK_IC_PREEDIT_BACKGROUND;
2300 attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
2301 attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
2302
2303 switch (style & GDK_IM_PREEDIT_MASK)
2304 {
2305 case GDK_IM_PREEDIT_POSITION:
2306 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2307 {
2308 g_warning ("over-the-spot style requires fontset");
2309 break;
2310 }
2311
2312 gdk_window_get_size (widget->window, &width, &height);
2313
2314 attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
2315 attr->spot_location.x = 0;
2316 attr->spot_location.y = height;
2317 attr->preedit_area.x = 0;
2318 attr->preedit_area.y = 0;
2319 attr->preedit_area.width = width;
2320 attr->preedit_area.height = height;
2321 attr->preedit_fontset = widget->style->font;
2322
2323 break;
2324 }
2325
2326 win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask);
2327
2328 if (win->m_ic == NULL)
2329 g_warning ("Can't create input context.");
2330 else
2331 {
2332 mask = gdk_window_get_events (widget->window);
2333 mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic));
2334 gdk_window_set_events (widget->window, mask);
2335
2336 if (GTK_WIDGET_HAS_FOCUS(widget))
2337 gdk_im_begin (win->m_ic, widget->window);
2338 }
2339 #endif // HAVE_XIM
2340
2341 return FALSE;
2342 }
2343
2344 //-----------------------------------------------------------------------------
2345 // InsertChild for wxWindowGTK.
2346 //-----------------------------------------------------------------------------
2347
2348 /* Callback for wxWindowGTK. This very strange beast has to be used because
2349 * C++ has no virtual methods in a constructor. We have to emulate a
2350 * virtual function here as wxNotebook requires a different way to insert
2351 * a child in it. I had opted for creating a wxNotebookPage window class
2352 * which would have made this superfluous (such in the MDI window system),
2353 * but no-one was listening to me... */
2354
2355 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2356 {
2357 /* the window might have been scrolled already, do we
2358 have to adapt the position */
2359 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2360 child->m_x += pizza->xoffset;
2361 child->m_y += pizza->yoffset;
2362
2363 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2364 GTK_WIDGET(child->m_widget),
2365 child->m_x,
2366 child->m_y,
2367 child->m_width,
2368 child->m_height );
2369 }
2370
2371 //-----------------------------------------------------------------------------
2372 // global functions
2373 //-----------------------------------------------------------------------------
2374
2375 wxWindow *wxGetActiveWindow()
2376 {
2377 // the cast is necessary when we compile in wxUniversal mode
2378 return (wxWindow *)g_focusWindow;
2379 }
2380
2381 //-----------------------------------------------------------------------------
2382 // wxWindowGTK
2383 //-----------------------------------------------------------------------------
2384
2385 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2386 // method
2387 #ifdef __WXUNIVERSAL__
2388 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2389 #else // __WXGTK__
2390 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2391 #endif // __WXUNIVERSAL__/__WXGTK__
2392
2393 void wxWindowGTK::Init()
2394 {
2395 // common init
2396 InitBase();
2397
2398 // GTK specific
2399 m_widget = (GtkWidget *) NULL;
2400 m_wxwindow = (GtkWidget *) NULL;
2401 m_focusWidget = (GtkWidget *) NULL;
2402
2403 // position/size
2404 m_x = 0;
2405 m_y = 0;
2406 m_width = 0;
2407 m_height = 0;
2408
2409 m_sizeSet = FALSE;
2410 m_hasVMT = FALSE;
2411 m_needParent = TRUE;
2412 m_isBeingDeleted = FALSE;
2413
2414 m_noExpose = FALSE;
2415 m_nativeSizeEvent = FALSE;
2416
2417 m_hasScrolling = FALSE;
2418 m_isScrolling = FALSE;
2419
2420 m_hAdjust = (GtkAdjustment*) NULL;
2421 m_vAdjust = (GtkAdjustment*) NULL;
2422 m_oldHorizontalPos = 0.0;
2423 m_oldVerticalPos = 0.0;
2424
2425 m_resizing = FALSE;
2426 m_widgetStyle = (GtkStyle*) NULL;
2427
2428 m_insertCallback = (wxInsertChildFunction) NULL;
2429
2430 m_isStaticBox = FALSE;
2431 m_isRadioButton = FALSE;
2432 m_isListBox = FALSE;
2433 m_isFrame = FALSE;
2434 m_acceptsFocus = FALSE;
2435
2436 m_clipPaintRegion = FALSE;
2437
2438 m_cursor = *wxSTANDARD_CURSOR;
2439
2440 m_delayedForegroundColour = FALSE;
2441 m_delayedBackgroundColour = FALSE;
2442
2443 #ifdef HAVE_XIM
2444 m_ic = (GdkIC*) NULL;
2445 m_icattr = (GdkICAttr*) NULL;
2446 #endif
2447 }
2448
2449 wxWindowGTK::wxWindowGTK()
2450 {
2451 Init();
2452 }
2453
2454 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2455 wxWindowID id,
2456 const wxPoint &pos,
2457 const wxSize &size,
2458 long style,
2459 const wxString &name )
2460 {
2461 Init();
2462
2463 Create( parent, id, pos, size, style, name );
2464 }
2465
2466 bool wxWindowGTK::Create( wxWindow *parent,
2467 wxWindowID id,
2468 const wxPoint &pos,
2469 const wxSize &size,
2470 long style,
2471 const wxString &name )
2472 {
2473 if (!PreCreation( parent, pos, size ) ||
2474 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2475 {
2476 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2477 return FALSE;
2478 }
2479
2480 m_insertCallback = wxInsertChildInWindow;
2481
2482 // always needed for background clearing
2483 m_delayedBackgroundColour = TRUE;
2484
2485 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2486 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2487
2488 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2489
2490 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2491 scroll_class->scrollbar_spacing = 0;
2492
2493 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2494
2495 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
2496 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
2497
2498 m_wxwindow = gtk_pizza_new();
2499
2500 #ifndef __WXUNIVERSAL__
2501 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2502
2503 if (HasFlag(wxRAISED_BORDER))
2504 {
2505 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2506 }
2507 else if (HasFlag(wxSUNKEN_BORDER))
2508 {
2509 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2510 }
2511 else if (HasFlag(wxSIMPLE_BORDER))
2512 {
2513 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2514 }
2515 else
2516 {
2517 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2518 }
2519 #endif // __WXUNIVERSAL__
2520
2521 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2522
2523 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2524 m_acceptsFocus = TRUE;
2525
2526 // I _really_ don't want scrollbars in the beginning
2527 m_vAdjust->lower = 0.0;
2528 m_vAdjust->upper = 1.0;
2529 m_vAdjust->value = 0.0;
2530 m_vAdjust->step_increment = 1.0;
2531 m_vAdjust->page_increment = 1.0;
2532 m_vAdjust->page_size = 5.0;
2533 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
2534 m_hAdjust->lower = 0.0;
2535 m_hAdjust->upper = 1.0;
2536 m_hAdjust->value = 0.0;
2537 m_hAdjust->step_increment = 1.0;
2538 m_hAdjust->page_increment = 1.0;
2539 m_hAdjust->page_size = 5.0;
2540 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
2541
2542 // these handlers block mouse events to any window during scrolling such as
2543 // motion events and prevent GTK and wxWindows from fighting over where the
2544 // slider should be
2545
2546 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_press_event",
2547 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2548
2549 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_press_event",
2550 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2551
2552 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_release_event",
2553 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2554
2555 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_release_event",
2556 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2557
2558 // these handlers get notified when screen updates are required either when
2559 // scrolling or when the window size (and therefore scrollbar configuration)
2560 // has changed
2561
2562 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
2563 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
2564 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
2565 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
2566
2567 gtk_widget_show( m_wxwindow );
2568
2569 if (m_parent)
2570 m_parent->DoAddChild( this );
2571
2572 m_focusWidget = m_wxwindow;
2573
2574 PostCreation();
2575
2576 Show( TRUE );
2577
2578 return TRUE;
2579 }
2580
2581 wxWindowGTK::~wxWindowGTK()
2582 {
2583 if (g_focusWindow == this)
2584 g_focusWindow = NULL;
2585
2586 if (g_activeFrame == this)
2587 g_activeFrame = NULL;
2588
2589 m_isBeingDeleted = TRUE;
2590 m_hasVMT = FALSE;
2591
2592 if (m_widget)
2593 Show( FALSE );
2594
2595 DestroyChildren();
2596
2597 if (m_parent)
2598 m_parent->RemoveChild( this );
2599
2600 #ifdef HAVE_XIM
2601 if (m_ic)
2602 gdk_ic_destroy (m_ic);
2603 if (m_icattr)
2604 gdk_ic_attr_destroy (m_icattr);
2605 #endif
2606
2607 if (m_widgetStyle)
2608 {
2609 #if DISABLE_STYLE_IF_BROKEN_THEME
2610 // don't delete if it's a pixmap theme style
2611 if (!m_widgetStyle->engine_data)
2612 gtk_style_unref( m_widgetStyle );
2613 #endif
2614 m_widgetStyle = (GtkStyle*) NULL;
2615 }
2616
2617 if (m_wxwindow)
2618 {
2619 gtk_widget_destroy( m_wxwindow );
2620 m_wxwindow = (GtkWidget*) NULL;
2621 }
2622
2623 if (m_widget)
2624 {
2625 gtk_widget_destroy( m_widget );
2626 m_widget = (GtkWidget*) NULL;
2627 }
2628 }
2629
2630 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2631 {
2632 wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") );
2633
2634 /* this turns -1 into 20 so that a minimal window is
2635 visible even although -1,-1 has been given as the
2636 size of the window. the same trick is used in other
2637 ports and should make debugging easier */
2638 m_width = WidthDefault(size.x);
2639 m_height = HeightDefault(size.y);
2640
2641 m_x = (int)pos.x;
2642 m_y = (int)pos.y;
2643
2644 /* some reasonable defaults */
2645 if (!parent)
2646 {
2647 if (m_x == -1)
2648 {
2649 m_x = (gdk_screen_width () - m_width) / 2;
2650 if (m_x < 10) m_x = 10;
2651 }
2652 if (m_y == -1)
2653 {
2654 m_y = (gdk_screen_height () - m_height) / 2;
2655 if (m_y < 10) m_y = 10;
2656 }
2657 }
2658
2659 return TRUE;
2660 }
2661
2662 void wxWindowGTK::PostCreation()
2663 {
2664 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2665
2666 if (m_wxwindow)
2667 {
2668 if (!m_noExpose)
2669 {
2670 // these get reported to wxWindows -> wxPaintEvent
2671
2672 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2673
2674 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event",
2675 GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this );
2676
2677 #ifndef __WXGTK20__
2678 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw",
2679 GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this );
2680
2681 if (HasFlag(wxNO_FULL_REPAINT_ON_RESIZE))
2682 {
2683 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event",
2684 GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this );
2685 }
2686 #else
2687 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxNO_FULL_REPAINT_ON_RESIZE ) );
2688 #endif
2689 }
2690
2691 // these are called when the "sunken" or "raised" borders are drawn
2692 gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event",
2693 GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this );
2694
2695 #ifndef __WXGTK20__
2696 gtk_signal_connect( GTK_OBJECT(m_widget), "draw",
2697 GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this );
2698 #endif
2699 }
2700
2701 // focus handling
2702
2703 if (m_focusWidget == NULL)
2704 m_focusWidget = m_widget;
2705
2706 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
2707 GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
2708
2709 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event",
2710 GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
2711
2712 // connect to the various key and mouse handlers
2713
2714 GtkWidget *connect_widget = GetConnectWidget();
2715
2716 ConnectWidget( connect_widget );
2717
2718 /* We cannot set colours, fonts and cursors before the widget has
2719 been realized, so we do this directly after realization */
2720 gtk_signal_connect( GTK_OBJECT(connect_widget), "realize",
2721 GTK_SIGNAL_FUNC(gtk_window_realized_callback), (gpointer) this );
2722
2723 if (m_wxwindow)
2724 {
2725 // Catch native resize events
2726 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2727 GTK_SIGNAL_FUNC(gtk_window_size_callback), (gpointer)this );
2728
2729 // Initialize XIM support
2730 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize",
2731 GTK_SIGNAL_FUNC(gtk_wxwindow_realized_callback), (gpointer) this );
2732
2733 // And resize XIM window
2734 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2735 GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this );
2736 }
2737
2738 if (!GTK_IS_COMBO(m_widget))
2739 {
2740 // This is needed if we want to add our windows into native
2741 // GTK control, such as the toolbar. With this callback, the
2742 // toolbar gets to know the correct size (the one set by the
2743 // programmer). Sadly, it misbehaves for wxComboBox. FIXME
2744 // when moving to GTK 2.0.
2745 gtk_signal_connect( GTK_OBJECT(m_widget), "size_request",
2746 GTK_SIGNAL_FUNC(gtk_window_size_request_callback), (gpointer) this );
2747 }
2748
2749 m_hasVMT = TRUE;
2750 }
2751
2752 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2753 {
2754 gtk_signal_connect( GTK_OBJECT(widget), "key_press_event",
2755 GTK_SIGNAL_FUNC(gtk_window_key_press_callback), (gpointer)this );
2756
2757 gtk_signal_connect( GTK_OBJECT(widget), "key_release_event",
2758 GTK_SIGNAL_FUNC(gtk_window_key_release_callback), (gpointer)this );
2759
2760 gtk_signal_connect( GTK_OBJECT(widget), "button_press_event",
2761 GTK_SIGNAL_FUNC(gtk_window_button_press_callback), (gpointer)this );
2762
2763 gtk_signal_connect( GTK_OBJECT(widget), "button_release_event",
2764 GTK_SIGNAL_FUNC(gtk_window_button_release_callback), (gpointer)this );
2765
2766 gtk_signal_connect( GTK_OBJECT(widget), "motion_notify_event",
2767 GTK_SIGNAL_FUNC(gtk_window_motion_notify_callback), (gpointer)this );
2768
2769 gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event",
2770 GTK_SIGNAL_FUNC(gtk_window_enter_callback), (gpointer)this );
2771
2772 gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event",
2773 GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this );
2774 }
2775
2776 bool wxWindowGTK::Destroy()
2777 {
2778 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2779
2780 m_hasVMT = FALSE;
2781
2782 return wxWindowBase::Destroy();
2783 }
2784
2785 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2786 {
2787 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2788 }
2789
2790 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2791 {
2792 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2793 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2794
2795 /*
2796 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2797 */
2798
2799 if (m_resizing) return; /* I don't like recursions */
2800 m_resizing = TRUE;
2801
2802 int currentX, currentY;
2803 GetPosition(&currentX, &currentY);
2804 if (x == -1)
2805 x = currentX;
2806 if (y == -1)
2807 y = currentY;
2808 AdjustForParentClientOrigin(x, y, sizeFlags);
2809
2810 if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */
2811 {
2812 /* don't set the size for children of wxNotebook, just take the values. */
2813 m_x = x;
2814 m_y = y;
2815 m_width = width;
2816 m_height = height;
2817 }
2818 else
2819 {
2820 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2821 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2822 {
2823 if (x != -1) m_x = x + pizza->xoffset;
2824 if (y != -1) m_y = y + pizza->yoffset;
2825 if (width != -1) m_width = width;
2826 if (height != -1) m_height = height;
2827 }
2828 else
2829 {
2830 m_x = x + pizza->xoffset;
2831 m_y = y + pizza->yoffset;
2832 m_width = width;
2833 m_height = height;
2834 }
2835
2836 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
2837 {
2838 if (width == -1) m_width = 80;
2839 }
2840
2841 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
2842 {
2843 if (height == -1) m_height = 26;
2844 }
2845
2846 int minWidth = GetMinWidth(),
2847 minHeight = GetMinHeight(),
2848 maxWidth = GetMaxWidth(),
2849 maxHeight = GetMaxHeight();
2850
2851 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
2852 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2853 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
2854 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2855
2856 int border = 0;
2857 int bottom_border = 0;
2858
2859 #ifndef __WXGTK20__
2860 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2861 {
2862 /* the default button has a border around it */
2863 border = 6;
2864 bottom_border = 5;
2865 }
2866 #endif
2867
2868 DoMoveWindow( m_x-border,
2869 m_y-border,
2870 m_width+2*border,
2871 m_height+border+bottom_border );
2872 }
2873
2874 if (m_hasScrolling)
2875 {
2876 /* Sometimes the client area changes size without the
2877 whole windows's size changing, but if the whole
2878 windows's size doesn't change, no wxSizeEvent will
2879 normally be sent. Here we add an extra test if
2880 the client test has been changed and this will
2881 be used then. */
2882 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2883 }
2884
2885 /*
2886 wxPrintf( "OnSize sent from " );
2887 if (GetClassInfo() && GetClassInfo()->GetClassName())
2888 wxPrintf( GetClassInfo()->GetClassName() );
2889 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2890 */
2891
2892 if (!m_nativeSizeEvent)
2893 {
2894 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2895 event.SetEventObject( this );
2896 GetEventHandler()->ProcessEvent( event );
2897 }
2898
2899 m_resizing = FALSE;
2900 }
2901
2902 void wxWindowGTK::OnInternalIdle()
2903 {
2904 // Update invalidated regions.
2905 Update();
2906
2907 // Synthetize activate events.
2908 if ( g_sendActivateEvent != -1 )
2909 {
2910 bool activate = g_sendActivateEvent != 0;
2911
2912 // do it only once
2913 g_sendActivateEvent = -1;
2914
2915 wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast);
2916 }
2917
2918 if ( g_activeFrameLostFocus )
2919 {
2920 if ( g_activeFrame )
2921 {
2922 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame);
2923 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
2924 event.SetEventObject(g_activeFrame);
2925 g_activeFrame->GetEventHandler()->ProcessEvent(event);
2926 g_activeFrame = NULL;
2927 }
2928 g_activeFrameLostFocus = FALSE;
2929 }
2930
2931 if (g_delayedFocus == this)
2932 {
2933 if (GTK_WIDGET_REALIZED(m_widget))
2934 {
2935 gtk_widget_grab_focus( m_widget );
2936 g_delayedFocus = NULL;
2937 }
2938 }
2939
2940 wxCursor cursor = m_cursor;
2941 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2942
2943 if (cursor.Ok())
2944 {
2945 /* I now set the cursor anew in every OnInternalIdle call
2946 as setting the cursor in a parent window also effects the
2947 windows above so that checking for the current cursor is
2948 not possible. */
2949
2950 if (m_wxwindow)
2951 {
2952 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2953 if (window)
2954 gdk_window_set_cursor( window, cursor.GetCursor() );
2955
2956 if (!g_globalCursor.Ok())
2957 cursor = *wxSTANDARD_CURSOR;
2958
2959 window = m_widget->window;
2960 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2961 gdk_window_set_cursor( window, cursor.GetCursor() );
2962
2963 }
2964 else
2965 {
2966
2967 GdkWindow *window = m_widget->window;
2968 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2969 gdk_window_set_cursor( window, cursor.GetCursor() );
2970
2971 }
2972 }
2973
2974 UpdateWindowUI();
2975 }
2976
2977 void wxWindowGTK::DoGetSize( int *width, int *height ) const
2978 {
2979 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2980
2981 if (width) (*width) = m_width;
2982 if (height) (*height) = m_height;
2983 }
2984
2985 void wxWindowGTK::DoSetClientSize( int width, int height )
2986 {
2987 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2988
2989 if (!m_wxwindow)
2990 {
2991 SetSize( width, height );
2992 }
2993 else
2994 {
2995 int dw = 0;
2996 int dh = 0;
2997
2998 #ifndef __WXUNIVERSAL__
2999 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3000 {
3001 /* when using GTK 1.2 we set the shadow border size to 2 */
3002 dw += 2 * 2;
3003 dh += 2 * 2;
3004 }
3005 if (HasFlag(wxSIMPLE_BORDER))
3006 {
3007 /* when using GTK 1.2 we set the simple border size to 1 */
3008 dw += 1 * 2;
3009 dh += 1 * 2;
3010 }
3011 #endif // __WXUNIVERSAL__
3012
3013 if (m_hasScrolling)
3014 {
3015 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3016
3017 GtkRequisition vscroll_req;
3018 vscroll_req.width = 2;
3019 vscroll_req.height = 2;
3020 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3021 (scroll_window->vscrollbar, &vscroll_req );
3022
3023 GtkRequisition hscroll_req;
3024 hscroll_req.width = 2;
3025 hscroll_req.height = 2;
3026 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3027 (scroll_window->hscrollbar, &hscroll_req );
3028
3029 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3030
3031 if (scroll_window->vscrollbar_visible)
3032 {
3033 dw += vscroll_req.width;
3034 dw += scroll_class->scrollbar_spacing;
3035 }
3036
3037 if (scroll_window->hscrollbar_visible)
3038 {
3039 dh += hscroll_req.height;
3040 dh += scroll_class->scrollbar_spacing;
3041 }
3042 }
3043
3044 SetSize( width+dw, height+dh );
3045 }
3046 }
3047
3048 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3049 {
3050 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3051
3052 if (!m_wxwindow)
3053 {
3054 if (width) (*width) = m_width;
3055 if (height) (*height) = m_height;
3056 }
3057 else
3058 {
3059 int dw = 0;
3060 int dh = 0;
3061
3062 #ifndef __WXUNIVERSAL__
3063 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3064 {
3065 /* when using GTK 1.2 we set the shadow border size to 2 */
3066 dw += 2 * 2;
3067 dh += 2 * 2;
3068 }
3069 if (HasFlag(wxSIMPLE_BORDER))
3070 {
3071 /* when using GTK 1.2 we set the simple border size to 1 */
3072 dw += 1 * 2;
3073 dh += 1 * 2;
3074 }
3075 #endif // __WXUNIVERSAL__
3076
3077 if (m_hasScrolling)
3078 {
3079 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3080
3081 GtkRequisition vscroll_req;
3082 vscroll_req.width = 2;
3083 vscroll_req.height = 2;
3084 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3085 (scroll_window->vscrollbar, &vscroll_req );
3086
3087 GtkRequisition hscroll_req;
3088 hscroll_req.width = 2;
3089 hscroll_req.height = 2;
3090 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3091 (scroll_window->hscrollbar, &hscroll_req );
3092
3093 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3094
3095 if (scroll_window->vscrollbar_visible)
3096 {
3097 dw += vscroll_req.width;
3098 dw += scroll_class->scrollbar_spacing;
3099 }
3100
3101 if (scroll_window->hscrollbar_visible)
3102 {
3103 dh += hscroll_req.height;
3104 dh += scroll_class->scrollbar_spacing;
3105 }
3106 }
3107
3108 if (width) (*width) = m_width - dw;
3109 if (height) (*height) = m_height - dh;
3110 }
3111
3112 /*
3113 printf( "GetClientSize, name %s ", GetName().c_str() );
3114 if (width) printf( " width = %d", (*width) );
3115 if (height) printf( " height = %d", (*height) );
3116 printf( "\n" );
3117 */
3118 }
3119
3120 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3121 {
3122 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3123
3124 int dx = 0;
3125 int dy = 0;
3126 if (m_parent && m_parent->m_wxwindow)
3127 {
3128 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3129 dx = pizza->xoffset;
3130 dy = pizza->yoffset;
3131 }
3132
3133 if (x) (*x) = m_x - dx;
3134 if (y) (*y) = m_y - dy;
3135 }
3136
3137 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3138 {
3139 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3140
3141 if (!m_widget->window) return;
3142
3143 GdkWindow *source = (GdkWindow *) NULL;
3144 if (m_wxwindow)
3145 source = GTK_PIZZA(m_wxwindow)->bin_window;
3146 else
3147 source = m_widget->window;
3148
3149 int org_x = 0;
3150 int org_y = 0;
3151 gdk_window_get_origin( source, &org_x, &org_y );
3152
3153 if (!m_wxwindow)
3154 {
3155 if (GTK_WIDGET_NO_WINDOW (m_widget))
3156 {
3157 org_x += m_widget->allocation.x;
3158 org_y += m_widget->allocation.y;
3159 }
3160 }
3161
3162 if (x) *x += org_x;
3163 if (y) *y += org_y;
3164 }
3165
3166 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3167 {
3168 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3169
3170 if (!m_widget->window) return;
3171
3172 GdkWindow *source = (GdkWindow *) NULL;
3173 if (m_wxwindow)
3174 source = GTK_PIZZA(m_wxwindow)->bin_window;
3175 else
3176 source = m_widget->window;
3177
3178 int org_x = 0;
3179 int org_y = 0;
3180 gdk_window_get_origin( source, &org_x, &org_y );
3181
3182 if (!m_wxwindow)
3183 {
3184 if (GTK_WIDGET_NO_WINDOW (m_widget))
3185 {
3186 org_x += m_widget->allocation.x;
3187 org_y += m_widget->allocation.y;
3188 }
3189 }
3190
3191 if (x) *x -= org_x;
3192 if (y) *y -= org_y;
3193 }
3194
3195 bool wxWindowGTK::Show( bool show )
3196 {
3197 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3198
3199 if (!wxWindowBase::Show(show))
3200 {
3201 // nothing to do
3202 return FALSE;
3203 }
3204
3205 if (show)
3206 gtk_widget_show( m_widget );
3207 else
3208 gtk_widget_hide( m_widget );
3209
3210 return TRUE;
3211 }
3212
3213 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3214 {
3215 win->OnParentEnable(enable);
3216
3217 // Recurse, so that children have the opportunity to Do The Right Thing
3218 // and reset colours that have been messed up by a parent's (really ancestor's)
3219 // Enable call
3220 for ( wxWindowList::Node *node = win->GetChildren().GetFirst();
3221 node;
3222 node = node->GetNext() )
3223 {
3224 wxWindow *child = node->GetData();
3225 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3226 wxWindowNotifyEnable(child, enable);
3227 }
3228 }
3229
3230 bool wxWindowGTK::Enable( bool enable )
3231 {
3232 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3233
3234 if (!wxWindowBase::Enable(enable))
3235 {
3236 // nothing to do
3237 return FALSE;
3238 }
3239
3240 gtk_widget_set_sensitive( m_widget, enable );
3241 if ( m_wxwindow )
3242 gtk_widget_set_sensitive( m_wxwindow, enable );
3243
3244 wxWindowNotifyEnable(this, enable);
3245
3246 return TRUE;
3247 }
3248
3249 int wxWindowGTK::GetCharHeight() const
3250 {
3251 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3252
3253 wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") );
3254
3255 GdkFont *font = m_font.GetInternalFont( 1.0 );
3256
3257 return font->ascent + font->descent;
3258 }
3259
3260 int wxWindowGTK::GetCharWidth() const
3261 {
3262 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3263
3264 wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") );
3265
3266 GdkFont *font = m_font.GetInternalFont( 1.0 );
3267
3268 return gdk_string_width( font, "H" );
3269 }
3270
3271 void wxWindowGTK::GetTextExtent( const wxString& string,
3272 int *x,
3273 int *y,
3274 int *descent,
3275 int *externalLeading,
3276 const wxFont *theFont ) const
3277 {
3278 wxFont fontToUse = m_font;
3279 if (theFont) fontToUse = *theFont;
3280
3281 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3282
3283 GdkFont *font = fontToUse.GetInternalFont( 1.0 );
3284 if (x) (*x) = gdk_string_width( font, string.mbc_str() );
3285 if (y) (*y) = font->ascent + font->descent;
3286 if (descent) (*descent) = font->descent;
3287 if (externalLeading) (*externalLeading) = 0; // ??
3288 }
3289
3290 void wxWindowGTK::SetFocus()
3291 {
3292 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3293
3294 if (m_wxwindow)
3295 {
3296 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3297 {
3298 gtk_widget_grab_focus (m_wxwindow);
3299 }
3300 }
3301 else if (m_widget)
3302 {
3303 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3304 {
3305 if (!GTK_WIDGET_REALIZED(m_widget))
3306 g_delayedFocus = this;
3307 else
3308 gtk_widget_grab_focus (m_widget);
3309 }
3310 else if (GTK_IS_CONTAINER(m_widget))
3311 {
3312 SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD );
3313 }
3314 else
3315 {
3316 // ?
3317 }
3318 }
3319 }
3320
3321 bool wxWindowGTK::AcceptsFocus() const
3322 {
3323 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3324 }
3325
3326 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3327 {
3328 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3329
3330 wxWindowGTK *oldParent = m_parent,
3331 *newParent = (wxWindowGTK *)newParentBase;
3332
3333 wxASSERT( GTK_IS_WIDGET(m_widget) );
3334
3335 if ( !wxWindowBase::Reparent(newParent) )
3336 return FALSE;
3337
3338 wxASSERT( GTK_IS_WIDGET(m_widget) );
3339
3340 /* prevent GTK from deleting the widget arbitrarily */
3341 gtk_widget_ref( m_widget );
3342
3343 if (oldParent)
3344 {
3345 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3346 }
3347
3348 wxASSERT( GTK_IS_WIDGET(m_widget) );
3349
3350 if (newParent)
3351 {
3352 /* insert GTK representation */
3353 (*(newParent->m_insertCallback))(newParent, this);
3354 }
3355
3356 /* reverse: prevent GTK from deleting the widget arbitrarily */
3357 gtk_widget_unref( m_widget );
3358
3359 return TRUE;
3360 }
3361
3362 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3363 {
3364 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3365
3366 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3367
3368 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3369
3370 /* add to list */
3371 AddChild( child );
3372
3373 /* insert GTK representation */
3374 (*m_insertCallback)(this, child);
3375 }
3376
3377 void wxWindowGTK::Raise()
3378 {
3379 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3380
3381 if (!m_widget->window) return;
3382
3383 gdk_window_raise( m_widget->window );
3384 }
3385
3386 void wxWindowGTK::Lower()
3387 {
3388 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3389
3390 if (!m_widget->window) return;
3391
3392 gdk_window_lower( m_widget->window );
3393 }
3394
3395 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3396 {
3397 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3398
3399 if (cursor == m_cursor)
3400 return FALSE;
3401
3402 if (g_isIdle)
3403 wxapp_install_idle_handler();
3404
3405 if (cursor == wxNullCursor)
3406 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3407 else
3408 return wxWindowBase::SetCursor( cursor );
3409 }
3410
3411 void wxWindowGTK::WarpPointer( int x, int y )
3412 {
3413 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3414
3415 // We provide this function ourselves as it is
3416 // missing in GDK (top of this file).
3417
3418 GdkWindow *window = (GdkWindow*) NULL;
3419 if (m_wxwindow)
3420 window = GTK_PIZZA(m_wxwindow)->bin_window;
3421 else
3422 window = GetConnectWidget()->window;
3423
3424 if (window)
3425 gdk_window_warp_pointer( window, x, y );
3426 }
3427
3428 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3429 {
3430 if (!m_widget) return;
3431 if (!m_widget->window) return;
3432
3433 #ifndef __WXGTK20__
3434 if (g_isIdle)
3435 wxapp_install_idle_handler();
3436
3437 if (eraseBackground && m_wxwindow && m_wxwindow->window)
3438 {
3439 if (rect)
3440 {
3441 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3442 m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height );
3443 }
3444 else
3445 {
3446 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3447 m_clearRegion.Clear();
3448 m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3449 }
3450 }
3451
3452 if (rect)
3453 {
3454 if (m_wxwindow)
3455 {
3456 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3457 m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height );
3458 }
3459 else
3460 {
3461 GdkRectangle gdk_rect;
3462 gdk_rect.x = rect->x;
3463 gdk_rect.y = rect->y;
3464 gdk_rect.width = rect->width;
3465 gdk_rect.height = rect->height;
3466 gtk_widget_draw( m_widget, &gdk_rect );
3467 }
3468 }
3469 else
3470 {
3471 if (m_wxwindow)
3472 {
3473 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3474 m_updateRegion.Clear();
3475 m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3476 }
3477 else
3478 {
3479 gtk_widget_draw( m_widget, (GdkRectangle*) NULL );
3480 }
3481 }
3482 #else
3483 if (m_wxwindow)
3484 {
3485 if (rect)
3486 {
3487 GdkRectangle gdk_rect;
3488 gdk_rect.x = rect->x;
3489 gdk_rect.y = rect->y;
3490 gdk_rect.width = rect->width;
3491 gdk_rect.height = rect->height;
3492 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE );
3493 }
3494 else
3495 {
3496 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE );
3497 }
3498 }
3499 #endif
3500 }
3501
3502 void wxWindowGTK::Update()
3503 {
3504 #ifdef __WXGTK20__
3505 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3506 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3507 #endif
3508
3509 if (!m_updateRegion.IsEmpty())
3510 GtkSendPaintEvents();
3511 }
3512
3513 void wxWindowGTK::GtkSendPaintEvents()
3514 {
3515 if (!m_wxwindow)
3516 {
3517 m_clearRegion.Clear();
3518 m_updateRegion.Clear();
3519 return;
3520 }
3521
3522 m_clipPaintRegion = TRUE;
3523
3524 // if (!m_clearRegion.IsEmpty()) // always send an erase event
3525 {
3526 wxWindowDC dc( (wxWindow*)this );
3527 dc.SetClippingRegion( m_clearRegion );
3528
3529 wxEraseEvent erase_event( GetId(), &dc );
3530 erase_event.SetEventObject( this );
3531
3532 if (!GetEventHandler()->ProcessEvent(erase_event))
3533 {
3534 if (!g_eraseGC)
3535 {
3536 g_eraseGC = gdk_gc_new( GTK_PIZZA(m_wxwindow)->bin_window );
3537 gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
3538 }
3539 gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() );
3540
3541 wxRegionIterator upd( m_clearRegion );
3542 while (upd)
3543 {
3544 gdk_draw_rectangle( GTK_PIZZA(m_wxwindow)->bin_window, g_eraseGC, 1,
3545 upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() );
3546 upd ++;
3547 }
3548 }
3549 m_clearRegion.Clear();
3550 }
3551
3552 wxNcPaintEvent nc_paint_event( GetId() );
3553 nc_paint_event.SetEventObject( this );
3554 GetEventHandler()->ProcessEvent( nc_paint_event );
3555
3556 wxPaintEvent paint_event( GetId() );
3557 paint_event.SetEventObject( this );
3558 GetEventHandler()->ProcessEvent( paint_event );
3559
3560 m_clipPaintRegion = FALSE;
3561
3562 #ifndef __WXUNIVERSAL__
3563 #ifndef __WXGTK20__
3564 // The following code will result in all window-less widgets
3565 // being redrawn because the wxWindows class is allowed to
3566 // paint over the window-less widgets.
3567
3568 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
3569
3570 GList *children = pizza->children;
3571 while (children)
3572 {
3573 GtkPizzaChild *child = (GtkPizzaChild*) children->data;
3574 children = children->next;
3575
3576 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
3577 GTK_WIDGET_DRAWABLE (child->widget))
3578 {
3579 // Get intersection of widget area and update region
3580 wxRegion region( m_updateRegion );
3581
3582 GdkEventExpose gdk_event;
3583 gdk_event.type = GDK_EXPOSE;
3584 gdk_event.window = pizza->bin_window;
3585 gdk_event.count = 0;
3586
3587 wxRegionIterator upd( m_updateRegion );
3588 while (upd)
3589 {
3590 GdkRectangle rect;
3591 rect.x = upd.GetX();
3592 rect.y = upd.GetY();
3593 rect.width = upd.GetWidth();
3594 rect.height = upd.GetHeight();
3595
3596 if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area))
3597 {
3598 gtk_widget_event (child->widget, (GdkEvent*) &gdk_event);
3599 }
3600
3601 upd ++;
3602 }
3603 }
3604 }
3605 #endif
3606 #endif
3607
3608 m_updateRegion.Clear();
3609 }
3610
3611 void wxWindowGTK::Clear()
3612 {
3613 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3614
3615 if (m_wxwindow && m_wxwindow->window)
3616 {
3617 m_clearRegion.Clear();
3618 wxSize size( GetClientSize() );
3619 m_clearRegion.Union( 0,0,size.x,size.y );
3620
3621 // Better do this in idle?
3622 Update();
3623 }
3624 }
3625
3626 #if wxUSE_TOOLTIPS
3627 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3628 {
3629 wxWindowBase::DoSetToolTip(tip);
3630
3631 if (m_tooltip)
3632 m_tooltip->Apply( (wxWindow *)this );
3633 }
3634
3635 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3636 {
3637 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
3638 }
3639 #endif // wxUSE_TOOLTIPS
3640
3641 void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour )
3642 {
3643 GdkWindow *window = (GdkWindow*) NULL;
3644 if (m_wxwindow)
3645 window = GTK_PIZZA(m_wxwindow)->bin_window;
3646 else
3647 window = GetConnectWidget()->window;
3648
3649 wxASSERT( window );
3650
3651 // We need the pixel value e.g. for background clearing.
3652 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
3653
3654 if ((m_wxwindow) &&
3655 (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)))
3656 {
3657 /* wxMSW doesn't clear the window here. I don't do that either to
3658 provide compatibility. call Clear() to do the job. */
3659
3660 gdk_window_set_background( window, m_backgroundColour.GetColor() );
3661 }
3662
3663 ApplyWidgetStyle();
3664 }
3665
3666 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3667 {
3668 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
3669
3670 if (!wxWindowBase::SetBackgroundColour(colour))
3671 return FALSE;
3672
3673 GdkWindow *window = (GdkWindow*) NULL;
3674 if (m_wxwindow)
3675 window = GTK_PIZZA(m_wxwindow)->bin_window;
3676 else
3677 window = GetConnectWidget()->window;
3678
3679 if (!window)
3680 {
3681 // indicate that a new style has been set
3682 // but it couldn't get applied as the
3683 // widget hasn't been realized yet.
3684 m_delayedBackgroundColour = TRUE;
3685 return TRUE;
3686 }
3687 else
3688 {
3689 GtkSetBackgroundColour( colour );
3690 }
3691
3692 return TRUE;
3693 }
3694
3695 void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour )
3696 {
3697 GdkWindow *window = (GdkWindow*) NULL;
3698 if (m_wxwindow)
3699 window = GTK_PIZZA(m_wxwindow)->bin_window;
3700 else
3701 window = GetConnectWidget()->window;
3702
3703 wxASSERT( window );
3704
3705 ApplyWidgetStyle();
3706 }
3707
3708 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3709 {
3710 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
3711
3712 if (!wxWindowBase::SetForegroundColour(colour))
3713 {
3714 // don't leave if the GTK widget has just
3715 // been realized
3716 if (!m_delayedForegroundColour) return FALSE;
3717 }
3718
3719 GdkWindow *window = (GdkWindow*) NULL;
3720 if (m_wxwindow)
3721 window = GTK_PIZZA(m_wxwindow)->bin_window;
3722 else
3723 window = GetConnectWidget()->window;
3724
3725 if (!window)
3726 {
3727 // indicate that a new style has been set
3728 // but it couldn't get applied as the
3729 // widget hasn't been realized yet.
3730 m_delayedForegroundColour = TRUE;
3731 }
3732 else
3733 {
3734 GtkSetForegroundColour( colour );
3735 }
3736
3737 return TRUE;
3738 }
3739
3740 GtkStyle *wxWindowGTK::GetWidgetStyle()
3741 {
3742 if (m_widgetStyle)
3743 {
3744 GtkStyle *remake = gtk_style_copy( m_widgetStyle );
3745
3746 // FIXME: no more klass in 2.0
3747 #ifndef __WXGTK20__
3748 remake->klass = m_widgetStyle->klass;
3749 #endif
3750
3751 gtk_style_unref( m_widgetStyle );
3752 m_widgetStyle = remake;
3753 }
3754 else
3755 {
3756 GtkStyle *def = gtk_rc_get_style( m_widget );
3757
3758 if (!def)
3759 def = gtk_widget_get_default_style();
3760
3761 m_widgetStyle = gtk_style_copy( def );
3762
3763 // FIXME: no more klass in 2.0
3764 #ifndef __WXGTK20__
3765 m_widgetStyle->klass = def->klass;
3766 #endif
3767 }
3768
3769 return m_widgetStyle;
3770 }
3771
3772 void wxWindowGTK::SetWidgetStyle()
3773 {
3774 #if DISABLE_STYLE_IF_BROKEN_THEME
3775 if (m_widget->style->engine_data)
3776 {
3777 static bool s_warningPrinted = FALSE;
3778 if (!s_warningPrinted)
3779 {
3780 printf( "wxWindows warning: Widget styles disabled due to buggy GTK theme.\n" );
3781 s_warningPrinted = TRUE;
3782 }
3783 m_widgetStyle = m_widget->style;
3784 return;
3785 }
3786 #endif
3787
3788 GtkStyle *style = GetWidgetStyle();
3789
3790 if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ))
3791 {
3792 SET_STYLE_FONT(style, m_font.GetInternalFont( 1.0 ));
3793 }
3794
3795 if (m_foregroundColour.Ok())
3796 {
3797 m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
3798 if (m_foregroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT))
3799 {
3800 style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor();
3801 style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor();
3802 style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor();
3803 }
3804 else
3805 {
3806 // Try to restore the gtk default style. This is still a little
3807 // oversimplified for what is probably really needed here for controls
3808 // other than buttons, but is better than not being able to (re)set a
3809 // control's foreground colour to *wxBLACK -- RL
3810 GtkStyle *def = gtk_rc_get_style( m_widget );
3811
3812 if (!def)
3813 def = gtk_widget_get_default_style();
3814
3815 style->fg[GTK_STATE_NORMAL] = def->fg[GTK_STATE_NORMAL];
3816 style->fg[GTK_STATE_PRELIGHT] = def->fg[GTK_STATE_PRELIGHT];
3817 style->fg[GTK_STATE_ACTIVE] = def->fg[GTK_STATE_ACTIVE];
3818 }
3819 }
3820
3821 if (m_backgroundColour.Ok())
3822 {
3823 m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
3824 if (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))
3825 {
3826 style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
3827 style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
3828 style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
3829 style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
3830 style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
3831 style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
3832 style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
3833 style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
3834 }
3835 else
3836 {
3837 // Try to restore the gtk default style. This is still a little
3838 // oversimplified for what is probably really needed here for controls
3839 // other than buttons, but is better than not being able to (re)set a
3840 // control's background colour to default grey and means resetting a
3841 // button to wxSYS_COLOUR_BTNFACE will restore its usual highlighting
3842 // behavior -- RL
3843 GtkStyle *def = gtk_rc_get_style( m_widget );
3844
3845 if (!def)
3846 def = gtk_widget_get_default_style();
3847
3848 style->bg[GTK_STATE_NORMAL] = def->bg[GTK_STATE_NORMAL];
3849 style->base[GTK_STATE_NORMAL] = def->base[GTK_STATE_NORMAL];
3850 style->bg[GTK_STATE_PRELIGHT] = def->bg[GTK_STATE_PRELIGHT];
3851 style->base[GTK_STATE_PRELIGHT] = def->base[GTK_STATE_PRELIGHT];
3852 style->bg[GTK_STATE_ACTIVE] = def->bg[GTK_STATE_ACTIVE];
3853 style->base[GTK_STATE_ACTIVE] = def->base[GTK_STATE_ACTIVE];
3854 style->bg[GTK_STATE_INSENSITIVE] = def->bg[GTK_STATE_INSENSITIVE];
3855 style->base[GTK_STATE_INSENSITIVE] = def->base[GTK_STATE_INSENSITIVE];
3856 }
3857 }
3858 }
3859
3860 void wxWindowGTK::ApplyWidgetStyle()
3861 {
3862 }
3863
3864 //-----------------------------------------------------------------------------
3865 // Pop-up menu stuff
3866 //-----------------------------------------------------------------------------
3867
3868 #if wxUSE_MENUS_NATIVE
3869
3870 extern "C"
3871 void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting )
3872 {
3873 *is_waiting = FALSE;
3874 }
3875
3876 static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win )
3877 {
3878 menu->SetInvokingWindow( win );
3879 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
3880 while (node)
3881 {
3882 wxMenuItem *menuitem = node->GetData();
3883 if (menuitem->IsSubMenu())
3884 {
3885 SetInvokingWindow( menuitem->GetSubMenu(), win );
3886 }
3887
3888 node = node->GetNext();
3889 }
3890 }
3891
3892 // used to pass the coordinates from wxWindowGTK::DoPopupMenu() to
3893 // wxPopupMenuPositionCallback()
3894 //
3895 // should be safe even in the MT case as the user can hardly popup 2 menus
3896 // simultaneously, can he?
3897 static gint gs_pop_x = 0;
3898 static gint gs_pop_y = 0;
3899
3900 extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu,
3901 gint *x, gint *y,
3902 #ifdef __WXGTK20__
3903 gboolean * WXUNUSED(whatever),
3904 #endif
3905 gpointer WXUNUSED(user_data) )
3906 {
3907 // ensure that the menu appears entirely on screen
3908 GtkRequisition req;
3909 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
3910
3911 wxSize sizeScreen = wxGetDisplaySize();
3912
3913 gint xmax = sizeScreen.x - req.width,
3914 ymax = sizeScreen.y - req.height;
3915
3916 *x = gs_pop_x < xmax ? gs_pop_x : xmax;
3917 *y = gs_pop_y < ymax ? gs_pop_y : ymax;
3918 }
3919
3920 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
3921 {
3922 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
3923
3924 wxCHECK_MSG( menu != NULL, FALSE, wxT("invalid popup-menu") );
3925
3926 SetInvokingWindow( menu, this );
3927
3928 menu->UpdateUI();
3929
3930 gs_pop_x = x;
3931 gs_pop_y = y;
3932 ClientToScreen( &gs_pop_x, &gs_pop_y );
3933
3934 bool is_waiting = TRUE;
3935
3936 gtk_signal_connect( GTK_OBJECT(menu->m_menu),
3937 "hide",
3938 GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
3939 (gpointer)&is_waiting );
3940
3941 gtk_menu_popup(
3942 GTK_MENU(menu->m_menu),
3943 (GtkWidget *) NULL, // parent menu shell
3944 (GtkWidget *) NULL, // parent menu item
3945 wxPopupMenuPositionCallback, // function to position it
3946 NULL, // client data
3947 0, // button used to activate it
3948 gs_timeLastClick // the time of activation
3949 );
3950
3951 while (is_waiting)
3952 {
3953 while (gtk_events_pending())
3954 gtk_main_iteration();
3955 }
3956
3957 return TRUE;
3958 }
3959
3960 #endif // wxUSE_MENUS_NATIVE
3961
3962 #if wxUSE_DRAG_AND_DROP
3963
3964 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
3965 {
3966 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3967
3968 GtkWidget *dnd_widget = GetConnectWidget();
3969
3970 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
3971
3972 if (m_dropTarget) delete m_dropTarget;
3973 m_dropTarget = dropTarget;
3974
3975 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
3976 }
3977
3978 #endif // wxUSE_DRAG_AND_DROP
3979
3980 GtkWidget* wxWindowGTK::GetConnectWidget()
3981 {
3982 GtkWidget *connect_widget = m_widget;
3983 if (m_wxwindow) connect_widget = m_wxwindow;
3984
3985 return connect_widget;
3986 }
3987
3988 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
3989 {
3990 if (m_wxwindow)
3991 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
3992
3993 return (window == m_widget->window);
3994 }
3995
3996 bool wxWindowGTK::SetFont( const wxFont &font )
3997 {
3998 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
3999
4000 if (!wxWindowBase::SetFont(font))
4001 {
4002 return FALSE;
4003 }
4004
4005 wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
4006 if ( sysbg == m_backgroundColour )
4007 {
4008 m_backgroundColour = wxNullColour;
4009 ApplyWidgetStyle();
4010 m_backgroundColour = sysbg;
4011 }
4012 else
4013 {
4014 ApplyWidgetStyle();
4015 }
4016
4017 return TRUE;
4018 }
4019
4020 void wxWindowGTK::DoCaptureMouse()
4021 {
4022 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4023
4024 GdkWindow *window = (GdkWindow*) NULL;
4025 if (m_wxwindow)
4026 window = GTK_PIZZA(m_wxwindow)->bin_window;
4027 else
4028 window = GetConnectWidget()->window;
4029
4030 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4031
4032 wxCursor* cursor = & m_cursor;
4033 if (!cursor->Ok())
4034 cursor = wxSTANDARD_CURSOR;
4035
4036 gdk_pointer_grab( window, FALSE,
4037 (GdkEventMask)
4038 (GDK_BUTTON_PRESS_MASK |
4039 GDK_BUTTON_RELEASE_MASK |
4040 GDK_POINTER_MOTION_HINT_MASK |
4041 GDK_POINTER_MOTION_MASK),
4042 (GdkWindow *) NULL,
4043 cursor->GetCursor(),
4044 (guint32)GDK_CURRENT_TIME );
4045 g_captureWindow = this;
4046 g_captureWindowHasMouse = TRUE;
4047 }
4048
4049 void wxWindowGTK::DoReleaseMouse()
4050 {
4051 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4052
4053 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4054
4055 g_captureWindow = (wxWindowGTK*) NULL;
4056
4057 GdkWindow *window = (GdkWindow*) NULL;
4058 if (m_wxwindow)
4059 window = GTK_PIZZA(m_wxwindow)->bin_window;
4060 else
4061 window = GetConnectWidget()->window;
4062
4063 if (!window)
4064 return;
4065
4066 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4067 }
4068
4069 /* static */
4070 wxWindow *wxWindowBase::GetCapture()
4071 {
4072 return (wxWindow *)g_captureWindow;
4073 }
4074
4075 bool wxWindowGTK::IsRetained() const
4076 {
4077 return FALSE;
4078 }
4079
4080 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4081 int range, bool refresh )
4082 {
4083 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4084
4085 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4086
4087 m_hasScrolling = TRUE;
4088
4089 if (orient == wxHORIZONTAL)
4090 {
4091 float fpos = (float)pos;
4092 float frange = (float)range;
4093 float fthumb = (float)thumbVisible;
4094 if (fpos > frange-fthumb) fpos = frange-fthumb;
4095 if (fpos < 0.0) fpos = 0.0;
4096
4097 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4098 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4099 {
4100 SetScrollPos( orient, pos, refresh );
4101 return;
4102 }
4103
4104 m_oldHorizontalPos = fpos;
4105
4106 m_hAdjust->lower = 0.0;
4107 m_hAdjust->upper = frange;
4108 m_hAdjust->value = fpos;
4109 m_hAdjust->step_increment = 1.0;
4110 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4111 m_hAdjust->page_size = fthumb;
4112 }
4113 else
4114 {
4115 float fpos = (float)pos;
4116 float frange = (float)range;
4117 float fthumb = (float)thumbVisible;
4118 if (fpos > frange-fthumb) fpos = frange-fthumb;
4119 if (fpos < 0.0) fpos = 0.0;
4120
4121 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4122 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4123 {
4124 SetScrollPos( orient, pos, refresh );
4125 return;
4126 }
4127
4128 m_oldVerticalPos = fpos;
4129
4130 m_vAdjust->lower = 0.0;
4131 m_vAdjust->upper = frange;
4132 m_vAdjust->value = fpos;
4133 m_vAdjust->step_increment = 1.0;
4134 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4135 m_vAdjust->page_size = fthumb;
4136 }
4137
4138 if (orient == wxHORIZONTAL)
4139 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
4140 else
4141 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
4142 }
4143
4144 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4145 {
4146 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4147
4148 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4149
4150 if (orient == wxHORIZONTAL)
4151 {
4152 float fpos = (float)pos;
4153 if (fpos > m_hAdjust->upper - m_hAdjust->page_size) fpos = m_hAdjust->upper - m_hAdjust->page_size;
4154 if (fpos < 0.0) fpos = 0.0;
4155 m_oldHorizontalPos = fpos;
4156
4157 if (fabs(fpos-m_hAdjust->value) < 0.2) return;
4158 m_hAdjust->value = fpos;
4159 }
4160 else
4161 {
4162 float fpos = (float)pos;
4163 if (fpos > m_vAdjust->upper - m_vAdjust->page_size) fpos = m_vAdjust->upper - m_vAdjust->page_size;
4164 if (fpos < 0.0) fpos = 0.0;
4165 m_oldVerticalPos = fpos;
4166
4167 if (fabs(fpos-m_vAdjust->value) < 0.2) return;
4168 m_vAdjust->value = fpos;
4169 }
4170
4171 if (m_wxwindow->window)
4172 {
4173 if (orient == wxHORIZONTAL)
4174 {
4175 gtk_signal_disconnect_by_func( GTK_OBJECT(m_hAdjust),
4176 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4177
4178 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" );
4179
4180 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
4181 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4182 }
4183 else
4184 {
4185 gtk_signal_disconnect_by_func( GTK_OBJECT(m_vAdjust),
4186 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4187
4188 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" );
4189
4190 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
4191 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4192 }
4193 }
4194 }
4195
4196 int wxWindowGTK::GetScrollThumb( int orient ) const
4197 {
4198 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4199
4200 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4201
4202 if (orient == wxHORIZONTAL)
4203 return (int)(m_hAdjust->page_size+0.5);
4204 else
4205 return (int)(m_vAdjust->page_size+0.5);
4206 }
4207
4208 int wxWindowGTK::GetScrollPos( int orient ) const
4209 {
4210 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4211
4212 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4213
4214 if (orient == wxHORIZONTAL)
4215 return (int)(m_hAdjust->value+0.5);
4216 else
4217 return (int)(m_vAdjust->value+0.5);
4218 }
4219
4220 int wxWindowGTK::GetScrollRange( int orient ) const
4221 {
4222 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4223
4224 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4225
4226 if (orient == wxHORIZONTAL)
4227 return (int)(m_hAdjust->upper+0.5);
4228 else
4229 return (int)(m_vAdjust->upper+0.5);
4230 }
4231
4232 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4233 {
4234 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4235
4236 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4237
4238 // No scrolling requested.
4239 if ((dx == 0) && (dy == 0)) return;
4240
4241 #ifndef __WXGTK20__
4242 if (!m_updateRegion.IsEmpty())
4243 {
4244 m_updateRegion.Offset( dx, dy );
4245
4246 int cw = 0;
4247 int ch = 0;
4248 GetClientSize( &cw, &ch );
4249 m_updateRegion.Intersect( 0, 0, cw, ch );
4250 }
4251
4252 if (!m_clearRegion.IsEmpty())
4253 {
4254 m_clearRegion.Offset( dx, dy );
4255
4256 int cw = 0;
4257 int ch = 0;
4258 GetClientSize( &cw, &ch );
4259 m_clearRegion.Intersect( 0, 0, cw, ch );
4260 }
4261
4262 m_clipPaintRegion = TRUE;
4263
4264 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4265
4266 m_clipPaintRegion = FALSE;
4267 #else
4268
4269 gdk_window_scroll( GTK_PIZZA(m_wxwindow)->bin_window, dx, dy );
4270
4271 GTK_PIZZA(m_wxwindow)->xoffset += dx;
4272 GTK_PIZZA(m_wxwindow)->yoffset += dy;
4273
4274 #endif
4275
4276 }
4277
4278
4279 // Find the wxWindow at the current mouse position, also returning the mouse
4280 // position.
4281 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4282 {
4283 pt = wxGetMousePosition();
4284 wxWindow* found = wxFindWindowAtPoint(pt);
4285 return found;
4286 }
4287
4288 // Get the current mouse position.
4289 wxPoint wxGetMousePosition()
4290 {
4291 /* This crashes when used within wxHelpContext,
4292 so we have to use the X-specific implementation below.
4293 gint x, y;
4294 GdkModifierType *mask;
4295 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4296
4297 return wxPoint(x, y);
4298 */
4299
4300 int x, y;
4301 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4302 if (!windowAtPtr)
4303 return wxPoint(-999, -999);
4304
4305 Display *display = GDK_WINDOW_XDISPLAY(windowAtPtr);
4306 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4307 Window rootReturn, childReturn;
4308 int rootX, rootY, winX, winY;
4309 unsigned int maskReturn;
4310
4311 XQueryPointer (display,
4312 rootWindow,
4313 &rootReturn,
4314 &childReturn,
4315 &rootX, &rootY, &winX, &winY, &maskReturn);
4316 return wxPoint(rootX, rootY);
4317
4318 }
4319
4320 // ----------------------------------------------------------------------------
4321 // wxDCModule
4322 // ----------------------------------------------------------------------------
4323
4324 class wxWinModule : public wxModule
4325 {
4326 public:
4327 bool OnInit();
4328 void OnExit();
4329
4330 private:
4331 DECLARE_DYNAMIC_CLASS(wxWinModule)
4332 };
4333
4334 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4335
4336 bool wxWinModule::OnInit()
4337 {
4338 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4339 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4340
4341 return TRUE;
4342 }
4343
4344 void wxWinModule::OnExit()
4345 {
4346 if (g_eraseGC)
4347 gdk_gc_unref( g_eraseGC );
4348 }
4349