]> git.saurik.com Git - wxWidgets.git/blame - 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
c801d85f 1/////////////////////////////////////////////////////////////////////////////
faa94f3e 2// Name: src/gtk/window.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
c67d8618 5// Id: $Id$
01111366 6// Copyright: (c) 1998 Robert Roebling, Julian Smart
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
d02af7bb
JJ
13#ifdef __VMS
14#define XWarpPointer XWARPPOINTER
15#endif
16
c801d85f 17#include "wx/window.h"
88a7a4e1
WS
18
19#ifndef WX_PRECOMP
e4db172a 20 #include "wx/log.h"
670f9935 21 #include "wx/app.h"
cca410b3 22 #include "wx/toplevel.h"
ed4b0fdc 23 #include "wx/dcclient.h"
3b3dc801 24 #include "wx/menu.h"
9eddec69 25 #include "wx/settings.h"
246c5004 26 #include "wx/msgdlg.h"
18680f86 27 #include "wx/math.h"
88a7a4e1
WS
28#endif
29
bfeeb7f3
PC
30#include "wx/dnd.h"
31#include "wx/tooltip.h"
32#include "wx/caret.h"
48d011c8 33#include "wx/fontutil.h"
8ab7b4c5 34#include "wx/sysopt.h"
b4071e91 35
fab591c5 36#include <ctype.h>
c801d85f 37
9e691f46 38#include "wx/gtk/private.h"
bfeeb7f3 39#include "wx/gtk/win_gtk.h"
3ac8d3bc 40#include <gdk/gdkkeysyms.h>
3ac8d3bc 41#include <gdk/gdkx.h>
6bc8a1c8 42
868a2826
RR
43//-----------------------------------------------------------------------------
44// documentation on internals
45//-----------------------------------------------------------------------------
46
47/*
48 I have been asked several times about writing some documentation about
77ffb593 49 the GTK port of wxWidgets, especially its internal structures. Obviously,
868a2826 50 you cannot understand wxGTK without knowing a little about the GTK, but
47d67540 51 some more information about what the wxWindow, which is the base class
868a2826 52 for all other window classes, does seems required as well.
47d67540 53
30760ce7
RR
54 I)
55
868a2826 56 What does wxWindow do? It contains the common interface for the following
e380f72b 57 jobs of its descendants:
47d67540 58
868a2826 59 1) Define the rudimentary behaviour common to all window classes, such as
e380f72b
RR
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.
868a2826
RR
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
e380f72b 65 classes which do not display a native GTK widget. To name them, these
868a2826 66 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
47d67540 67 work classes are a special case and are handled a bit differently from
e380f72b 68 the rest. The same holds true for the wxNotebook class.
47d67540 69
868a2826
RR
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.
47d67540 73
e380f72b
RR
74 4) Provide the entire mechanism for scrolling widgets. This actual inter-
75 face for this is usually in wxScrolledWindow, but the GTK implementation
868a2826 76 is in this class.
47d67540 77
868a2826
RR
78 5) A multitude of helper or extra methods for special purposes, such as
79 Drag'n'Drop, managing validators etc.
47d67540 80
30760ce7
RR
81 6) Display a border (sunken, raised, simple or none).
82
77ffb593 83 Normally one might expect, that one wxWidgets window would always correspond
6a17b868 84 to one GTK widget. Under GTK, there is no such all-round widget that has all
868a2826
RR
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
77ffb593 90 represent a client area in the sense of wxWidgets capable to do the jobs
868a2826
RR
91 2), 3) and 4). I have written this class and it resides in win_gtk.c of
92 this directory.
47d67540 93
868a2826 94 All windows must have a widget, with which they interact with other under-
e380f72b 95 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
90e572f1 96 the wxWindow class has a member variable called m_widget which holds a
e380f72b
RR
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
f8e045e2 99 wxStaticText class handles only a GtkLabel widget a pointer to which you
e380f72b 100 can find in m_widget (defined in wxWindow)
8bbe427f 101
e380f72b 102 When the class has a client area for drawing into and for containing children
08f53168
PC
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.
8bbe427f
VZ
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
08f53168 109 the type wxPizza.
47d67540 110
868a2826
RR
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-
08f53168 115 Window and a m_wxwindow field that is pointer to a wxPizza and this
868a2826 116 one is (in the GTK sense) a child of the GtkScrolledWindow.
47d67540 117
868a2826 118 If the m_wxwindow field is set, then all input to this widget is inter-
77ffb593 119 cepted and sent to the wxWidgets class. If not, all input to the widget
868a2826 120 that gets pointed to by m_widget gets intercepted and sent to the class.
148cd9b6 121
30760ce7 122 II)
148cd9b6 123
77ffb593 124 The design of scrolling in wxWidgets is markedly different from that offered
30760ce7
RR
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
77ffb593 127 the window. In wxWidgets, the scrollbar will only emit an event, send this
30760ce7 128 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
08f53168 129 which actually moves the window and its sub-windows. Note that wxPizza
77ffb593 130 memorizes how much it has been scrolled but that wxWidgets forgets this
30760ce7 131 so that the two coordinates systems have to be kept in synch. This is done
08f53168 132 in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
148cd9b6
VZ
133
134 III)
135
6a17b868 136 Singularly the most broken code in GTK is the code that is supposed to
30760ce7
RR
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
90e572f1 143 the disadvantage that windows might get size events before the GTK widget
30760ce7 144 actually has the reported size. This doesn't normally pose any problem, but
90e572f1 145 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
30760ce7
RR
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
90e572f1 148 window that is used for OpenGL output really has that size (as reported by
30760ce7
RR
149 GTK).
150
151 IV)
148cd9b6 152
30760ce7 153 If someone at some point of time feels the immense desire to have a look at,
90e572f1
MR
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
30760ce7 157 own windowless widgets. Beware.
148cd9b6 158
30760ce7 159 V)
148cd9b6 160
30760ce7
RR
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
90e572f1 168 point to the GdkWindow of the parent widget (-> "window-less widget") and
30760ce7 169 that the two obviously have very different meanings.
868a2826
RR
170
171*/
172
f03fc89f
VZ
173//-----------------------------------------------------------------------------
174// data
175//-----------------------------------------------------------------------------
176
b541538f
PC
177// Don't allow event propagation during drag
178bool g_blockEventsOnDrag;
179// Don't allow mouse event propagation during scroll
180bool g_blockEventsOnScroll;
238d735d 181extern wxCursor g_globalCursor;
f68586e5 182
1e6feb95
VZ
183// mouse capture state: the window which has it and if the mouse is currently
184// inside it
185static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL;
0a164d4c 186static bool g_captureWindowHasMouse = false;
1e6feb95 187
12ff8221 188wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL;
1e6feb95
VZ
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
12ff8221 193wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL;
148cd9b6 194
d7fa7eaa
RR
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
5e513780
RR
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;
8f9850dd 203
2e563988
RR
204//-----------------------------------------------------------------------------
205// debug
206//-----------------------------------------------------------------------------
207
6cad4f1b
VZ
208// the trace mask used for the focus debugging messages
209#define TRACE_FOCUS _T("focus")
210
85eb36c2
RR
211//-----------------------------------------------------------------------------
212// missing gdk functions
213//-----------------------------------------------------------------------------
214
215void
216gdk_window_warp_pointer (GdkWindow *window,
c50f1fb9
VZ
217 gint x,
218 gint y)
85eb36c2 219{
85eb36c2 220 if (!window)
2454dc8a 221 window = gdk_get_default_root_window();
c50f1fb9 222
dd00f3f6
OK
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 }
85eb36c2
RR
231}
232
ed673c6a
RR
233//-----------------------------------------------------------------------------
234// local code (see below)
235//-----------------------------------------------------------------------------
236
f6bcfd97 237// returns the child of win which currently has focus or NULL if not found
1e6feb95 238//
fc71ef6e 239// Note: can't be static, needed by textctrl.cpp.
3379ed37 240wxWindow *wxFindFocusedChild(wxWindowGTK *win)
f6bcfd97 241{
3379ed37 242 wxWindow *winFocus = wxWindowGTK::FindFocus();
f6bcfd97 243 if ( !winFocus )
3379ed37 244 return (wxWindow *)NULL;
f6bcfd97
BP
245
246 if ( winFocus == win )
3379ed37 247 return (wxWindow *)win;
f6bcfd97 248
222ed1d6 249 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
f6bcfd97
BP
250 node;
251 node = node->GetNext() )
252 {
3379ed37 253 wxWindow *child = wxFindFocusedChild(node->GetData());
f6bcfd97
BP
254 if ( child )
255 return child;
256 }
257
3379ed37 258 return (wxWindow *)NULL;
f6bcfd97
BP
259}
260
47c93b63
RR
261//-----------------------------------------------------------------------------
262// "size_request" of m_widget
263//-----------------------------------------------------------------------------
264
865bb325
VZ
265// make it extern because wxStaticText needs to disconnect this one
266extern "C" {
e4161a2a 267void wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
e1f448ee 268 GtkRequisition *requisition,
e4161a2a 269 wxWindow * win)
47c93b63 270{
e1f448ee 271 int w, h;
47c93b63 272 win->GetSize( &w, &h );
e1f448ee
VZ
273 if (w < 2)
274 w = 2;
275 if (h < 2)
276 h = 2;
1e6feb95 277
47c93b63
RR
278 requisition->height = h;
279 requisition->width = w;
280}
865bb325 281}
47c93b63 282
c801d85f 283//-----------------------------------------------------------------------------
034be888 284// "expose_event" of m_wxwindow
c801d85f
KB
285//-----------------------------------------------------------------------------
286
865bb325 287extern "C" {
7f7beb1d 288static gboolean
03eaa52a 289gtk_window_expose_callback( GtkWidget*,
7f7beb1d
MR
290 GdkEventExpose *gdk_event,
291 wxWindow *win )
47d67540 292{
9abbd4a0 293#if 0
d7fa7eaa 294 if (win->GetName())
3d2d8da1
RR
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 }
0a164d4c 304
a1696b86
RR
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 );
d7fa7eaa 316#endif
1e6feb95 317
b15ed747
RR
318 win->GetUpdateRegion() = wxRegion( gdk_event->region );
319
320 win->GtkSendPaintEvents();
321
90e572f1 322 // Let parent window draw window-less widgets
2109c024 323 return FALSE;
b6fa52db 324}
865bb325 325}
b6fa52db 326
08f53168
PC
327//-----------------------------------------------------------------------------
328// "expose_event" from m_widget, for drawing border
329//-----------------------------------------------------------------------------
330
6de67633 331#ifndef __WXUNIVERSAL__
03eaa52a
PC
332
333GtkWidget* GetEntryWidget();
334
08f53168
PC
335extern "C" {
336static gboolean
6de67633 337expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
08f53168
PC
338{
339 // if this event is not for the GdkWindow the border is drawn on
6de67633 340 if (win->m_wxwindow == win->m_widget && gdk_event->window == widget->window)
08f53168
PC
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 {
03eaa52a 355 GdkGC* gc = gdk_gc_new(gdk_event->window);
08f53168 356 gdk_gc_set_foreground(gc, &widget->style->black);
6de67633 357 gdk_draw_rectangle(gdk_event->window, gc, false, x, y, w - 1, h - 1);
08f53168
PC
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;
03eaa52a
PC
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();
08f53168 376 gtk_paint_shadow(
03eaa52a
PC
377 styleWidget->style, gdk_event->window, GTK_STATE_NORMAL,
378 shadow, NULL, styleWidget, detail, x, y, w, h);
08f53168
PC
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
c801d85f 387//-----------------------------------------------------------------------------
b292e2f5 388// "key_press_event" from any window
c801d85f 389//-----------------------------------------------------------------------------
c801d85f 390
7da1b5f9
RD
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
74710601
VZ
403// set WXTRACE to this to see the key event codes on the console
404#define TRACE_KEYS _T("keyevent")
f17393f1 405
1c6896d7
VZ
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
faa94f3e 524 key_code = WXK_PAGEUP;
1c6896d7
VZ
525 break;
526
527 case GDK_Next: // == GDK_Page_Down
faa94f3e 528 key_code = WXK_PAGEDOWN;
1c6896d7
VZ
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
faa94f3e 607 key_code = isChar ? WXK_PAGEUP : WXK_NUMPAD_PAGEUP;
1c6896d7
VZ
608 break;
609
610 case GDK_KP_Next: // == GDK_KP_Page_Down
faa94f3e 611 key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
1c6896d7
VZ
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
a3c15d89
VS
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 );
cfa8c7d6 699 event.SetId(win->GetId());
a3c15d89
VS
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;
c4d25c01
VS
707#if wxUSE_UNICODE
708 event.m_uniChar = gdk_keyval_to_unicode(gdk_event->keyval);
709#endif
c958d025
RR
710 wxGetMousePosition( &x, &y );
711 win->ScreenToClient( &x, &y );
a3c15d89
VS
712 event.m_x = x;
713 event.m_y = y;
714 event.SetEventObject( win );
715}
716
717
74710601
VZ
718static bool
719wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
720 wxWindowGTK *win,
721 GdkEventKey *gdk_event)
47d67540 722{
1c6896d7
VZ
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;
0a62b197 735
ada7d2c0 736 wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %ld"),
0a62b197
VZ
737 event.GetEventType() == wxEVT_KEY_UP ? _T("release")
738 : _T("press"),
739 keysym);
740
0a164d4c 741 long key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
1c6896d7
VZ
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 }
a2053b27 755
1c6896d7 756 // we want to always get the same key code when the same key is
90e572f1 757 // pressed regardless of the state of the modifiers, i.e. on a
1c6896d7
VZ
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);
0a62b197
VZ
766
767 wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode);
768
1c6896d7
VZ
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
ada7d2c0 806 wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %ld"), key_code);
c801d85f 807
74710601 808 // sending unknown key events doesn't really make sense
1c6896d7 809 if ( !key_code )
0a164d4c 810 return false;
3d6f7261 811
1c6896d7 812 // now fill all the other fields
a3c15d89 813 wxFillOtherKeyEventFields(event, win, gdk_event);
0a164d4c 814
f5e27805 815 event.m_keyCode = key_code;
61f0bec0
RD
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
74710601 822
0a164d4c 823 return true;
74710601
VZ
824}
825
7c5e6fc6 826
a3c15d89
VS
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 {
3fe39b0c 839 g_object_unref (context);
a3c15d89
VS
840 }
841};
a3c15d89 842
865bb325 843extern "C" {
7f7beb1d
MR
844static gboolean
845gtk_window_key_press_callback( GtkWidget *widget,
846 GdkEventKey *gdk_event,
847 wxWindow *win )
74710601 848{
1c6896d7
VZ
849 if (!win->m_hasVMT)
850 return FALSE;
851 if (g_blockEventsOnDrag)
852 return FALSE;
f1272160 853
03a126c6
RR
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;
f1272160
RR
859
860 wxKeyEvent event( wxEVT_KEY_DOWN );
861 bool ret = false;
862 bool return_after_IM = false;
863
84dc821c
JS
864 if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
865 {
866 // Emit KEY_DOWN event
937013e0 867 ret = win->HandleWindowEvent( event );
84dc821c
JS
868 }
869 else
f1272160
RR
870 {
871 // Return after IM processing as we cannot do
872 // anything with it anyhow.
873 return_after_IM = true;
874 }
875
9e86e5ca 876 if ((!ret) && (win->m_imData != NULL))
f6fca1f8 877 {
f1272160
RR
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.
0a164d4c 880 // we should send the key_down event anyway.
f1272160 881 bool intercepted_by_IM = gtk_im_context_filter_keypress(win->m_imData->context, gdk_event);
f6fca1f8 882 win->m_imData->lastKeyEvent = NULL;
f1272160
RR
883 if (intercepted_by_IM)
884 {
f6fca1f8 885 wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM"));
7f7beb1d 886 return TRUE;
f1272160 887 }
f6fca1f8 888 }
68567a96 889
f1272160 890 if (return_after_IM)
7f7beb1d 891 return FALSE;
68567a96 892
50b58dec
RD
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 );
937013e0 903 ret = ancestor->HandleWindowEvent( command_event );
50b58dec
RD
904 break;
905 }
906 if (ancestor->IsTopLevel())
907 break;
908 ancestor = ancestor->GetParent();
909 }
910 }
911#endif // wxUSE_ACCEL
912
1ec3a984
RR
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.
2dde25b9 915 if (!ret)
d728dd40 916 {
7c5e6fc6 917 long key_code;
1c6896d7 918 KeySym keysym = gdk_event->keyval;
36025bcc 919 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
0a164d4c 920 key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
36025bcc 921 if ( !key_code )
1c6896d7 922 {
0b187670 923 if ( wxIsAsciiKeysym(keysym) )
1ec3a984 924 {
36025bcc
VS
925 // ASCII key
926 key_code = (unsigned char)keysym;
927 }
0b187670
RR
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 }
36025bcc 933 }
7c5e6fc6 934
36025bcc
VS
935 if ( key_code )
936 {
937 wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code);
7c5e6fc6 938
36025bcc 939 event.m_keyCode = key_code;
7c5e6fc6 940
1dabdced
RD
941 // To conform to the docs we need to translate Ctrl-alpha
942 // characters to values in the range 1-26.
7da1b5f9
RD
943 if ( event.ControlDown() &&
944 ( wxIsLowerChar(key_code) || wxIsUpperChar(key_code) ))
1dabdced 945 {
7da1b5f9
RD
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;
1dabdced
RD
950#if wxUSE_UNICODE
951 event.m_uniChar = event.m_keyCode;
952#endif
7f7beb1d 953 }
1dabdced 954
90e572f1 955 // Implement OnCharHook by checking ancestor top level windows
36025bcc
VS
956 wxWindow *parent = win;
957 while (parent && !parent->IsTopLevel())
958 parent = parent->GetParent();
959 if (parent)
960 {
961 event.SetEventType( wxEVT_CHAR_HOOK );
937013e0 962 ret = parent->HandleWindowEvent( event );
36025bcc
VS
963 }
964
965 if (!ret)
966 {
967 event.SetEventType(wxEVT_CHAR);
937013e0 968 ret = win->HandleWindowEvent( event );
1ec3a984 969 }
f17393f1 970 }
d728dd40 971 }
4d3ab2a0 972
c7b2e494 973 return ret;
2b5f62a0 974}
865bb325 975}
2b5f62a0 976
865bb325 977extern "C" {
7f7beb1d 978static void
e4161a2a 979gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context),
7f7beb1d
MR
980 const gchar *str,
981 wxWindow *window)
2b5f62a0 982{
2b5f62a0
VZ
983 wxKeyEvent event( wxEVT_KEY_DOWN );
984
a3c15d89
VS
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 }
28c513cb
RR
992 else
993 {
994 event.SetEventObject( window );
995 }
a3c15d89 996
6256849f
VS
997 const wxString data(wxGTK_CONV_BACK_SYS(str));
998 if( data.empty() )
5bfaca1b 999 return;
7c5e6fc6 1000
a3c15d89 1001 bool ret = false;
7c5e6fc6
RD
1002
1003 // Implement OnCharHook by checking ancestor top level windows
1004 wxWindow *parent = window;
1005 while (parent && !parent->IsTopLevel())
2b5f62a0 1006 parent = parent->GetParent();
2b5f62a0 1007
6256849f 1008 for( wxString::const_iterator pstr = data.begin(); pstr != data.end(); ++pstr )
7c5e6fc6 1009 {
5bfaca1b
VS
1010#if wxUSE_UNICODE
1011 event.m_uniChar = *pstr;
f6fca1f8 1012 // Backward compatible for ISO-8859-1
5bfaca1b
VS
1013 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1014 wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
1015#else
6256849f 1016 event.m_keyCode = (char)*pstr;
5bfaca1b 1017#endif // wxUSE_UNICODE
1dabdced
RD
1018
1019 // To conform to the docs we need to translate Ctrl-alpha
1020 // characters to values in the range 1-26.
7da1b5f9
RD
1021 if ( event.ControlDown() &&
1022 ( wxIsLowerChar(*pstr) || wxIsUpperChar(*pstr) ))
1dabdced 1023 {
7da1b5f9
RD
1024 if ( wxIsLowerChar(*pstr) )
1025 event.m_keyCode = *pstr - 'a' + 1;
1026 if ( wxIsUpperChar(*pstr) )
1027 event.m_keyCode = *pstr - 'A' + 1;
1028
1dabdced
RD
1029 event.m_keyCode = *pstr - 'a' + 1;
1030#if wxUSE_UNICODE
1031 event.m_uniChar = event.m_keyCode;
7f7beb1d
MR
1032#endif
1033 }
1dabdced 1034
5bfaca1b
VS
1035 if (parent)
1036 {
1037 event.SetEventType( wxEVT_CHAR_HOOK );
937013e0 1038 ret = parent->HandleWindowEvent( event );
5bfaca1b
VS
1039 }
1040
1041 if (!ret)
1042 {
1043 event.SetEventType(wxEVT_CHAR);
937013e0 1044 ret = window->HandleWindowEvent( event );
5bfaca1b 1045 }
2b5f62a0
VZ
1046 }
1047}
865bb325 1048}
2b5f62a0
VZ
1049
1050
b666df2c
RR
1051//-----------------------------------------------------------------------------
1052// "key_release_event" from any window
1053//-----------------------------------------------------------------------------
1054
865bb325 1055extern "C" {
7f7beb1d 1056static gboolean
e4161a2a 1057gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d
MR
1058 GdkEventKey *gdk_event,
1059 wxWindowGTK *win )
b666df2c 1060{
74710601
VZ
1061 if (!win->m_hasVMT)
1062 return FALSE;
b666df2c 1063
74710601
VZ
1064 if (g_blockEventsOnDrag)
1065 return FALSE;
b666df2c
RR
1066
1067 wxKeyEvent event( wxEVT_KEY_UP );
74710601 1068 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
b666df2c 1069 {
90e572f1 1070 // unknown key pressed, ignore (the event would be useless anyhow)
74710601 1071 return FALSE;
b666df2c
RR
1072 }
1073
97687291 1074 return win->GTKProcessEvent(event);
b666df2c 1075}
865bb325 1076}
b666df2c 1077
c5f9d156
VS
1078// ============================================================================
1079// the mouse events
1080// ============================================================================
1081
d1f2ac45
VZ
1082// ----------------------------------------------------------------------------
1083// mouse event processing helpers
1084// ----------------------------------------------------------------------------
1085
50f00d0c
VS
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 );
b4e43132
VZ
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;
50f00d0c
VS
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;
fcb29b23 1105
847dfdb4 1106 if ((win->m_wxwindow) && (win->GetLayoutDirection() == wxLayout_RightToLeft))
428f4657
RR
1107 {
1108 // origin in the upper right corner
08f53168 1109 int window_width = win->m_wxwindow->allocation.width;
720a0aab 1110 event.m_x = window_width - event.m_x;
428f4657 1111 }
50f00d0c
VS
1112
1113 event.SetEventObject( win );
1114 event.SetId( win->GetId() );
1115 event.SetTimestamp( gdk_event->time );
1116}
c5f9d156 1117
2daa0ce9
VZ
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
1e6feb95 1124
1a8caf94
RR
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))
2daa0ce9 1136 {
1a8caf94
RR
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;
2daa0ce9
VZ
1147 }
1148}
1149
d1f2ac45
VZ
1150// find the window to send the mouse event too
1151static
1152wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1153{
7d4909b2
RR
1154 wxCoord xx = x;
1155 wxCoord yy = y;
1156
d1f2ac45
VZ
1157 if (win->m_wxwindow)
1158 {
08f53168
PC
1159 wxPizza* pizza = WX_PIZZA(win->m_wxwindow);
1160 xx += pizza->m_scroll_x;
1161 yy += pizza->m_scroll_y;
d1f2ac45
VZ
1162 }
1163
222ed1d6 1164 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
d1f2ac45
VZ
1165 while (node)
1166 {
b1d4dd7a 1167 wxWindowGTK *child = node->GetData();
d1f2ac45 1168
b1d4dd7a 1169 node = node->GetNext();
d1f2ac45
VZ
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;
7408cf7f 1179 int yy2 = child->m_y + child->m_height;
d1f2ac45
VZ
1180
1181 // left
7d4909b2 1182 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
d1f2ac45 1183 // right
7d4909b2 1184 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
d1f2ac45 1185 // top
7d4909b2 1186 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
d1f2ac45 1187 // bottom
7d4909b2 1188 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
d1f2ac45
VZ
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) &&
7d4909b2
RR
1200 (child->m_x <= xx) &&
1201 (child->m_y <= yy) &&
af3653dd
RR
1202 (child->m_x+child->m_width >= xx) &&
1203 (child->m_y+child->m_height >= yy))
d1f2ac45
VZ
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
ef5c70f9
VZ
1216// ----------------------------------------------------------------------------
1217// common event handlers helpers
1218// ----------------------------------------------------------------------------
1219
97687291
VZ
1220bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1221{
1222 // nothing special at this level
937013e0 1223 return HandleWindowEvent(event);
97687291
VZ
1224}
1225
5478f221 1226int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
ef5c70f9 1227{
ef5c70f9 1228 if (!m_hasVMT)
5478f221 1229 return FALSE;
ef5c70f9 1230 if (g_blockEventsOnDrag)
5478f221 1231 return TRUE;
ef5c70f9 1232 if (g_blockEventsOnScroll)
5478f221 1233 return TRUE;
ef5c70f9
VZ
1234
1235 if (!GTKIsOwnWindow(event->window))
5478f221 1236 return FALSE;
ef5c70f9 1237
5478f221 1238 return -1;
ef5c70f9
VZ
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) \
5478f221 1245 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
ef5c70f9
VZ
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
5478f221
VZ
1256#define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1257 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1258 if ( rc != -1 ) \
1259 return rc
1260
ef5c70f9
VZ
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);
937013e0 1268 (void)win->HandleWindowEvent(eventChildFocus);
ef5c70f9
VZ
1269
1270 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1271 eventFocus.SetEventObject(win);
1272
937013e0 1273 return win->HandleWindowEvent(eventFocus);
ef5c70f9
VZ
1274}
1275
1276// all event handlers must have C linkage as they're called from GTK+ C code
1277extern "C"
1278{
1279
c801d85f 1280//-----------------------------------------------------------------------------
2f2aa628
RR
1281// "button_press_event"
1282//-----------------------------------------------------------------------------
c801d85f 1283
7f7beb1d
MR
1284static gboolean
1285gtk_window_button_press_callback( GtkWidget *widget,
1286 GdkEventButton *gdk_event,
1287 wxWindowGTK *win )
903f689b 1288{
5478f221 1289 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
034be888 1290
8f9850dd
RR
1291 g_lastButtonNumber = gdk_event->button;
1292
90e572f1 1293 // GDK sends surplus button down events
2b5f62a0
VZ
1294 // before a double click event. We
1295 // need to filter these out.
1e89eecd 1296 if ((gdk_event->type == GDK_BUTTON_PRESS) && (win->m_wxwindow))
2b5f62a0
VZ
1297 {
1298 GdkEvent *peek_event = gdk_event_peek();
1299 if (peek_event)
1300 {
8b8a8e0e
RR
1301 if ((peek_event->type == GDK_2BUTTON_PRESS) ||
1302 (peek_event->type == GDK_3BUTTON_PRESS))
2b5f62a0
VZ
1303 {
1304 gdk_event_free( peek_event );
1305 return TRUE;
1306 }
1307 else
1308 {
1309 gdk_event_free( peek_event );
1310 }
1311 }
1312 }
1313
2daa0ce9 1314 wxEventType event_type = wxEVT_NULL;
47d67540 1315
15475ced
VZ
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 }
15475ced 1326
f5e27805 1327 if (gdk_event->button == 1)
c801d85f 1328 {
f3f0d961 1329 // note that GDK generates triple click events which are not supported
77ffb593 1330 // by wxWidgets but still have to be passed to the app as otherwise
f3f0d961 1331 // clicks would simply go missing
f5e27805
RR
1332 switch (gdk_event->type)
1333 {
15475ced
VZ
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
f3f0d961
VZ
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;
b1f50e65
VZ
1346
1347 default:
1348 // just to silence gcc warnings
1349 ;
f5e27805 1350 }
362c6693 1351 }
f5e27805 1352 else if (gdk_event->button == 2)
c801d85f 1353 {
f5e27805
RR
1354 switch (gdk_event->type)
1355 {
15475ced 1356 case GDK_3BUTTON_PRESS:
f3f0d961
VZ
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;
b1f50e65
VZ
1364
1365 default:
1366 ;
f5e27805 1367 }
362c6693 1368 }
f5e27805 1369 else if (gdk_event->button == 3)
c801d85f 1370 {
f5e27805
RR
1371 switch (gdk_event->type)
1372 {
15475ced 1373 case GDK_3BUTTON_PRESS:
f3f0d961
VZ
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;
b1f50e65
VZ
1381
1382 default:
1383 ;
2b5f62a0
VZ
1384 }
1385 }
47d67540 1386
2daa0ce9
VZ
1387 if ( event_type == wxEVT_NULL )
1388 {
1389 // unknown mouse button or click type
1390 return FALSE;
1391 }
1392
8f9850dd 1393 g_lastMouseEvent = (GdkEvent*) gdk_event;
fcb29b23 1394
f5e27805 1395 wxMouseEvent event( event_type );
c5f9d156 1396 InitMouseEvent( win, event, gdk_event );
47d67540 1397
2daa0ce9 1398 AdjustEventButtonState(event);
94633ad9 1399
90e572f1 1400 // wxListBox actually gets mouse events from the item, so we need to give it
3ae4c570
VZ
1401 // a chance to correct this
1402 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
2daa0ce9 1403
90e572f1
MR
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
d1f2ac45
VZ
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);
ff8bfdbb 1409
ccb9fa0d
RD
1410 // reset the event object and id in case win changed.
1411 event.SetEventObject( win );
1412 event.SetId( win->GetId() );
670f9935 1413
fcb29b23 1414 bool ret = win->GTKProcessEvent( event );
5e513780 1415 g_lastMouseEvent = NULL;
fcb29b23
VZ
1416 if ( ret )
1417 return TRUE;
47d67540 1418
78cd9c69 1419 if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
c7bfb76a 1420 (g_focusWindow != win) /* && win->IsFocusable() */)
8622e8cd 1421 {
c7bfb76a 1422 win->SetFocus();
8622e8cd
RR
1423 }
1424
ac103441
RR
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);
97687291 1439 return win->GTKProcessEvent(evtCtx);
ac103441
RR
1440 }
1441
034be888 1442 return FALSE;
362c6693 1443}
c801d85f
KB
1444
1445//-----------------------------------------------------------------------------
97b3455a 1446// "button_release_event"
2f2aa628 1447//-----------------------------------------------------------------------------
c801d85f 1448
7f7beb1d
MR
1449static gboolean
1450gtk_window_button_release_callback( GtkWidget *widget,
1451 GdkEventButton *gdk_event,
1452 wxWindowGTK *win )
47d67540 1453{
5478f221 1454 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
47d67540 1455
8f9850dd
RR
1456 g_lastButtonNumber = 0;
1457
f5e27805 1458 wxEventType event_type = wxEVT_NULL;
47d67540 1459
f5e27805
RR
1460 switch (gdk_event->button)
1461 {
2b5f62a0
VZ
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:
6a17b868 1475 // unknown button, don't process
2b5f62a0 1476 return FALSE;
f5e27805 1477 }
47d67540 1478
8f9850dd 1479 g_lastMouseEvent = (GdkEvent*) gdk_event;
fcb29b23 1480
f5e27805 1481 wxMouseEvent event( event_type );
c5f9d156 1482 InitMouseEvent( win, event, gdk_event );
f5e27805 1483
2daa0ce9
VZ
1484 AdjustEventButtonState(event);
1485
3ae4c570
VZ
1486 // same wxListBox hack as above
1487 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
e2762ff0 1488
d1f2ac45
VZ
1489 if ( !g_captureWindow )
1490 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
47d67540 1491
ccb9fa0d
RD
1492 // reset the event object and id in case win changed.
1493 event.SetEventObject( win );
1494 event.SetId( win->GetId() );
1495
e6cac2df 1496 bool ret = win->GTKProcessEvent(event);
f4322df6 1497
e6cac2df 1498 g_lastMouseEvent = NULL;
f4322df6 1499
e6cac2df 1500 return ret;
362c6693 1501}
c801d85f
KB
1502
1503//-----------------------------------------------------------------------------
2f2aa628
RR
1504// "motion_notify_event"
1505//-----------------------------------------------------------------------------
c801d85f 1506
7f7beb1d 1507static gboolean
e4161a2a 1508gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d
MR
1509 GdkEventMotion *gdk_event,
1510 wxWindowGTK *win )
47d67540 1511{
5478f221 1512 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
034be888 1513
ff8bfdbb 1514 if (gdk_event->is_hint)
aae24d21 1515 {
f7a11f8c
RR
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;
aae24d21 1522 }
ff8bfdbb 1523
8f9850dd
RR
1524 g_lastMouseEvent = (GdkEvent*) gdk_event;
1525
e380f72b 1526 wxMouseEvent event( wxEVT_MOTION );
c5f9d156 1527 InitMouseEvent(win, event, gdk_event);
e380f72b 1528
50382578 1529 if ( g_captureWindow )
2f2aa628 1530 {
6a17b868 1531 // synthesise a mouse enter or leave event if needed
1e6feb95 1532 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
7c5e6fc6 1533 // This seems to be necessary and actually been added to
50382578
RR
1534 // GDK itself in version 2.0.X
1535 gdk_flush();
7c5e6fc6 1536
1e6feb95
VZ
1537 bool hasMouse = winUnderMouse == gdk_event->window;
1538 if ( hasMouse != g_captureWindowHasMouse )
1539 {
1540 // the mouse changed window
1541 g_captureWindowHasMouse = hasMouse;
1542
17a1ebd1
VZ
1543 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1544 : wxEVT_LEAVE_WINDOW);
1545 InitMouseEvent(win, eventM, gdk_event);
1546 eventM.SetEventObject(win);
97687291 1547 win->GTKProcessEvent(eventM);
1e6feb95
VZ
1548 }
1549 }
1550 else // no capture
1551 {
d1f2ac45 1552 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
ccb9fa0d
RD
1553
1554 // reset the event object and id in case win changed.
1555 event.SetEventObject( win );
1556 event.SetId( win->GetId() );
2f2aa628 1557 }
47d67540 1558
38f69be1
RR
1559 if ( !g_captureWindow )
1560 {
1561 wxSetCursorEvent cevent( event.m_x, event.m_y );
97687291 1562 if (win->GTKProcessEvent( cevent ))
38f69be1 1563 {
ec1e0c66 1564 win->SetCursor( cevent.GetCursor() );
38f69be1
RR
1565 }
1566 }
2e1f5012 1567
5e513780 1568 bool ret = win->GTKProcessEvent(event);
fcb29b23 1569
5e513780
RR
1570 g_lastMouseEvent = NULL;
1571
1572 return ret;
362c6693 1573}
c801d85f 1574
557c9f5b 1575//-----------------------------------------------------------------------------
40e5ebbf 1576// "scroll_event" (mouse wheel event)
557c9f5b
JS
1577//-----------------------------------------------------------------------------
1578
7f7beb1d 1579static gboolean
76e4be8e 1580window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
557c9f5b 1581{
76e4be8e
PC
1582 if (gdk_event->direction != GDK_SCROLL_UP &&
1583 gdk_event->direction != GDK_SCROLL_DOWN)
1584 {
1585 return false;
1586 }
0a164d4c 1587
76e4be8e 1588 wxMouseEvent event(wxEVT_MOUSEWHEEL);
b4e43132 1589 InitMouseEvent(win, event, gdk_event);
c53c7fad
RD
1590
1591 // FIXME: Get these values from GTK or GDK
58d185f7 1592 event.m_linesPerAction = 3;
60773911 1593 event.m_wheelDelta = 120;
557c9f5b
JS
1594 if (gdk_event->direction == GDK_SCROLL_UP)
1595 event.m_wheelRotation = 120;
1596 else
1597 event.m_wheelRotation = -120;
1598
97687291 1599 return win->GTKProcessEvent(event);
557c9f5b 1600}
ac103441
RR
1601
1602//-----------------------------------------------------------------------------
1603// "popup-menu"
1604//-----------------------------------------------------------------------------
ef5c70f9 1605
ac103441
RR
1606static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1607{
ac9d38d8 1608 wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
ac103441 1609 event.SetEventObject(win);
97687291 1610 return win->GTKProcessEvent(event);
ac103441 1611}
557c9f5b 1612
c801d85f 1613//-----------------------------------------------------------------------------
2f2aa628
RR
1614// "focus_in_event"
1615//-----------------------------------------------------------------------------
c801d85f 1616
7f7beb1d 1617static gboolean
e4161a2a 1618gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d
MR
1619 GdkEventFocus *WXUNUSED(event),
1620 wxWindow *win )
c801d85f 1621{
a3c15d89
VS
1622 if (win->m_imData)
1623 gtk_im_context_focus_in(win->m_imData->context);
4b1ae153 1624
1e6feb95 1625 g_focusWindowLast =
b292e2f5 1626 g_focusWindow = win;
ff8bfdbb 1627
6cad4f1b
VZ
1628 wxLogTrace(TRACE_FOCUS,
1629 _T("%s: focus in"), win->GetName().c_str());
7de59551 1630
1e6feb95 1631#if wxUSE_CARET
f6bcfd97
BP
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
f7108f16 1640 gboolean ret = FALSE;
628bad75 1641
6cad4f1b
VZ
1642 // does the window itself think that it has the focus?
1643 if ( !win->m_hasFocus )
5cd09f0b 1644 {
6cad4f1b 1645 // not yet, notify it
0a164d4c
WS
1646 win->m_hasFocus = true;
1647
628bad75 1648 (void)DoSendFocusEvents(win);
7f7beb1d
MR
1649
1650 ret = TRUE;
034be888 1651 }
ca298c88 1652
628bad75
RR
1653 // Disable default focus handling for custom windows
1654 // since the default GTK+ handler issues a repaint
1655 if (win->m_wxwindow)
4c20ee63 1656 return ret;
7f7beb1d
MR
1657
1658 return FALSE;
362c6693 1659}
c801d85f
KB
1660
1661//-----------------------------------------------------------------------------
2f2aa628
RR
1662// "focus_out_event"
1663//-----------------------------------------------------------------------------
c801d85f 1664
7f7beb1d 1665static gboolean
e4161a2a
VZ
1666gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
1667 GdkEventFocus * WXUNUSED(gdk_event),
7f7beb1d 1668 wxWindowGTK *win )
c801d85f 1669{
a3c15d89
VS
1670 if (win->m_imData)
1671 gtk_im_context_focus_out(win->m_imData->context);
4b1ae153 1672
6cad4f1b
VZ
1673 wxLogTrace( TRACE_FOCUS,
1674 _T("%s: focus out"), win->GetName().c_str() );
b231914f 1675
148cd9b6 1676
3379ed37 1677 wxWindowGTK *winFocus = wxFindFocusedChild(win);
f6bcfd97
BP
1678 if ( winFocus )
1679 win = winFocus;
1680
1e6feb95 1681 g_focusWindow = (wxWindowGTK *)NULL;
148cd9b6 1682
1e6feb95 1683#if wxUSE_CARET
f6bcfd97
BP
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
6cad4f1b
VZ
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 )
5cd09f0b 1695 {
6a74af89
VZ
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
0a164d4c 1700 win->m_hasFocus = false;
6cad4f1b
VZ
1701
1702 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1703 event.SetEventObject( win );
1704
97687291 1705 (void)win->GTKProcessEvent( event );
7f7beb1d 1706
6a74af89
VZ
1707 // Disable default focus handling for custom windows
1708 // since the default GTK+ handler issues a repaint
1709 if ( has_wxwindow )
1710 return TRUE;
78cd9c69 1711 }
7f7beb1d 1712
6a74af89 1713 // continue with normal processing
7f7beb1d 1714 return FALSE;
362c6693 1715}
c801d85f 1716
28e88942
VZ
1717static gboolean
1718wx_window_focus_callback(GtkWidget *widget,
e4161a2a 1719 GtkDirectionType WXUNUSED(direction),
28e88942
VZ
1720 wxWindowGTK *win)
1721{
08f53168 1722 // the default handler for focus signal in GtkScrolledWindow sets
28e88942
VZ
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
b4071e91
RR
1735//-----------------------------------------------------------------------------
1736// "enter_notify_event"
1737//-----------------------------------------------------------------------------
1738
f3a5f83a
MR
1739static gboolean
1740gtk_window_enter_callback( GtkWidget *widget,
1741 GdkEventCrossing *gdk_event,
1742 wxWindowGTK *win )
b4071e91 1743{
5478f221 1744 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
47d67540 1745
7f5f144a
RR
1746 // Event was emitted after a grab
1747 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
7c5e6fc6 1748
4a33eba6
RR
1749 int x = 0;
1750 int y = 0;
1751 GdkModifierType state = (GdkModifierType)0;
ff8bfdbb 1752
a2053b27 1753 gdk_window_get_pointer( widget->window, &x, &y, &state );
ff8bfdbb 1754
edc1d330 1755 wxMouseEvent event( wxEVT_ENTER_WINDOW );
c5f9d156
VS
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;
ff8bfdbb 1760
38f69be1
RR
1761 if ( !g_captureWindow )
1762 {
1763 wxSetCursorEvent cevent( event.m_x, event.m_y );
97687291 1764 if (win->GTKProcessEvent( cevent ))
38f69be1 1765 {
ec1e0c66 1766 win->SetCursor( cevent.GetCursor() );
38f69be1
RR
1767 }
1768 }
2e1f5012 1769
97687291 1770 return win->GTKProcessEvent(event);
b4071e91 1771}
47d67540 1772
b4071e91
RR
1773//-----------------------------------------------------------------------------
1774// "leave_notify_event"
1775//-----------------------------------------------------------------------------
1776
f3a5f83a
MR
1777static gboolean
1778gtk_window_leave_callback( GtkWidget *widget,
1779 GdkEventCrossing *gdk_event,
1780 wxWindowGTK *win )
b4071e91 1781{
5478f221 1782 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
b292e2f5 1783
7f5f144a
RR
1784 // Event was emitted after an ungrab
1785 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
7c5e6fc6 1786
e380f72b 1787 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
47d67540 1788
4a33eba6
RR
1789 int x = 0;
1790 int y = 0;
1791 GdkModifierType state = (GdkModifierType)0;
ff8bfdbb 1792
4a33eba6 1793 gdk_window_get_pointer( widget->window, &x, &y, &state );
ff8bfdbb 1794
b4e43132 1795 InitMouseEvent(win, event, gdk_event);
ff8bfdbb 1796
97687291 1797 return win->GTKProcessEvent(event);
b4071e91 1798}
47d67540 1799
c801d85f 1800//-----------------------------------------------------------------------------
add7cadd 1801// "value_changed" from scrollbar
2f2aa628 1802//-----------------------------------------------------------------------------
c801d85f 1803
add7cadd
PC
1804static void
1805gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
47d67540 1806{
add7cadd
PC
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;
22c9b211
VZ
1812
1813 // find the scrollbar which generated the event
1814 wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
1815
1816 // generate the corresponding wx event
bfeeb7f3 1817 const int orient = wxWindow::OrientFromScrollDir(dir);
add7cadd
PC
1818 wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
1819 event.SetEventObject(win);
22c9b211 1820
97687291 1821 win->GTKProcessEvent(event);
add7cadd 1822 }
362c6693 1823}
c801d85f 1824
cb43b372
RR
1825//-----------------------------------------------------------------------------
1826// "button_press_event" from scrollbar
1827//-----------------------------------------------------------------------------
1828
7f7beb1d 1829static gboolean
add7cadd 1830gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
cb43b372 1831{
0a164d4c 1832 g_blockEventsOnScroll = true;
add7cadd 1833 win->m_mouseButtonDown = true;
9e691f46 1834
add7cadd 1835 return false;
cb43b372
RR
1836}
1837
c918b2cd
PC
1838//-----------------------------------------------------------------------------
1839// "event_after" from scrollbar
1840//-----------------------------------------------------------------------------
1841
c918b2cd
PC
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
bfeeb7f3 1849 const int orient = wxWindow::OrientFromScrollDir(
22c9b211 1850 win->ScrollDirFromRange(range));
74ab5f5b
VZ
1851 wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBRELEASE,
1852 win->GetScrollPos(orient), orient);
1853 evt.SetEventObject(win);
1854 win->GTKProcessEvent(evt);
c918b2cd
PC
1855 }
1856}
c918b2cd 1857
cb43b372
RR
1858//-----------------------------------------------------------------------------
1859// "button_release_event" from scrollbar
1860//-----------------------------------------------------------------------------
1861
7f7beb1d 1862static gboolean
add7cadd 1863gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
cb43b372 1864{
0a164d4c 1865 g_blockEventsOnScroll = false;
add7cadd
PC
1866 win->m_mouseButtonDown = false;
1867 // If thumb tracking
2a23d363 1868 if (win->m_isScrolling)
88413fec 1869 {
add7cadd 1870 win->m_isScrolling = false;
c918b2cd 1871 // Hook up handler to send thumb release event after this emission is finished.
8ea30e36
PC
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
c918b2cd 1874 g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
88413fec
RR
1875 }
1876
add7cadd 1877 return false;
cb43b372 1878}
ca298c88 1879
a2053b27
RR
1880//-----------------------------------------------------------------------------
1881// "realize" from m_widget
1882//-----------------------------------------------------------------------------
1883
7f7beb1d 1884static void
08f53168 1885gtk_window_realized_callback(GtkWidget* widget, wxWindow* win)
a2053b27 1886{
a3c15d89 1887 if (win->m_imData)
2b5f62a0 1888 {
a3c15d89 1889 gtk_im_context_set_client_window( win->m_imData->context,
08f53168 1890 widget->window);
2b5f62a0 1891 }
2b5f62a0 1892
8622e8cd
RR
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
3c679789
RR
1903 wxWindowCreateEvent event( win );
1904 event.SetEventObject( win );
97687291 1905 win->GTKProcessEvent( event );
a2053b27
RR
1906}
1907
b79395c5 1908//-----------------------------------------------------------------------------
cca410b3 1909// "size_allocate" from m_wxwindow or m_widget
b79395c5
RR
1910//-----------------------------------------------------------------------------
1911
cca410b3
PC
1912static void
1913size_allocate(GtkWidget*, GtkAllocation* alloc, wxWindow* win)
8f75cb6c 1914{
cca410b3
PC
1915 int w = alloc->width;
1916 int h = alloc->height;
1917 if (win->m_wxwindow)
5b8a521e 1918 {
08f53168
PC
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;
cca410b3
PC
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 }
5b8a521e 1940 }
8f75cb6c
RR
1941}
1942
7738af59 1943//-----------------------------------------------------------------------------
78cd9c69 1944// "grab_broken"
7738af59
VZ
1945//-----------------------------------------------------------------------------
1946
db885f2a
PC
1947#if GTK_CHECK_VERSION(2, 8, 0)
1948static gboolean
1949gtk_window_grab_broken( GtkWidget*,
7738af59
VZ
1950 GdkEventGrabBroken *event,
1951 wxWindow *win )
1952{
1953 // Mouse capture has been lost involuntarily, notify the application
db885f2a 1954 if(!event->keyboard && wxWindow::GetCapture() == win)
7738af59
VZ
1955 {
1956 wxMouseCaptureLostEvent evt( win->GetId() );
1957 evt.SetEventObject( win );
937013e0 1958 win->HandleWindowEvent( evt );
7738af59 1959 }
db885f2a 1960 return false;
7738af59 1961}
4b859ff4 1962#endif
7738af59 1963
013151c7
JS
1964//-----------------------------------------------------------------------------
1965// "style_set"
1966//-----------------------------------------------------------------------------
1967
1968static
1969void gtk_window_style_set_callback( GtkWidget *WXUNUSED(widget),
1970 GtkStyle *previous_style,
1971 wxWindow* win )
1972{
013151c7
JS
1973 if (win && previous_style)
1974 {
013151c7
JS
1975 wxSysColourChangedEvent event;
1976 event.SetEventObject(win);
1977
1978 win->GTKProcessEvent( event );
1979 }
1980}
1981
ef5c70f9
VZ
1982} // extern "C"
1983
013151c7
JS
1984// Helper to suspend colour change event event processing while we change a widget's style
1985class wxSuspendStyleEvents
1986{
1987public:
a8eb15f3
PC
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 }
013151c7 2004
a8eb15f3 2005 wxWindow* m_win;
013151c7
JS
2006};
2007
ef5c70f9
VZ
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;
865bb325 2017}
63081513 2018
6ca41e57 2019//-----------------------------------------------------------------------------
1e6feb95 2020// InsertChild for wxWindowGTK.
6ca41e57
RR
2021//-----------------------------------------------------------------------------
2022
1e6feb95 2023/* Callback for wxWindowGTK. This very strange beast has to be used because
b1170810
RR
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... */
6ca41e57 2029
1e6feb95 2030static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
6ca41e57 2031{
bf0c00c6
RR
2032 /* the window might have been scrolled already, do we
2033 have to adapt the position */
08f53168
PC
2034 wxPizza* pizza = WX_PIZZA(parent->m_wxwindow);
2035 child->m_x += pizza->m_scroll_x;
2036 child->m_y += pizza->m_scroll_y;
148cd9b6 2037
08f53168
PC
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);
6ca41e57
RR
2042}
2043
bbe0af5b
RR
2044//-----------------------------------------------------------------------------
2045// global functions
2046//-----------------------------------------------------------------------------
2047
1e6feb95 2048wxWindow *wxGetActiveWindow()
bbe0af5b 2049{
6cad4f1b 2050 return wxWindow::FindFocus();
bbe0af5b
RR
2051}
2052
7dd40b6f
RD
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);
b4e43132
VZ
2069 ms.SetAux1Down(mask & GDK_BUTTON4_MASK);
2070 ms.SetAux2Down(mask & GDK_BUTTON5_MASK);
7dd40b6f
RD
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);
3d257b8d 2076
7dd40b6f
RD
2077 return ms;
2078}
3d257b8d 2079
c801d85f 2080//-----------------------------------------------------------------------------
1e6feb95 2081// wxWindowGTK
c801d85f
KB
2082//-----------------------------------------------------------------------------
2083
6522713c
VZ
2084// in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2085// method
1e6feb95 2086#ifdef __WXUNIVERSAL__
6522713c
VZ
2087 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2088#else // __WXGTK__
1e6feb95 2089 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
6522713c 2090#endif // __WXUNIVERSAL__/__WXGTK__
c801d85f 2091
1e6feb95 2092void wxWindowGTK::Init()
c801d85f 2093{
f03fc89f 2094 // GTK specific
a2053b27 2095 m_widget = (GtkWidget *) NULL;
e380f72b 2096 m_wxwindow = (GtkWidget *) NULL;
76fcf0f2 2097 m_focusWidget = (GtkWidget *) NULL;
8bbe427f 2098
f03fc89f 2099 // position/size
a2053b27
RR
2100 m_x = 0;
2101 m_y = 0;
2102 m_width = 0;
e380f72b 2103 m_height = 0;
8bbe427f 2104
0a164d4c 2105 m_hasVMT = false;
0a164d4c 2106 m_isBeingDeleted = false;
02761f6c 2107
f46ad98f 2108 m_showOnIdle= false;
148cd9b6 2109
0a164d4c
WS
2110 m_noExpose = false;
2111 m_nativeSizeEvent = false;
94633ad9 2112
0a164d4c 2113 m_isScrolling = false;
add7cadd 2114 m_mouseButtonDown = false;
add7cadd 2115
22c9b211
VZ
2116 // initialize scrolling stuff
2117 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2118 {
2119 m_scrollBar[dir] = NULL;
2120 m_scrollPos[dir] = 0;
22c9b211 2121 }
f03fc89f 2122
815ac4a7
VZ
2123 m_oldClientWidth =
2124 m_oldClientHeight = 0;
8bbe427f 2125
c821db16 2126 m_insertCallback = wxInsertChildInWindow;
8bbe427f 2127
0a164d4c 2128 m_hasFocus = false;
148cd9b6 2129
0a164d4c 2130 m_clipPaintRegion = false;
b6fa52db 2131
c7382f91
JS
2132 m_needsStyleChange = false;
2133
5e014a0c 2134 m_cursor = *wxSTANDARD_CURSOR;
2daa0ce9 2135
a3c15d89 2136 m_imData = NULL;
a589495e 2137 m_dirtyTabOrder = false;
362c6693 2138}
c801d85f 2139
1e6feb95 2140wxWindowGTK::wxWindowGTK()
68995f26
VZ
2141{
2142 Init();
2143}
2144
1e6feb95
VZ
2145wxWindowGTK::wxWindowGTK( wxWindow *parent,
2146 wxWindowID id,
2147 const wxPoint &pos,
2148 const wxSize &size,
2149 long style,
2150 const wxString &name )
6ca41e57 2151{
68995f26
VZ
2152 Init();
2153
e380f72b 2154 Create( parent, id, pos, size, style, name );
6ca41e57 2155}
8bbe427f 2156
1e6feb95
VZ
2157bool wxWindowGTK::Create( wxWindow *parent,
2158 wxWindowID id,
2159 const wxPoint &pos,
2160 const wxSize &size,
2161 long style,
2162 const wxString &name )
c801d85f 2163{
e5048854
JS
2164 // Get default border
2165 wxBorder border = GetBorder(style);
2166 style &= ~wxBORDER_MASK;
2167 style |= border;
ac9d38d8 2168
4dcaf11a
RR
2169 if (!PreCreation( parent, pos, size ) ||
2170 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2171 {
1e6feb95 2172 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
0a164d4c 2173 return false;
4dcaf11a 2174 }
47d67540 2175
e5048854 2176
08f53168 2177 m_wxwindow = wxPizza::New(m_windowStyle);
3e09bcfd 2178 if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
c9192212 2179 m_widget = m_wxwindow;
6552c7af
RR
2180 else
2181 {
2182 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
ff8bfdbb 2183
6552c7af 2184 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
47d67540 2185
6552c7af
RR
2186 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2187 scroll_class->scrollbar_spacing = 0;
47d67540 2188
6552c7af
RR
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) )
fd4081aa 2196 {
6552c7af
RR
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 }
fd4081aa 2204 }
fd4081aa 2205
6552c7af
RR
2206 if (HasFlag(wxALWAYS_SHOW_SB))
2207 {
2208 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
d2288928 2209
6552c7af
RR
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 }
47d67540 2217
6552c7af
RR
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 );
47d67540 2222
6552c7af 2223 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
4e5a4c69 2224
6552c7af
RR
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",
22c9b211 2232 G_CALLBACK(gtk_scrollbar_button_press_event), this);
6552c7af 2233 g_signal_connect(m_scrollBar[dir], "button_release_event",
22c9b211
VZ
2234 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2235
6552c7af 2236 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
22c9b211 2237 G_CALLBACK(gtk_scrollbar_event_after), this);
6552c7af 2238 g_signal_handler_block(m_scrollBar[dir], handler_id);
22c9b211 2239
6552c7af 2240 // these handlers get notified when scrollbar slider moves
40e5ebbf 2241 g_signal_connect_after(m_scrollBar[dir], "value_changed",
22c9b211 2242 G_CALLBACK(gtk_scrollbar_value_changed), this);
6552c7af 2243 }
76ed8f8d 2244
6552c7af
RR
2245 gtk_widget_show( m_wxwindow );
2246 }
47d67540 2247
f03fc89f
VZ
2248 if (m_parent)
2249 m_parent->DoAddChild( this );
94633ad9 2250
76fcf0f2 2251 m_focusWidget = m_wxwindow;
8bbe427f 2252
e380f72b 2253 PostCreation();
8bbe427f 2254
0a164d4c 2255 return true;
362c6693 2256}
c801d85f 2257
1e6feb95 2258wxWindowGTK::~wxWindowGTK()
c801d85f 2259{
7de59551
RD
2260 SendDestroyEvent();
2261
44cd54c2
JS
2262 if (g_focusWindow == this)
2263 g_focusWindow = NULL;
2264
3e679f01
VZ
2265 if ( g_delayedFocus == this )
2266 g_delayedFocus = NULL;
2267
0a164d4c
WS
2268 m_isBeingDeleted = true;
2269 m_hasVMT = false;
47d67540 2270
02c3e53b
JS
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 {
9fa72bd2
MR
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);
02c3e53b
JS
2284 }
2285
f03fc89f 2286 if (m_widget)
0a164d4c 2287 Show( false );
8bbe427f 2288
f6551618
MW
2289 // delete before the widgets to avoid a crash on solaris
2290 delete m_imData;
f6551618 2291
6552c7af 2292 if (m_wxwindow && (m_wxwindow != m_widget))
a2053b27 2293 {
f03fc89f 2294 gtk_widget_destroy( m_wxwindow );
c50f1fb9 2295 m_wxwindow = (GtkWidget*) NULL;
a2053b27 2296 }
8bbe427f 2297
f03fc89f 2298 if (m_widget)
a2053b27 2299 {
f03fc89f 2300 gtk_widget_destroy( m_widget );
c50f1fb9 2301 m_widget = (GtkWidget*) NULL;
a2053b27 2302 }
362c6693 2303}
c801d85f 2304
1e6feb95 2305bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
c801d85f 2306{
e8375af8
VZ
2307 if ( GTKNeedsParent() )
2308 {
2309 wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2310 }
8bbe427f 2311
a7c26d10
RD
2312 // Use either the given size, or the default if -1 is given.
2313 // See wxWindowBase for these functions.
3013a903 2314 m_width = WidthDefault(size.x) ;
f03fc89f 2315 m_height = HeightDefault(size.y);
8bbe427f 2316
43a18898
RR
2317 m_x = (int)pos.x;
2318 m_y = (int)pos.y;
8bbe427f 2319
0a164d4c 2320 return true;
c801d85f
KB
2321}
2322
1e6feb95 2323void wxWindowGTK::PostCreation()
c801d85f 2324{
82b978d7
RD
2325 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2326
43a18898
RR
2327 if (m_wxwindow)
2328 {
147bc491 2329 if (!m_noExpose)
b02da6b1 2330 {
77ffb593 2331 // these get reported to wxWidgets -> wxPaintEvent
1e6feb95 2332
9fa72bd2
MR
2333 g_signal_connect (m_wxwindow, "expose_event",
2334 G_CALLBACK (gtk_window_expose_callback), this);
147bc491 2335
847dfdb4 2336 if (GetLayoutDirection() == wxLayout_LeftToRight)
08f53168 2337 gtk_widget_set_redraw_on_allocate(m_wxwindow, HasFlag(wxFULL_REPAINT_ON_RESIZE));
93d23d8f 2338 }
2b5f62a0 2339
ed56a258
JS
2340 // Create input method handler
2341 m_imData = new wxGtkIMData;
2342
2b5f62a0 2343 // Cannot handle drawing preedited text yet
a3c15d89 2344 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2b5f62a0 2345
9fa72bd2 2346 g_signal_connect (m_imData->context, "commit",
da210120 2347 G_CALLBACK (gtk_wxwindow_commit_cb), this);
148cd9b6 2348
08f53168 2349 // border drawing
6de67633 2350#ifndef __WXUNIVERSAL__
d7645b3e 2351 if (HasFlag(wxPizza::BORDER_STYLES))
08f53168
PC
2352 {
2353 g_signal_connect(m_widget, "expose_event",
2354 G_CALLBACK(expose_event_border), this);
2355 }
2356#endif
43a18898 2357 }
47d67540 2358
76fcf0f2 2359 // focus handling
63081513 2360
06fda9e8
RR
2361 if (!GTK_IS_WINDOW(m_widget))
2362 {
2363 if (m_focusWidget == NULL)
2364 m_focusWidget = m_widget;
0a164d4c 2365
4c20ee63
RR
2366 if (m_wxwindow)
2367 {
2368 g_signal_connect (m_focusWidget, "focus_in_event",
9fa72bd2 2369 G_CALLBACK (gtk_window_focus_in_callback), this);
4c20ee63 2370 g_signal_connect (m_focusWidget, "focus_out_event",
f1e57cb9 2371 G_CALLBACK (gtk_window_focus_out_callback), this);
4c20ee63
RR
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 }
06fda9e8 2380 }
76fcf0f2 2381
28e88942
VZ
2382 if ( !AcceptsFocusFromKeyboard() )
2383 {
80332672 2384 SetCanFocus(false);
28e88942
VZ
2385
2386 g_signal_connect(m_widget, "focus",
2387 G_CALLBACK(wx_window_focus_callback), this);
2388 }
2389
76fcf0f2 2390 // connect to the various key and mouse handlers
63081513 2391
a2053b27 2392 GtkWidget *connect_widget = GetConnectWidget();
f03fc89f 2393
a2053b27 2394 ConnectWidget( connect_widget );
47d67540 2395
63081513 2396 /* We cannot set colours, fonts and cursors before the widget has
a2053b27 2397 been realized, so we do this directly after realization */
9fa72bd2
MR
2398 g_signal_connect (connect_widget, "realize",
2399 G_CALLBACK (gtk_window_realized_callback), this);
2daa0ce9 2400
cca410b3
PC
2401 if (!IsTopLevel())
2402 {
2403 g_signal_connect(m_wxwindow ? m_wxwindow : m_widget, "size_allocate",
2404 G_CALLBACK(size_allocate), this);
2405 }
2406
63081513
RR
2407 if (m_wxwindow)
2408 {
db885f2a 2409#if GTK_CHECK_VERSION(2, 8, 0)
4b859ff4
RR
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",
7738af59 2414 G_CALLBACK (gtk_window_grab_broken), this);
4b859ff4
RR
2415 }
2416#endif
7738af59
VZ
2417 }
2418
2419 if ( connect_widget != m_wxwindow )
2420 {
db885f2a 2421#if GTK_CHECK_VERSION(2, 8, 0)
4b859ff4
RR
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",
7738af59 2426 G_CALLBACK (gtk_window_grab_broken), this);
4b859ff4
RR
2427 }
2428#endif
63081513 2429 }
2daa0ce9 2430
5b34e141 2431#ifdef GTK_IS_FILE_CHOOSER_BUTTON
00ce89e7 2432 if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget))
ec376c8f 2433 {
556151f5
MW
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)
ec376c8f 2437 // FIXME: what should be done here ?
60869e72 2438 } else
5b34e141 2439#endif
60869e72 2440 if ( !IsTopLevel() ) // top level windows use their own callback
47c93b63
RR
2441 {
2442 // This is needed if we want to add our windows into native
024e9a4c 2443 // GTK controls, such as the toolbar. With this callback, the
47c93b63 2444 // toolbar gets to know the correct size (the one set by the
024e9a4c 2445 // programmer). Sadly, it misbehaves for wxComboBox.
9fa72bd2
MR
2446 g_signal_connect (m_widget, "size_request",
2447 G_CALLBACK (wxgtk_window_size_request_callback),
2448 this);
47c93b63 2449 }
1e6feb95 2450
40bab631
VS
2451 InheritAttributes();
2452
0a164d4c 2453 m_hasVMT = true;
e892f2fd 2454
978af864 2455 SetLayoutDirection(wxLayout_Default);
a433fbd5
VZ
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 );
b4071e91
RR
2461}
2462
1e6feb95 2463void wxWindowGTK::ConnectWidget( GtkWidget *widget )
b4071e91 2464{
9fa72bd2
MR
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",
76e4be8e 2476 G_CALLBACK (window_scroll_event), this);
9fa72bd2
MR
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);
013151c7
JS
2483
2484 if (IsTopLevel() && m_wxwindow)
2485 g_signal_connect (m_wxwindow, "style_set",
2486 G_CALLBACK (gtk_window_style_set_callback), this);
362c6693 2487}
c801d85f 2488
1e6feb95 2489bool wxWindowGTK::Destroy()
c801d85f 2490{
82b978d7 2491 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
47d67540 2492
0a164d4c 2493 m_hasVMT = false;
c801d85f 2494
f03fc89f 2495 return wxWindowBase::Destroy();
362c6693 2496}
c801d85f 2497
1e6feb95 2498void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
23efdd02 2499{
08f53168 2500 gtk_widget_set_size_request(m_widget, width, height);
69597639 2501 // inform the parent to perform the move
08f53168 2502 WX_PIZZA(m_parent->m_wxwindow)->move(m_widget, x, y);
23efdd02 2503}
2daa0ce9 2504
82008f15
PC
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
1e6feb95 2522void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
c801d85f 2523{
82b978d7 2524 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
1e6feb95 2525 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
8bbe427f 2526
b9f29261
VS
2527 int currentX, currentY;
2528 GetPosition(&currentX, &currentY);
443c834d 2529 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
b9f29261 2530 x = currentX;
443c834d 2531 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
b9f29261 2532 y = currentY;
a200c35e
VS
2533 AdjustForParentClientOrigin(x, y, sizeFlags);
2534
fe39b16a
RR
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
cca410b3 2546 const wxSize oldSize(m_width, m_height);
fe39b16a
RR
2547 if (width != -1)
2548 m_width = width;
2549 if (height != -1)
2550 m_height = height;
2551
82008f15 2552 ConstrainSize();
fe39b16a 2553
cca410b3 2554 if (m_parent->m_wxwindow)
fb1585ae 2555 {
08f53168
PC
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;
47d67540 2559
863e0817
RR
2560 int left_border = 0;
2561 int right_border = 0;
2562 int top_border = 0;
c50f1fb9 2563 int bottom_border = 0;
f03fc89f 2564
863e0817 2565 /* the default button has a border around it */
29f538ce 2566 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
c50f1fb9 2567 {
f893066b
RR
2568 GtkBorder *default_border = NULL;
2569 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2570 if (default_border)
863e0817 2571 {
f893066b
RR
2572 left_border += default_border->left;
2573 right_border += default_border->right;
2574 top_border += default_border->top;
2575 bottom_border += default_border->bottom;
6cfdfed8 2576 gtk_border_free( default_border );
863e0817 2577 }
863e0817 2578 }
c50f1fb9 2579
18ac7bef
PC
2580 DoMoveWindow( m_x - left_border,
2581 m_y - top_border,
863e0817
RR
2582 m_width+left_border+right_border,
2583 m_height+top_border+bottom_border );
54517652 2584 }
148cd9b6 2585
cca410b3 2586 if (m_width != oldSize.x || m_height != oldSize.y)
5b8a521e 2587 {
cca410b3
PC
2588 // update these variables to keep size_allocate handler
2589 // from sending another size event for this change
5b8a521e 2590 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
6d693bb4 2591
cca410b3
PC
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 );
937013e0 2597 HandleWindowEvent( event );
cca410b3 2598 }
30760ce7 2599 }
362c6693 2600}
c801d85f 2601
7317857d 2602bool wxWindowGTK::GtkShowFromOnIdle()
9390a202 2603{
f46ad98f
RR
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);
937013e0 2615 HandleWindowEvent(eventShow);
f46ad98f 2616 m_showOnIdle = false;
7317857d 2617 return true;
f46ad98f 2618 }
02761f6c 2619
7317857d
RR
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 }
02761f6c 2641
9146082c
RR
2642 wxCursor cursor = m_cursor;
2643 if (g_globalCursor.Ok()) cursor = g_globalCursor;
c50f1fb9 2644
f7a11f8c 2645 if (cursor.Ok())
9146082c 2646 {
3017f78d 2647 /* I now set the cursor anew in every OnInternalIdle call
b02da6b1
VZ
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. */
148cd9b6 2651
6552c7af 2652 if (m_wxwindow && (m_wxwindow != m_widget))
6a008b33 2653 {
08f53168 2654 GdkWindow *window = m_wxwindow->window;
6a008b33 2655 if (window)
c50f1fb9 2656 gdk_window_set_cursor( window, cursor.GetCursor() );
6a008b33
VZ
2657
2658 if (!g_globalCursor.Ok())
2659 cursor = *wxSTANDARD_CURSOR;
2660
2661 window = m_widget->window;
5e014a0c 2662 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
9146082c 2663 gdk_window_set_cursor( window, cursor.GetCursor() );
5e014a0c 2664
6a008b33 2665 }
b3d006af 2666 else if ( m_widget )
6a008b33 2667 {
9146082c 2668 GdkWindow *window = m_widget->window;
b3d006af 2669 if ( window && !GTK_WIDGET_NO_WINDOW(m_widget) )
9146082c 2670 gdk_window_set_cursor( window, cursor.GetCursor() );
6a008b33 2671 }
9146082c 2672 }
6a008b33 2673
24bd6cb9 2674 if (wxUpdateUIEvent::CanUpdate(this) && IsShown())
e39af974 2675 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
9390a202
RR
2676}
2677
1e6feb95 2678void wxWindowGTK::DoGetSize( int *width, int *height ) const
c801d85f 2679{
82b978d7 2680 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2681
fb1585ae
RR
2682 if (width) (*width) = m_width;
2683 if (height) (*height) = m_height;
362c6693 2684}
c801d85f 2685
1e6feb95 2686void wxWindowGTK::DoSetClientSize( int width, int height )
c801d85f 2687{
82b978d7
RD
2688 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2689
949ff63e
PC
2690 const wxSize size = GetSize();
2691 const wxSize clientSize = GetClientSize();
2692 SetSize(width + (size.x - clientSize.x), height + (size.y - clientSize.y));
362c6693 2693}
c801d85f 2694
1e6feb95 2695void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
c801d85f 2696{
82b978d7 2697 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2698
09bf8378
PC
2699 int w = m_width;
2700 int h = m_height;
2701
2702 if (m_wxwindow)
c801d85f 2703 {
8466fc74
PC
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 }
09bf8378 2719
08f53168
PC
2720 int border_x, border_y;
2721 WX_PIZZA(m_wxwindow)->get_border_widths(border_x, border_y);
8466fc74
PC
2722 w -= 2 * border_x;
2723 h -= 2 * border_y;
9000c624 2724
69346023
PC
2725 if (w < 0)
2726 w = 0;
2727 if (h < 0)
2728 h = 0;
1ecc4d80 2729 }
1e6feb95 2730
09bf8378
PC
2731 if (width) *width = w;
2732 if (height) *height = h;
362c6693 2733}
c801d85f 2734
1e6feb95 2735void wxWindowGTK::DoGetPosition( int *x, int *y ) const
c801d85f 2736{
82b978d7 2737 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2738
bf0c00c6
RR
2739 int dx = 0;
2740 int dy = 0;
fd8a7b0b 2741 if (!IsTopLevel() && m_parent && m_parent->m_wxwindow)
bf0c00c6 2742 {
08f53168
PC
2743 wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow);
2744 dx = pizza->m_scroll_x;
2745 dy = pizza->m_scroll_y;
bf0c00c6 2746 }
94633ad9 2747
96c8547e
JS
2748 if (m_x == -1 && m_y == -1)
2749 {
2750 GdkWindow *source = (GdkWindow *) NULL;
2751 if (m_wxwindow)
08f53168 2752 source = m_wxwindow->window;
96c8547e
JS
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
bb5521a9
VZ
2762 if (m_parent)
2763 m_parent->ScreenToClient(&org_x, &org_y);
96c8547e 2764
bfeeb7f3
PC
2765 wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
2766 wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
2767 }
96c8547e
JS
2768 }
2769
496beb3f
VS
2770 if (x) (*x) = m_x - dx;
2771 if (y) (*y) = m_y - dy;
362c6693 2772}
c801d85f 2773
1e6feb95 2774void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
c801d85f 2775{
82b978d7 2776 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2777
a2053b27
RR
2778 if (!m_widget->window) return;
2779
43a18898
RR
2780 GdkWindow *source = (GdkWindow *) NULL;
2781 if (m_wxwindow)
08f53168 2782 source = m_wxwindow->window;
43a18898
RR
2783 else
2784 source = m_widget->window;
47d67540 2785
43a18898
RR
2786 int org_x = 0;
2787 int org_y = 0;
2788 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 2789
43a18898 2790 if (!m_wxwindow)
c801d85f 2791 {
43a18898
RR
2792 if (GTK_WIDGET_NO_WINDOW (m_widget))
2793 {
2794 org_x += m_widget->allocation.x;
2795 org_y += m_widget->allocation.y;
2796 }
362c6693 2797 }
47d67540 2798
49e74855
RR
2799
2800 if (x)
2801 {
2802 if (GetLayoutDirection() == wxLayout_RightToLeft)
2803 *x = (GetClientSize().x - *x) + org_x;
fcb29b23 2804 else
49e74855
RR
2805 *x += org_x;
2806 }
fcb29b23 2807
43a18898 2808 if (y) *y += org_y;
362c6693 2809}
c801d85f 2810
1e6feb95 2811void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
c801d85f 2812{
82b978d7 2813 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2814
a2053b27
RR
2815 if (!m_widget->window) return;
2816
1ecc4d80
RR
2817 GdkWindow *source = (GdkWindow *) NULL;
2818 if (m_wxwindow)
08f53168 2819 source = m_wxwindow->window;
1ecc4d80
RR
2820 else
2821 source = m_widget->window;
47d67540 2822
1ecc4d80
RR
2823 int org_x = 0;
2824 int org_y = 0;
2825 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 2826
1ecc4d80 2827 if (!m_wxwindow)
c801d85f 2828 {
1ecc4d80
RR
2829 if (GTK_WIDGET_NO_WINDOW (m_widget))
2830 {
2831 org_x += m_widget->allocation.x;
2832 org_y += m_widget->allocation.y;
2833 }
362c6693 2834 }
47d67540 2835
49e74855
RR
2836 if (x)
2837 {
2838 if (GetLayoutDirection() == wxLayout_RightToLeft)
2839 *x = (GetClientSize().x - *x) - org_x;
fcb29b23 2840 else
49e74855
RR
2841 *x -= org_x;
2842 }
1ecc4d80 2843 if (y) *y -= org_y;
362c6693 2844}
c801d85f 2845
1e6feb95 2846bool wxWindowGTK::Show( bool show )
c801d85f 2847{
0a164d4c 2848 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
47d67540 2849
739730ca
RR
2850 if (!wxWindowBase::Show(show))
2851 {
2852 // nothing to do
0a164d4c 2853 return false;
739730ca 2854 }
8bbe427f 2855
faeaa9db 2856 if (show && m_showOnIdle)
f46ad98f 2857 {
faeaa9db 2858 // deferred
f46ad98f 2859 }
1ecc4d80 2860 else
f46ad98f 2861 {
faeaa9db
PC
2862 if (show)
2863 gtk_widget_show(m_widget);
2864 else
2865 gtk_widget_hide(m_widget);
f46ad98f
RR
2866 wxShowEvent eventShow(GetId(), show);
2867 eventShow.SetEventObject(this);
937013e0 2868 HandleWindowEvent(eventShow);
f46ad98f 2869 }
2b5f62a0 2870
0a164d4c 2871 return true;
362c6693 2872}
c801d85f 2873
47a8a4d5 2874void wxWindowGTK::DoEnable( bool enable )
fdca68a6 2875{
47a8a4d5 2876 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
1ecc4d80 2877
f03fc89f 2878 gtk_widget_set_sensitive( m_widget, enable );
6552c7af 2879 if (m_wxwindow && (m_wxwindow != m_widget))
f03fc89f 2880 gtk_widget_set_sensitive( m_wxwindow, enable );
362c6693 2881}
c801d85f 2882
1e6feb95 2883int wxWindowGTK::GetCharHeight() const
2f2aa628 2884{
82b978d7 2885 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
47d67540 2886
cc402e64
VZ
2887 wxFont font = GetFont();
2888 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
2f2aa628 2889
db885f2a 2890 PangoContext* context = gtk_widget_get_pango_context(m_widget);
bbd006c0
RR
2891
2892 if (!context)
2893 return 0;
2894
cc402e64 2895 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
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
3fe39b0c 2904 g_object_unref (layout);
7de59551 2905
f69e2009 2906 return (int) PANGO_PIXELS(rect.height);
362c6693 2907}
c801d85f 2908
1e6feb95 2909int wxWindowGTK::GetCharWidth() const
c33c4050 2910{
82b978d7 2911 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
47d67540 2912
cc402e64
VZ
2913 wxFont font = GetFont();
2914 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
47d67540 2915
db885f2a 2916 PangoContext* context = gtk_widget_get_pango_context(m_widget);
bbd006c0
RR
2917
2918 if (!context)
2919 return 0;
2920
cc402e64 2921 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
2922 PangoLayout *layout = pango_layout_new(context);
2923 pango_layout_set_font_description(layout, desc);
95c430aa 2924 pango_layout_set_text(layout, "g", 1);
bbd006c0
RR
2925 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
2926
2927 PangoRectangle rect;
2928 pango_layout_line_get_extents(line, NULL, &rect);
2929
3fe39b0c 2930 g_object_unref (layout);
7de59551 2931
f69e2009 2932 return (int) PANGO_PIXELS(rect.width);
c33c4050
RR
2933}
2934
1e6feb95 2935void wxWindowGTK::GetTextExtent( const wxString& string,
0a164d4c
WS
2936 int *x,
2937 int *y,
2938 int *descent,
2939 int *externalLeading,
2940 const wxFont *theFont ) const
c33c4050 2941{
cc402e64 2942 wxFont fontToUse = theFont ? *theFont : GetFont();
47d67540 2943
223d09f6 2944 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
2b5f62a0 2945
0a164d4c 2946 if (string.empty())
48d011c8 2947 {
b15ed747
RR
2948 if (x) (*x) = 0;
2949 if (y) (*y) = 0;
48d011c8
RR
2950 return;
2951 }
47d67540 2952
48d011c8
RR
2953 PangoContext *context = NULL;
2954 if (m_widget)
2b5f62a0
VZ
2955 context = gtk_widget_get_pango_context( m_widget );
2956
48d011c8
RR
2957 if (!context)
2958 {
b15ed747
RR
2959 if (x) (*x) = 0;
2960 if (y) (*y) = 0;
48d011c8
RR
2961 return;
2962 }
2b5f62a0 2963
48d011c8
RR
2964 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
2965 PangoLayout *layout = pango_layout_new(context);
2966 pango_layout_set_font_description(layout, desc);
2967 {
a3669332
VZ
2968 const wxCharBuffer data = wxGTK_CONV( string );
2969 if ( data )
2970 pango_layout_set_text(layout, data, strlen(data));
48d011c8 2971 }
2b5f62a0 2972
48d011c8 2973 PangoRectangle rect;
fd43b1b3 2974 pango_layout_get_extents(layout, NULL, &rect);
2b5f62a0 2975
f69e2009
VS
2976 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
2977 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
48d011c8
RR
2978 if (descent)
2979 {
f69e2009
VS
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);
48d011c8
RR
2984 }
2985 if (externalLeading) (*externalLeading) = 0; // ??
2b5f62a0 2986
3fe39b0c 2987 g_object_unref (layout);
c33c4050
RR
2988}
2989
ef5c70f9
VZ
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
00ff24c8 3006void wxWindowGTK::SetFocus()
7cec1c9e
RR
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 {
00ff24c8
RR
3017 // wxWindow::SetFocus() should really set the focus to
3018 // this control, whatever the flags are
7cec1c9e
RR
3019 if (!GTK_WIDGET_CAN_FOCUS(m_wxwindow))
3020 GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
db885f2a 3021
173348db 3022 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
b231914f 3023 {
173348db 3024 gtk_widget_grab_focus (m_wxwindow);
b231914f 3025 }
354aa1e3 3026 }
db885f2a 3027 else
c801d85f 3028 {
00ff24c8
RR
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);
db885f2a 3033
eccd5602
RR
3034 if (GTK_IS_CONTAINER(m_widget))
3035 {
00ff24c8 3036 if (GTK_IS_RADIO_BUTTON(m_widget))
ccb5db57
RR
3037 {
3038 gtk_widget_grab_focus (m_widget);
3039 return;
3040 }
fcb29b23 3041
eccd5602
RR
3042 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3043 }
3044 else
173348db 3045 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
463c1fa1 3046 {
0a164d4c 3047
d7fa7eaa 3048 if (!GTK_WIDGET_REALIZED(m_widget))
6aeb6f2a 3049 {
6cad4f1b
VZ
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)"),
6aeb6f2a
VZ
3055 GetClassInfo()->GetClassName(), GetLabel().c_str());
3056
d7fa7eaa 3057 g_delayedFocus = this;
6aeb6f2a 3058 }
d7fa7eaa 3059 else
6aeb6f2a 3060 {
6cad4f1b
VZ
3061 wxLogTrace(TRACE_FOCUS,
3062 _T("Setting focus to %s(%s)"),
6aeb6f2a
VZ
3063 GetClassInfo()->GetClassName(), GetLabel().c_str());
3064
d7fa7eaa 3065 gtk_widget_grab_focus (m_widget);
6aeb6f2a 3066 }
463c1fa1 3067 }
0a164d4c 3068 else
ff8bfdbb 3069 {
6cad4f1b
VZ
3070 wxLogTrace(TRACE_FOCUS,
3071 _T("Can't set focus to %s(%s)"),
3072 GetClassInfo()->GetClassName(), GetLabel().c_str());
ff8bfdbb 3073 }
362c6693 3074 }
362c6693 3075}
c801d85f 3076
80332672
VZ
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
1e6feb95 3093bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
463c1fa1 3094{
0a164d4c 3095 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
c50f1fb9 3096
1e6feb95
VZ
3097 wxWindowGTK *oldParent = m_parent,
3098 *newParent = (wxWindowGTK *)newParentBase;
a2053b27 3099
5fd11f09
RR
3100 wxASSERT( GTK_IS_WIDGET(m_widget) );
3101
f03fc89f 3102 if ( !wxWindowBase::Reparent(newParent) )
0a164d4c 3103 return false;
8bbe427f 3104
5fd11f09
RR
3105 wxASSERT( GTK_IS_WIDGET(m_widget) );
3106
3107 /* prevent GTK from deleting the widget arbitrarily */
3108 gtk_widget_ref( m_widget );
3109
8ce63e9d
RR
3110 if (oldParent)
3111 {
3017f78d 3112 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
8ce63e9d 3113 }
c50f1fb9 3114
5fd11f09
RR
3115 wxASSERT( GTK_IS_WIDGET(m_widget) );
3116
8ce63e9d
RR
3117 if (newParent)
3118 {
f46ad98f
RR
3119 if (GTK_WIDGET_VISIBLE (newParent->m_widget))
3120 {
3121 m_showOnIdle = true;
3122 gtk_widget_hide( m_widget );
3123 }
02761f6c 3124
8ce63e9d
RR
3125 /* insert GTK representation */
3126 (*(newParent->m_insertCallback))(newParent, this);
3127 }
c50f1fb9 3128
5fd11f09
RR
3129 /* reverse: prevent GTK from deleting the widget arbitrarily */
3130 gtk_widget_unref( m_widget );
e892f2fd 3131
978af864 3132 SetLayoutDirection(wxLayout_Default);
148cd9b6 3133
0a164d4c 3134 return true;
362c6693 3135}
c801d85f 3136
1e6feb95 3137void wxWindowGTK::DoAddChild(wxWindowGTK *child)
ddb6bc71 3138{
223d09f6 3139 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
223d09f6 3140 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
ddb6bc71 3141
ddb6bc71
RR
3142 /* add to list */
3143 AddChild( child );
c50f1fb9 3144
ddb6bc71
RR
3145 /* insert GTK representation */
3146 (*m_insertCallback)(this, child);
3147}
3148
a589495e
VS
3149void wxWindowGTK::AddChild(wxWindowBase *child)
3150{
3151 wxWindowBase::AddChild(child);
3152 m_dirtyTabOrder = true;
a1abca32 3153 wxTheApp->WakeUpIdle();
a589495e
VS
3154}
3155
3156void wxWindowGTK::RemoveChild(wxWindowBase *child)
3157{
3158 wxWindowBase::RemoveChild(child);
3159 m_dirtyTabOrder = true;
a1abca32 3160 wxTheApp->WakeUpIdle();
a589495e 3161}
0a164d4c 3162
978af864
VZ
3163/* static */
3164wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3165{
10bd1f7d 3166 return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
978af864
VZ
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
10bd1f7d 3176 gtk_widget_set_direction(widget,
978af864
VZ
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);
fcb29b23 3206
6552c7af 3207 if (m_wxwindow && (m_wxwindow != m_widget))
69597639
RR
3208 GTKSetLayout(m_wxwindow, dir);
3209}
3210
3211wxCoord
3212wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3213 wxCoord WXUNUSED(width),
3214 wxCoord WXUNUSED(widthTotal)) const
3215{
08f53168 3216 // We now mirror the coordinates of RTL windows in wxPizza
69597639 3217 return x;
978af864
VZ
3218}
3219
915bd4e4 3220void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, WindowOrder move)
a589495e
VS
3221{
3222 wxWindowBase::DoMoveInTabOrder(win, move);
3223 m_dirtyTabOrder = true;
a1abca32 3224 wxTheApp->WakeUpIdle();
a589495e
VS
3225}
3226
5644933f
VZ
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 {
7a3ba5af 3237 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
5644933f
VZ
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
2e1f5012
VZ
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
a589495e
VS
3262void wxWindowGTK::RealizeTabOrder()
3263{
3264 if (m_wxwindow)
3265 {
12848fda 3266 if ( !m_children.empty() )
a589495e 3267 {
259858fc 3268 // we don't only construct the correct focus chain but also use
2e1f5012
VZ
3269 // this opportunity to update the mnemonic widgets for the widgets
3270 // that need them
259858fc 3271
a589495e 3272 GList *chain = NULL;
2e1f5012 3273 wxWindowGTK* mnemonicWindow = NULL;
0a164d4c 3274
12848fda
VZ
3275 for ( wxWindowList::const_iterator i = m_children.begin();
3276 i != m_children.end();
3277 ++i )
a589495e 3278 {
259858fc 3279 wxWindowGTK *win = *i;
2e1f5012
VZ
3280
3281 if ( mnemonicWindow )
259858fc
VZ
3282 {
3283 if ( win->AcceptsFocusFromKeyboard() )
3284 {
2e1f5012
VZ
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 }
259858fc
VZ
3301 }
3302 }
2e1f5012 3303 else if ( win->GTKWidgetNeedsMnemonic() )
259858fc 3304 {
2e1f5012 3305 mnemonicWindow = win;
259858fc 3306 }
259858fc
VZ
3307
3308 chain = g_list_prepend(chain, win->m_widget);
a589495e 3309 }
0a164d4c 3310
a589495e 3311 chain = g_list_reverse(chain);
0a164d4c 3312
a589495e
VS
3313 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3314 g_list_free(chain);
3315 }
12848fda 3316 else // no children
a589495e
VS
3317 {
3318 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3319 }
3320 }
a589495e
VS
3321}
3322
1e6feb95 3323void wxWindowGTK::Raise()
362c6693 3324{
82b978d7
RD
3325 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3326
fdfb8475
RR
3327 if (m_wxwindow && m_wxwindow->window)
3328 {
3329 gdk_window_raise( m_wxwindow->window );
3330 }
0a164d4c 3331 else if (m_widget->window)
fdfb8475
RR
3332 {
3333 gdk_window_raise( m_widget->window );
3334 }
362c6693
RR
3335}
3336
1e6feb95 3337void wxWindowGTK::Lower()
362c6693 3338{
82b978d7
RD
3339 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3340
fdfb8475
RR
3341 if (m_wxwindow && m_wxwindow->window)
3342 {
3343 gdk_window_lower( m_wxwindow->window );
3344 }
0a164d4c 3345 else if (m_widget->window)
fdfb8475
RR
3346 {
3347 gdk_window_lower( m_widget->window );
3348 }
362c6693 3349}
c801d85f 3350
1e6feb95 3351bool wxWindowGTK::SetCursor( const wxCursor &cursor )
86b29a61 3352{
fe282c08 3353 if ( !wxWindowBase::SetCursor(cursor.Ok() ? cursor : *wxSTANDARD_CURSOR) )
2f262021 3354 return false;
f6bcfd97 3355
2f262021 3356 GTKUpdateCursor();
1e6feb95 3357
2f262021 3358 return true;
ef5c70f9
VZ
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 {
5dd7eef7
VZ
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());
ef5c70f9
VZ
3385 }
3386 }
3387 }
362c6693 3388}
c801d85f 3389
1e6feb95 3390void wxWindowGTK::WarpPointer( int x, int y )
4f22cf8d 3391{
82b978d7
RD
3392 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3393
3bcc8d15
RR
3394 // We provide this function ourselves as it is
3395 // missing in GDK (top of this file).
148cd9b6 3396
ed673c6a
RR
3397 GdkWindow *window = (GdkWindow*) NULL;
3398 if (m_wxwindow)
08f53168 3399 window = m_wxwindow->window;
ed673c6a
RR
3400 else
3401 window = GetConnectWidget()->window;
148cd9b6 3402
ed673c6a
RR
3403 if (window)
3404 gdk_window_warp_pointer( window, x, y );
4f22cf8d
RR
3405}
3406
22c9b211 3407wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
0c131a5a 3408{
22c9b211
VZ
3409 // find the scrollbar which generated the event
3410 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
0c131a5a 3411 {
22c9b211
VZ
3412 if ( range == m_scrollBar[dir] )
3413 return (ScrollDir)dir;
0c131a5a 3414 }
22c9b211
VZ
3415
3416 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3417
3418 return ScrollDir_Max;
0c131a5a
VZ
3419}
3420
22c9b211 3421bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
0c131a5a 3422{
add7cadd 3423 bool changed = false;
22c9b211
VZ
3424 GtkRange* range = m_scrollBar[dir];
3425 if ( range && units )
add7cadd
PC
3426 {
3427 GtkAdjustment* adj = range->adjustment;
22c9b211
VZ
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;
add7cadd 3435 }
22c9b211 3436
add7cadd 3437 return changed;
0c131a5a 3438}
3013a903 3439
22c9b211
VZ
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
e4161a2a
VZ
3450void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
3451 const wxRect *rect)
c801d85f 3452{
a67f1484
VZ
3453 if (!m_widget)
3454 return;
3455 if (!m_widget->window)
3456 return;
2b5f62a0 3457
4e5a4c69
RR
3458 if (m_wxwindow)
3459 {
08f53168 3460 if (m_wxwindow->window == NULL) return;
02761f6c 3461
a67f1484
VZ
3462 GdkRectangle gdk_rect,
3463 *p;
4e5a4c69
RR
3464 if (rect)
3465 {
4e5a4c69
RR
3466 gdk_rect.x = rect->x;
3467 gdk_rect.y = rect->y;
3468 gdk_rect.width = rect->width;
3469 gdk_rect.height = rect->height;
49e74855
RR
3470 if (GetLayoutDirection() == wxLayout_RightToLeft)
3471 gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
807a572e 3472
a67f1484 3473 p = &gdk_rect;
4e5a4c69 3474 }
a67f1484 3475 else // invalidate everything
4e5a4c69 3476 {
a67f1484 3477 p = NULL;
4e5a4c69 3478 }
fcb29b23 3479
08f53168 3480 gdk_window_invalidate_rect(m_wxwindow->window, p, true);
4e5a4c69 3481 }
362c6693 3482}
c801d85f 3483
beab25bd 3484void wxWindowGTK::Update()
010afced
RR
3485{
3486 GtkUpdate();
1b965a9c
VZ
3487
3488 // when we call Update() we really want to update the window immediately on
90e572f1 3489 // screen, even if it means flushing the entire queue and hence slowing down
1b965a9c
VZ
3490 // everything -- but it should still be done, it's just that Update() should
3491 // be called very rarely
3492 gdk_flush();
010afced
RR
3493}
3494
3495void wxWindowGTK::GtkUpdate()
beab25bd 3496{
08f53168
PC
3497 if (m_wxwindow && m_wxwindow->window)
3498 gdk_window_process_updates(m_wxwindow->window, false);
6552c7af 3499 if (m_widget && m_widget->window && (m_wxwindow != m_widget))
6bdeda47 3500 gdk_window_process_updates( m_widget->window, FALSE );
a67f1484
VZ
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 }
beab25bd
RR
3511}
3512
657b4fd4 3513bool wxWindowGTK::DoIsExposed( int x, int y ) const
847dfdb4
RR
3514{
3515 return m_updateRegion.Contains(x, y) != wxOutRegion;
3516}
3517
3518
657b4fd4 3519bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
847dfdb4
RR
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
beab25bd
RR
3527void wxWindowGTK::GtkSendPaintEvents()
3528{
3bcc8d15
RR
3529 if (!m_wxwindow)
3530 {
3bcc8d15
RR
3531 m_updateRegion.Clear();
3532 return;
3533 }
beab25bd 3534
f90566f5 3535 // Clip to paint region in wxClientDC
0a164d4c 3536 m_clipPaintRegion = true;
fab591c5 3537
bcb614b3
RR
3538 m_nativeUpdateRegion = m_updateRegion;
3539
847dfdb4
RR
3540 if (GetLayoutDirection() == wxLayout_RightToLeft)
3541 {
bcb614b3
RR
3542 // Transform m_updateRegion under RTL
3543 m_updateRegion.Clear();
fcb29b23 3544
847dfdb4 3545 gint width;
08f53168 3546 gdk_drawable_get_size(m_wxwindow->window, &width, NULL);
fcb29b23 3547
bcb614b3 3548 wxRegionIterator upd( m_nativeUpdateRegion );
847dfdb4
RR
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();
fcb29b23 3556
847dfdb4 3557 rect.x = width - rect.x - rect.width;
bcb614b3 3558 m_updateRegion.Union( rect );
fcb29b23 3559
847dfdb4
RR
3560 ++upd;
3561 }
3562 }
fcb29b23 3563
aac97549 3564 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
f90566f5
RR
3565 {
3566 // find ancestor from which to steal background
cd5e74ba 3567 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
f90566f5 3568 if (!parent)
cc06fe74 3569 parent = (wxWindow*)this;
2b5f62a0 3570
822cf31c 3571 if (GTK_WIDGET_MAPPED(parent->m_widget))
f90566f5 3572 {
bcb614b3 3573 wxRegionIterator upd( m_nativeUpdateRegion );
822cf31c
KH
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,
08f53168 3583 m_wxwindow->window,
822cf31c
KH
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
60d8e886 3591 ++upd;
822cf31c 3592 }
f90566f5
RR
3593 }
3594 }
3595 else
b15ed747
RR
3596 {
3597 wxWindowDC dc( (wxWindow*)this );
3598 dc.SetClippingRegion( m_updateRegion );
3599
8ab7b4c5
JS
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
b15ed747
RR
3608 wxEraseEvent erase_event( GetId(), &dc );
3609 erase_event.SetEventObject( this );
3610
937013e0 3611 HandleWindowEvent(erase_event);
b15ed747 3612 }
03eaa52a 3613
beab25bd
RR
3614 wxNcPaintEvent nc_paint_event( GetId() );
3615 nc_paint_event.SetEventObject( this );
937013e0 3616 HandleWindowEvent( nc_paint_event );
03eaa52a 3617
beab25bd
RR
3618 wxPaintEvent paint_event( GetId() );
3619 paint_event.SetEventObject( this );
937013e0 3620 HandleWindowEvent( paint_event );
beab25bd 3621
0a164d4c 3622 m_clipPaintRegion = false;
c89f5c02 3623
c89f5c02 3624 m_updateRegion.Clear();
bcb614b3 3625 m_nativeUpdateRegion.Clear();
beab25bd
RR
3626}
3627
8e1a5bf9
VZ
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
2e992e06
VZ
3636bool wxWindowGTK::IsDoubleBuffered() const
3637{
3638 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
3639}
3640
596f1d11 3641void wxWindowGTK::ClearBackground()
c801d85f 3642{
82b978d7 3643 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
362c6693 3644}
c801d85f 3645
ff8bfdbb 3646#if wxUSE_TOOLTIPS
1e6feb95 3647void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
b1170810 3648{
f03fc89f 3649 wxWindowBase::DoSetToolTip(tip);
ff8bfdbb 3650
f03fc89f 3651 if (m_tooltip)
3379ed37 3652 m_tooltip->Apply( (wxWindow *)this );
b1170810
RR
3653}
3654
6e5f6c7c 3655void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const gchar *tip )
b1170810 3656{
6e5f6c7c 3657 gtk_tooltips_set_tip(tips, GetConnectWidget(), tip, NULL);
301cd871 3658}
ff8bfdbb 3659#endif // wxUSE_TOOLTIPS
b1170810 3660
1e6feb95 3661bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
c801d85f 3662{
0a164d4c 3663 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
8bbe427f 3664
739730ca 3665 if (!wxWindowBase::SetBackgroundColour(colour))
44dfb5ce 3666 return false;
c50f1fb9 3667
5edef14e 3668 if (colour.Ok())
994bc575 3669 {
5edef14e
VS
3670 // We need the pixel value e.g. for background clearing.
3671 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
994bc575 3672 }
ca298c88 3673
5edef14e 3674 // apply style change (forceStyle=true so that new style is applied
c7382f91
JS
3675 // even if the bg colour changed from valid to wxNullColour)
3676 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3677 ApplyWidgetStyle(true);
ea323db3 3678
5edef14e 3679 return true;
6de97a3b
RR
3680}
3681
1e6feb95 3682bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
6de97a3b 3683{
0a164d4c 3684 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
8bbe427f 3685
739730ca
RR
3686 if (!wxWindowBase::SetForegroundColour(colour))
3687 {
5edef14e 3688 return false;
739730ca 3689 }
0a164d4c 3690
5edef14e 3691 if (colour.Ok())
ea323db3 3692 {
5edef14e
VS
3693 // We need the pixel value e.g. for background clearing.
3694 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
ea323db3 3695 }
f03fc89f 3696
5edef14e
VS
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
44dfb5ce 3701 return true;
58614078
RR
3702}
3703
2b5f62a0
VZ
3704PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3705{
3706 return gtk_widget_get_pango_context( m_widget );
3707}
0a164d4c 3708
5edef14e 3709GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
58614078 3710{
f40fdaa3 3711 // do we need to apply any changes at all?
5edef14e 3712 if ( !forceStyle &&
984e8d0b
VS
3713 !m_font.Ok() &&
3714 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
fb65642c 3715 {
f40fdaa3 3716 return NULL;
fb65642c
RR
3717 }
3718
f40fdaa3 3719 GtkRcStyle *style = gtk_rc_style_new();
1ecc4d80 3720
984e8d0b 3721 if ( m_font.Ok() )
db434467 3722 {
0a164d4c 3723 style->font_desc =
f40fdaa3 3724 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
288059b2 3725 }
1ecc4d80 3726
532ae0f6
VZ
3727 int flagsNormal = 0,
3728 flagsPrelight = 0,
3729 flagsActive = 0,
3730 flagsInsensitive = 0;
3731
fe161a26 3732 if ( m_foregroundColour.Ok() )
1ecc4d80 3733 {
c6685317 3734 const GdkColor *fg = m_foregroundColour.GetColor();
0a164d4c 3735
532ae0f6
VZ
3736 style->fg[GTK_STATE_NORMAL] =
3737 style->text[GTK_STATE_NORMAL] = *fg;
3738 flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
0a164d4c 3739
532ae0f6
VZ
3740 style->fg[GTK_STATE_PRELIGHT] =
3741 style->text[GTK_STATE_PRELIGHT] = *fg;
3742 flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
0a164d4c 3743
532ae0f6
VZ
3744 style->fg[GTK_STATE_ACTIVE] =
3745 style->text[GTK_STATE_ACTIVE] = *fg;
3746 flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
1ecc4d80
RR
3747 }
3748
fe161a26 3749 if ( m_backgroundColour.Ok() )
1ecc4d80 3750 {
c6685317 3751 const GdkColor *bg = m_backgroundColour.GetColor();
5edef14e 3752
532ae0f6 3753 style->bg[GTK_STATE_NORMAL] =
5edef14e 3754 style->base[GTK_STATE_NORMAL] = *bg;
532ae0f6 3755 flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 3756
532ae0f6 3757 style->bg[GTK_STATE_PRELIGHT] =
5edef14e 3758 style->base[GTK_STATE_PRELIGHT] = *bg;
532ae0f6 3759 flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 3760
532ae0f6 3761 style->bg[GTK_STATE_ACTIVE] =
5edef14e 3762 style->base[GTK_STATE_ACTIVE] = *bg;
532ae0f6 3763 flagsActive |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 3764
532ae0f6 3765 style->bg[GTK_STATE_INSENSITIVE] =
5edef14e 3766 style->base[GTK_STATE_INSENSITIVE] = *bg;
532ae0f6 3767 flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
1ecc4d80 3768 }
0a164d4c 3769
532ae0f6
VZ
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
f40fdaa3 3775 return style;
a81258be
RR
3776}
3777
f8e045e2 3778void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
a81258be 3779{
f8e045e2
RD
3780 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3781 if ( style )
3782 {
7074ce35 3783 DoApplyWidgetStyle(style);
f8e045e2
RD
3784 gtk_rc_style_unref(style);
3785 }
6dd18972
VS
3786
3787 // Style change may affect GTK+'s size calculation:
3788 InvalidateBestSize();
6de97a3b
RR
3789}
3790
7074ce35
VS
3791void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3792{
08809d18 3793 wxSuspendStyleEvents s(static_cast<wxWindow*>(this));
013151c7 3794
7074ce35 3795 if (m_wxwindow)
7074ce35 3796 gtk_widget_modify_style(m_wxwindow, style);
7cb93e45
RR
3797 else
3798 gtk_widget_modify_style(m_widget, style);
7074ce35
VS
3799}
3800
c7382f91
JS
3801bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3802{
3803 wxWindowBase::SetBackgroundStyle(style);
0a164d4c 3804
c7382f91
JS
3805 if (style == wxBG_STYLE_CUSTOM)
3806 {
788f9633
VZ
3807 GdkWindow *window;
3808 if ( m_wxwindow )
3809 {
08f53168 3810 window = m_wxwindow->window;
788f9633 3811 }
c7382f91 3812 else
788f9633
VZ
3813 {
3814 GtkWidget * const w = GetConnectWidget();
3815 window = w ? w->window : NULL;
3816 }
c7382f91
JS
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 }
788f9633
VZ
3829 else // window not realized yet
3830 {
c7382f91
JS
3831 // Do in OnIdle, because the window is not yet available
3832 m_needsStyleChange = true;
788f9633 3833 }
0a164d4c 3834
c7382f91
JS
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}
7074ce35 3845
06cfab17 3846#if wxUSE_DRAG_AND_DROP
ac57418f 3847
1e6feb95 3848void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
c801d85f 3849{
82b978d7
RD
3850 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3851
1ecc4d80 3852 GtkWidget *dnd_widget = GetConnectWidget();
47d67540 3853
1ecc4d80 3854 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
47d67540 3855
1ecc4d80
RR
3856 if (m_dropTarget) delete m_dropTarget;
3857 m_dropTarget = dropTarget;
47d67540 3858
1ecc4d80 3859 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
362c6693 3860}
c801d85f 3861
f03fc89f 3862#endif // wxUSE_DRAG_AND_DROP
ac57418f 3863
1e6feb95 3864GtkWidget* wxWindowGTK::GetConnectWidget()
e3e65dac 3865{
1ecc4d80
RR
3866 GtkWidget *connect_widget = m_widget;
3867 if (m_wxwindow) connect_widget = m_wxwindow;
47d67540 3868
1ecc4d80 3869 return connect_widget;
e3e65dac 3870}
47d67540 3871
ef5c70f9 3872bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
903f689b 3873{
ef5c70f9
VZ
3874 wxArrayGdkWindows windowsThis;
3875 GdkWindow * const winThis = GTKGetWindow(windowsThis);
148cd9b6 3876
ef5c70f9
VZ
3877 return winThis ? window == winThis
3878 : windowsThis.Index(window) != wxNOT_FOUND;
3879}
3880
3881GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
3882{
08f53168 3883 return m_wxwindow ? m_wxwindow->window : m_widget->window;
903f689b
RR
3884}
3885
1e6feb95 3886bool wxWindowGTK::SetFont( const wxFont &font )
c801d85f 3887{
0a164d4c 3888 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
9c288e4d 3889
5edef14e
VS
3890 if (!wxWindowBase::SetFont(font))
3891 return false;
c801d85f 3892
5edef14e
VS
3893 // apply style change (forceStyle=true so that new style is applied
3894 // even if the font changed from valid to wxNullFont):
0a164d4c 3895 ApplyWidgetStyle(true);
5edef14e
VS
3896
3897 return true;
362c6693 3898}
c801d85f 3899
94633ad9 3900void wxWindowGTK::DoCaptureMouse()
c801d85f 3901{
82b978d7
RD
3902 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3903
ed673c6a 3904 GdkWindow *window = (GdkWindow*) NULL;
b231914f 3905 if (m_wxwindow)
08f53168 3906 window = m_wxwindow->window;
ed673c6a 3907 else
b231914f 3908 window = GetConnectWidget()->window;
148cd9b6 3909
e4606ed9 3910 wxCHECK_RET( window, _T("CaptureMouse() failed") );
c50f1fb9 3911
f516d986 3912 const wxCursor* cursor = &m_cursor;
cca602ac
JS
3913 if (!cursor->Ok())
3914 cursor = wxSTANDARD_CURSOR;
3915
ed673c6a 3916 gdk_pointer_grab( window, FALSE,
1ecc4d80
RR
3917 (GdkEventMask)
3918 (GDK_BUTTON_PRESS_MASK |
3919 GDK_BUTTON_RELEASE_MASK |
148cd9b6 3920 GDK_POINTER_MOTION_HINT_MASK |
1ecc4d80 3921 GDK_POINTER_MOTION_MASK),
ff8bfdbb 3922 (GdkWindow *) NULL,
cca602ac 3923 cursor->GetCursor(),
b02da6b1 3924 (guint32)GDK_CURRENT_TIME );
b231914f 3925 g_captureWindow = this;
0a164d4c 3926 g_captureWindowHasMouse = true;
362c6693 3927}
c801d85f 3928
94633ad9 3929void wxWindowGTK::DoReleaseMouse()
c801d85f 3930{
82b978d7
RD
3931 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3932
e4606ed9 3933 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
47d67540 3934
c43430bb
VS
3935 g_captureWindow = (wxWindowGTK*) NULL;
3936
ed673c6a 3937 GdkWindow *window = (GdkWindow*) NULL;
b231914f 3938 if (m_wxwindow)
08f53168 3939 window = m_wxwindow->window;
ed673c6a 3940 else
b231914f 3941 window = GetConnectWidget()->window;
148cd9b6 3942
b02da6b1
VZ
3943 if (!window)
3944 return;
c50f1fb9 3945
b02da6b1 3946 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
362c6693 3947}
c801d85f 3948
7738af59
VZ
3949void wxWindowGTK::GTKReleaseMouseAndNotify()
3950{
3951 DoReleaseMouse();
3952 wxMouseCaptureLostEvent evt(GetId());
3953 evt.SetEventObject( this );
937013e0 3954 HandleWindowEvent( evt );
7738af59
VZ
3955}
3956
1e6feb95
VZ
3957/* static */
3958wxWindow *wxWindowBase::GetCapture()
3959{
3960 return (wxWindow *)g_captureWindow;
3961}
3962
3963bool wxWindowGTK::IsRetained() const
c801d85f 3964{
0a164d4c 3965 return false;
362c6693 3966}
c801d85f 3967
22c9b211
VZ
3968void wxWindowGTK::SetScrollbar(int orient,
3969 int pos,
3970 int thumbVisible,
3971 int range,
3972 bool WXUNUSED(update))
c801d85f 3973{
63c95f27
PC
3974 const int dir = ScrollDirFromOrient(orient);
3975 GtkRange* const sb = m_scrollBar[dir];
b9e7f011 3976 wxCHECK_RET( sb, _T("this window is not scrollable") );
c801d85f 3977
8466fc74 3978 if (range <= 0)
de7bb802
PC
3979 {
3980 // GtkRange requires upper > lower
3981 range =
3982 thumbVisible = 1;
3983 }
47d67540 3984
b9e7f011 3985 GtkAdjustment * const adj = sb->adjustment;
add7cadd
PC
3986 adj->step_increment = 1;
3987 adj->page_increment =
3988 adj->page_size = thumbVisible;
63c95f27
PC
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);
87a3ebe9
VZ
3999}
4000
22c9b211 4001void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
c801d85f 4002{
b9e7f011
VZ
4003 const int dir = ScrollDirFromOrient(orient);
4004 GtkRange * const sb = m_scrollBar[dir];
4005 wxCHECK_RET( sb, _T("this window is not scrollable") );
1ecc4d80 4006
add7cadd
PC
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)
47d67540 4010 {
63c95f27
PC
4011 g_signal_handlers_block_by_func(
4012 sb, (void*)gtk_scrollbar_value_changed, this);
40e5ebbf 4013
63c95f27
PC
4014 gtk_range_set_value(sb, pos);
4015 m_scrollPos[dir] = sb->adjustment->value;
98264520 4016
63c95f27
PC
4017 g_signal_handlers_unblock_by_func(
4018 sb, (void*)gtk_scrollbar_value_changed, this);
cb43b372 4019 }
362c6693 4020}
c801d85f 4021
22c9b211 4022int wxWindowGTK::GetScrollThumb(int orient) const
c801d85f 4023{
b9e7f011
VZ
4024 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4025 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
47d67540 4026
b9e7f011 4027 return int(sb->adjustment->page_size);
362c6693 4028}
c801d85f 4029
1e6feb95 4030int wxWindowGTK::GetScrollPos( int orient ) const
c801d85f 4031{
b9e7f011
VZ
4032 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4033 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
c801d85f 4034
b9e7f011 4035 return int(sb->adjustment->value + 0.5);
362c6693 4036}
c801d85f 4037
1e6feb95 4038int wxWindowGTK::GetScrollRange( int orient ) const
c801d85f 4039{
b9e7f011
VZ
4040 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4041 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
c801d85f 4042
b9e7f011 4043 return int(sb->adjustment->upper);
add7cadd
PC
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
38009079 4055wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
add7cadd 4056{
add7cadd
PC
4057 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4058
4059 const int barIndex = range == m_scrollBar[1];
4060 GtkAdjustment* adj = range->adjustment;
fcb29b23 4061
add7cadd 4062 const int value = int(adj->value + 0.5);
fcb29b23 4063
add7cadd
PC
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
8ea30e36 4069 if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
add7cadd
PC
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;
362c6693 4096}
c801d85f 4097
1e6feb95 4098void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
c801d85f 4099{
82b978d7
RD
4100 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4101
223d09f6 4102 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
2b5f62a0 4103
f47ae6e7 4104 // No scrolling requested.
8e217128 4105 if ((dx == 0) && (dy == 0)) return;
63c95f27 4106
0a164d4c 4107 m_clipPaintRegion = true;
0fc5dbf5 4108
08f53168 4109 WX_PIZZA(m_wxwindow)->scroll(dx, dy);
0fc5dbf5 4110
0a164d4c 4111 m_clipPaintRegion = false;
113faca1 4112
231018bd 4113#if wxUSE_CARET
113faca1
JS
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
fcb29b23 4121 {
113faca1 4122 caretRect.x += dx; caretRect.width -= dx;
fcb29b23 4123 }
113faca1
JS
4124 if (dy > 0)
4125 caretRect.height += dy;
4126 else
fcb29b23 4127 {
113faca1 4128 caretRect.y += dy; caretRect.height -= dy;
fcb29b23
VZ
4129 }
4130
113faca1
JS
4131 RefreshRect(caretRect);
4132 }
fcb29b23 4133#endif // wxUSE_CARET
c801d85f 4134}
3723b7b1 4135
6493aaca
VZ
4136void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4137{
4138 //RN: Note that static controls usually have no border on gtk, so maybe
88a7a4e1 4139 //it makes sense to treat that as simply no border at the wx level
6493aaca
VZ
4140 //as well...
4141 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4142 {
4143 GtkShadowType gtkstyle;
88a7a4e1 4144
6493aaca
VZ
4145 if(wxstyle & wxBORDER_RAISED)
4146 gtkstyle = GTK_SHADOW_OUT;
4147 else if (wxstyle & wxBORDER_SUNKEN)
4148 gtkstyle = GTK_SHADOW_IN;
78cd9c69
JS
4149#if 0
4150 // Now obsolete
6493aaca
VZ
4151 else if (wxstyle & wxBORDER_DOUBLE)
4152 gtkstyle = GTK_SHADOW_ETCHED_IN;
78cd9c69 4153#endif
6493aaca
VZ
4154 else //default
4155 gtkstyle = GTK_SHADOW_IN;
4156
88a7a4e1 4157 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
6493aaca
VZ
4158 gtkstyle );
4159 }
4160}
4161
015dca24
MR
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}
4e5a4c69 4167
3723b7b1
JS
4168// Find the wxWindow at the current mouse position, also returning the mouse
4169// position.
4170wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4171{
59a12e90
JS
4172 pt = wxGetMousePosition();
4173 wxWindow* found = wxFindWindowAtPoint(pt);
4174 return found;
3723b7b1
JS
4175}
4176
4177// Get the current mouse position.
4178wxPoint wxGetMousePosition()
4179{
59a12e90
JS
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
3723b7b1
JS
4189 int x, y;
4190 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
59a12e90 4191
37d81cc2 4192 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
59a12e90
JS
4193 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4194 Window rootReturn, childReturn;
4195 int rootX, rootY, winX, winY;
4196 unsigned int maskReturn;
4197
4198 XQueryPointer (display,
5cd09f0b
RR
4199 rootWindow,
4200 &rootReturn,
59a12e90
JS
4201 &childReturn,
4202 &rootX, &rootY, &winX, &winY, &maskReturn);
4203 return wxPoint(rootX, rootY);
4204
3723b7b1
JS
4205}
4206
08f53168
PC
4207GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
4208{
4209 GdkWindow* window = NULL;
4210 if (m_wxwindow)
4211 window = m_wxwindow->window;
4212 return window;
4213}