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