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