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