1///////////////////////////////////////////////////////////////////////////// 2// Name: src/gtk/popupwin.cpp 3// Purpose: 4// Author: Robert Roebling 5// Id: $Id: popupwin.cpp 41045 2006-09-07 16:06:47Z PC $ 6// Copyright: (c) 1998 Robert Roebling 7// Licence: wxWindows licence 8///////////////////////////////////////////////////////////////////////////// 9 10// For compilers that support precompilation, includes "wx.h". 11#include "wx/wxprec.h" 12 13#if wxUSE_POPUPWIN 14 15#include "wx/popupwin.h" 16 17#ifndef WX_PRECOMP 18 #include "wx/app.h" 19 #include "wx/frame.h" 20 #include "wx/cursor.h" 21#endif // WX_PRECOMP 22 23#include <gdk/gdk.h> 24#include <gtk/gtk.h> 25#include <gdk/gdkkeysyms.h> 26 27#include "wx/gtk/private.h" //for idle stuff 28#include "wx/gtk/win_gtk.h" 29 30//----------------------------------------------------------------------------- 31// "button_press" 32//----------------------------------------------------------------------------- 33 34extern "C" { 35static gint gtk_popup_button_press (GtkWidget *widget, GdkEvent *gdk_event, wxPopupWindow* win ) 36{ 37 GtkWidget *child = gtk_get_event_widget (gdk_event); 38 39 /* Ignore events sent out before we connected to the signal */ 40 if (win->m_time >= ((GdkEventButton*)gdk_event)->time) 41 return FALSE; 42 43 /* We don't ask for button press events on the grab widget, so 44 * if an event is reported directly to the grab widget, it must 45 * be on a window outside the application (and thus we remove 46 * the popup window). Otherwise, we check if the widget is a child 47 * of the grab widget, and only remove the popup window if it 48 * is not. */ 49 if (child != widget) 50 { 51 while (child) 52 { 53 if (child == widget) 54 return FALSE; 55 child = child->parent; 56 } 57 } 58 59 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() ); 60 event.SetEventObject( win ); 61 62 (void)win->GetEventHandler()->ProcessEvent( event ); 63 64 return TRUE; 65} 66} 67 68//----------------------------------------------------------------------------- 69// "focus" from m_window 70//----------------------------------------------------------------------------- 71 72extern "C" { 73static gint gtk_dialog_focus_callback( GtkWidget *widget, GtkDirectionType WXUNUSED(d), wxWindow *WXUNUSED(win) ) 74{ 75 if (g_isIdle) 76 wxapp_install_idle_handler(); 77 78 /* This disables GTK's tab traversal */ 79 return TRUE; 80} 81} 82 83//----------------------------------------------------------------------------- 84// "delete_event" 85//----------------------------------------------------------------------------- 86 87extern "C" { 88bool gtk_dialog_delete_callback( GtkWidget *WXUNUSED(widget), GdkEvent *WXUNUSED(event), wxPopupWindow *win ) 89{ 90 if (g_isIdle) 91 wxapp_install_idle_handler(); 92 93 if (win->IsEnabled()) 94 win->Close(); 95 96 return TRUE; 97} 98} 99 100//----------------------------------------------------------------------------- 101// "realize" from m_widget 102//----------------------------------------------------------------------------- 103 104/* we cannot MWM hints and icons before the widget has been realized, 105 so we do this directly after realization */ 106 107extern "C" { 108static gint 109gtk_dialog_realized_callback( GtkWidget * WXUNUSED(widget), wxPopupWindow *win ) 110{ 111 if (g_isIdle) 112 wxapp_install_idle_handler(); 113 114 /* all this is for Motif Window Manager "hints" and is supposed to be 115 recognized by other WM as well. not tested. */ 116 long decor = (long) GDK_DECOR_BORDER; 117 long func = (long) GDK_FUNC_MOVE ; 118 119 gdk_window_set_decorations( win->m_widget->window, (GdkWMDecoration)decor); 120 gdk_window_set_functions( win->m_widget->window, (GdkWMFunction)func); 121 122 gtk_window_set_resizable(GTK_WINDOW(win->m_widget), FALSE); 123 124 return FALSE; 125} 126} 127 128//----------------------------------------------------------------------------- 129// InsertChild for wxPopupWindow 130//----------------------------------------------------------------------------- 131 132/* Callback for wxFrame. This very strange beast has to be used because 133 * C++ has no virtual methods in a constructor. We have to emulate a 134 * virtual function here as wxWidgets requires different ways to insert 135 * a child in container classes. */ 136 137static void wxInsertChildInDialog( wxPopupWindow* parent, wxWindow* child ) 138{ 139 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow), 140 GTK_WIDGET(child->m_widget), 141 child->m_x, 142 child->m_y, 143 child->m_width, 144 child->m_height ); 145 146 if (parent->HasFlag(wxTAB_TRAVERSAL)) 147 { 148 /* we now allow a window to get the focus as long as it 149 doesn't have any children. */ 150 GTK_WIDGET_UNSET_FLAGS( parent->m_wxwindow, GTK_CAN_FOCUS ); 151 } 152} 153 154//----------------------------------------------------------------------------- 155// wxPopupWindow 156//----------------------------------------------------------------------------- 157 158BEGIN_EVENT_TABLE(wxPopupWindow,wxPopupWindowBase) 159#ifdef __WXUNIVERSAL__ 160 EVT_SIZE(wxPopupWindow::OnSize) 161#endif 162END_EVENT_TABLE() 163 164wxPopupWindow::~wxPopupWindow() 165{ 166} 167 168bool wxPopupWindow::Create( wxWindow *parent, int style ) 169{ 170 m_needParent = false; 171 172 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) || 173 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("popup") )) 174 { 175 wxFAIL_MSG( wxT("wxPopupWindow creation failed") ); 176 return false; 177 } 178 179 // Unlike windows, top level windows are created hidden by default. 180 m_isShown = false; 181 182 // All dialogs should really have this style 183 m_windowStyle |= wxTAB_TRAVERSAL; 184 185 m_insertCallback = (wxInsertChildFunction) wxInsertChildInDialog; 186 187 m_widget = gtk_window_new( GTK_WINDOW_POPUP ); 188 189 if ((m_parent) && (GTK_IS_WINDOW(m_parent->m_widget))) 190 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) ); 191 192 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); 193 194 g_signal_connect (m_widget, "delete_event", 195 G_CALLBACK (gtk_dialog_delete_callback), this); 196 197 m_wxwindow = gtk_pizza_new(); 198 gtk_widget_show( m_wxwindow ); 199 GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS ); 200 201 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow ); 202 203 if (m_parent) m_parent->AddChild( this ); 204 205 PostCreation(); 206 207 /* we cannot set MWM hints before the widget has 208 been realized, so we do this directly after realization */ 209 g_signal_connect (m_widget, "realize", 210 G_CALLBACK (gtk_dialog_realized_callback), this); 211 212 // disable native tab traversal 213 g_signal_connect (m_widget, "focus", 214 G_CALLBACK (gtk_dialog_focus_callback), this); 215 216 m_time = gtk_get_current_event_time(); 217 218 g_signal_connect (m_widget, "button_press_event", 219 G_CALLBACK (gtk_popup_button_press), this); 220 221 return true; 222} 223 224void wxPopupWindow::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) ) 225{ 226 wxFAIL_MSG( wxT("DoMoveWindow called for wxPopupWindow") ); 227} 228 229void wxPopupWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) 230{ 231 wxASSERT_MSG( (m_widget != NULL), wxT("invalid dialog") ); 232 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid dialog") ); 233 234 if (m_resizing) return; /* I don't like recursions */ 235 m_resizing = true; 236 237 int old_x = m_x; 238 int old_y = m_y; 239 240 int old_width = m_width; 241 int old_height = m_height; 242 243 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0) 244 { 245 if (x != -1) m_x = x; 246 if (y != -1) m_y = y; 247 if (width != -1) m_width = width; 248 if (height != -1) m_height = height; 249 } 250 else 251 { 252 m_x = x; 253 m_y = y; 254 m_width = width; 255 m_height = height; 256 } 257 258/* 259 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH) 260 { 261 if (width == -1) m_width = 80; 262 } 263 264 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT) 265 { 266 if (height == -1) m_height = 26; 267 } 268*/ 269 270 int minWidth = GetMinWidth(), 271 minHeight = GetMinHeight(), 272 maxWidth = GetMaxWidth(), 273 maxHeight = GetMaxHeight(); 274 275 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth; 276 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight; 277 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth; 278 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight; 279 280 if ((m_x != -1) || (m_y != -1)) 281 { 282 if ((m_x != old_x) || (m_y != old_y)) 283 { 284 /* we set the position here and when showing the dialog 285 for the first time in idle time */ 286 // Where does that happen in idle time? I do not see it anywhere - MR 287 gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y ); 288 } 289 } 290 291 if ((m_width != old_width) || (m_height != old_height)) 292 { 293 gtk_widget_set_size_request( m_widget, m_width, m_height ); 294 295 /* actual resizing is deferred to GtkOnSize in idle time and 296 when showing the dialog */ 297 m_sizeSet = false; 298 299 } 300 301 m_resizing = false; 302} 303 304void wxPopupWindow::GtkOnSize() 305{ 306 if (m_sizeSet) return; 307 if (!m_wxwindow) return; 308 309 /* FIXME: is this a hack? */ 310 /* Since for some reason GTK will revert to using maximum size ever set 311 for this window, we have to set geometry hints maxsize to match size 312 given. Also set the to that minsize since resizing isn't possible 313 anyway. */ 314 315 /* set size hints */ 316 gint flag = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE; // GDK_HINT_POS; 317 GdkGeometry geom; 318 geom.min_width = m_width; 319 geom.min_height = m_height; 320 geom.max_width = m_width; 321 geom.max_height = m_height; 322 gtk_window_set_geometry_hints( GTK_WINDOW(m_widget), 323 (GtkWidget*) NULL, 324 &geom, 325 (GdkWindowHints) flag ); 326 327 328 m_sizeSet = true; 329 330 wxSizeEvent event( wxSize(m_width,m_height), GetId() ); 331 event.SetEventObject( this ); 332 GetEventHandler()->ProcessEvent( event ); 333} 334 335void wxPopupWindow::OnInternalIdle() 336{ 337 if (!m_sizeSet && GTK_WIDGET_REALIZED(m_wxwindow)) 338 GtkOnSize(); 339 340 wxWindow::OnInternalIdle(); 341} 342 343bool wxPopupWindow::Show( bool show ) 344{ 345 if (show && !m_sizeSet) 346 { 347 /* by calling GtkOnSize here, we don't have to call 348 either after showing the frame, which would entail 349 much ugly flicker nor from within the size_allocate 350 handler, because GTK 1.1.X forbids that. */ 351 352 GtkOnSize(); 353 } 354 355 bool ret = wxWindow::Show( show ); 356 357 return ret; 358} 359 360#endif // wxUSE_POPUPWIN 361