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