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