]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkwindow.c
Some fixes for modal hint setting based on a patch from Arvind Samptur
[~andy/gtk] / gtk / gtkwindow.c
index de05e34f358b9ad7032c73641b9b312f7d7d9c5b..1d3dfea02395aa8a6caa6f1cfae7e200fd77622a 100644 (file)
@@ -213,6 +213,8 @@ static void gtk_window_transient_parent_realized   (GtkWidget  *parent,
 static void gtk_window_transient_parent_unrealized (GtkWidget  *parent,
                                                    GtkWidget  *window);
 
+static GdkScreen *gtk_window_check_screen (GtkWindow *window);
+
 static GtkWindowGeometryInfo* gtk_window_get_geometry_info         (GtkWindow    *window,
                                                                     gboolean      create);
 
@@ -261,6 +263,8 @@ static GtkBinClass *parent_class = NULL;
 static guint        window_signals[LAST_SIGNAL] = { 0 };
 static GList       *default_icon_list = NULL;
 static guint        default_icon_serial = 0;
+static gboolean     disable_startup_notification = FALSE;
+static gboolean     sent_startup_notification = FALSE;
 
 static void gtk_window_set_property (GObject         *object,
                                     guint            prop_id,
@@ -1483,6 +1487,14 @@ gtk_window_set_position (GtkWindow         *window,
   g_object_notify (G_OBJECT (window), "window_position");
 }
 
+/**
+ * gtk_window_activate_focus:
+ * @window: a #GtkWindow
+ * 
+ * Activates the current focused widget within the window.
+ * 
+ * Return value: %TRUE if a widget got activated.
+ **/
 gboolean 
 gtk_window_activate_focus (GtkWindow *window)
 {
@@ -1518,6 +1530,17 @@ gtk_window_get_focus (GtkWindow *window)
   return window->focus_widget;
 }
 
+/**
+ * gtk_window_activate_default:
+ * @window: a #GtkWindow
+ * 
+ * Activates the default widget for the window, unless the current 
+ * focused widget has been configured to receive the default action 
+ * (see #GTK_RECEIVES_DEFAULT in #GtkWidgetFlags), in which case the
+ * focused widget is activated. 
+ * 
+ * Return value: %TRUE if a widget got activated.
+ **/
 gboolean
 gtk_window_activate_default (GtkWindow *window)
 {
@@ -1559,13 +1582,30 @@ gtk_window_set_modal (GtkWindow *window,
 {
   g_return_if_fail (GTK_IS_WINDOW (window));
 
-  window->modal = modal != FALSE;
+  modal = modal != FALSE;
+  if (window->modal == modal)
+    return;
+  
+  window->modal = modal;
   
   /* adjust desired modality state */
-  if (GTK_WIDGET_VISIBLE (window) && window->modal)
-    gtk_grab_add (GTK_WIDGET (window));
-  else
-    gtk_grab_remove (GTK_WIDGET (window));
+  if (GTK_WIDGET_REALIZED (window))
+    {
+      GtkWidget *widget = GTK_WIDGET (window);
+      
+      if (window->modal)
+       gdk_window_set_modal_hint (widget->window, TRUE);
+      else
+       gdk_window_set_modal_hint (widget->window, FALSE);
+    }
+
+  if (GTK_WIDGET_VISIBLE (window))
+    {
+      if (window->modal)
+       gtk_grab_add (GTK_WIDGET (window));
+      else
+       gtk_grab_remove (GTK_WIDGET (window));
+    }
 
   g_object_notify (G_OBJECT (window), "modal");
 }
@@ -1724,6 +1764,14 @@ gtk_window_transient_parent_unrealized (GtkWidget *parent,
                         gdk_atom_intern ("WM_TRANSIENT_FOR", FALSE));
 }
 
+static void
+gtk_window_transient_parent_screen_changed (GtkWindow  *parent,
+                                           GParamSpec  *pspec,
+                                           GtkWindow   *window)
+{
+  gtk_window_set_screen (window, parent->screen);
+}
+
 static void       
 gtk_window_unset_transient_for  (GtkWindow *window)
 {
@@ -1735,6 +1783,9 @@ gtk_window_unset_transient_for  (GtkWindow *window)
       g_signal_handlers_disconnect_by_func (window->transient_parent,
                                            gtk_window_transient_parent_unrealized,
                                            window);
+      g_signal_handlers_disconnect_by_func (window->transient_parent,
+                                           gtk_window_transient_parent_screen_changed,
+                                           window);
       g_signal_handlers_disconnect_by_func (window->transient_parent,
                                            gtk_widget_destroyed,
                                            &window->transient_parent);
@@ -1797,8 +1848,11 @@ gtk_window_set_transient_for  (GtkWindow *window,
       g_signal_connect (parent, "unrealize",
                        G_CALLBACK (gtk_window_transient_parent_unrealized),
                        window);
+      g_signal_connect (parent, "notify::screen",
+                       G_CALLBACK (gtk_window_transient_parent_screen_changed),
+                       window);
       
-      window->screen = parent->screen;
+      gtk_window_set_screen (window, parent->screen);
 
       if (window->destroy_with_parent)
         connect_parent_destroyed (window);
@@ -1876,6 +1930,7 @@ gtk_window_get_type_hint (GtkWindow *window)
  * Windows may set a hint asking the desktop environment not to display
  * the window in the task bar. This function toggles this hint.
  * 
+ * Since: 2.2
  **/
 void
 gtk_window_set_skip_taskbar_hint (GtkWindow *window,
@@ -1906,6 +1961,8 @@ gtk_window_set_skip_taskbar_hint (GtkWindow *window,
  * Gets the value set by gtk_window_set_skip_taskbar_hint()
  * 
  * Return value: %TRUE if window shouldn't be in taskbar
+ * 
+ * Since: 2.2
  **/
 gboolean
 gtk_window_get_skip_taskbar_hint (GtkWindow *window)
@@ -1930,6 +1987,7 @@ gtk_window_get_skip_taskbar_hint (GtkWindow *window)
  * switcher that displays a thumbnail representation of the windows
  * on the screen.)
  * 
+ * Since: 2.2
  **/
 void
 gtk_window_set_skip_pager_hint (GtkWindow *window,
@@ -1960,6 +2018,8 @@ gtk_window_set_skip_pager_hint (GtkWindow *window,
  * Gets the value set by gtk_window_set_skip_pager_hint().
  * 
  * Return value: %TRUE if window shouldn't be in pager
+ * 
+ * Since: 2.2
  **/
 gboolean
 gtk_window_get_skip_pager_hint (GtkWindow *window)
@@ -2631,6 +2691,8 @@ load_pixbuf_verbosely (const char *filename,
  * with a pixbuf created by loading the image from @filename.
  *
  * Returns: %TRUE if setting the icon succeeded.
+ *
+ * Since: 2.2
  **/
 gboolean
 gtk_window_set_icon_from_file (GtkWindow   *window,
@@ -2711,9 +2773,11 @@ gtk_window_set_default_icon_list (GList *list)
  *
  * Sets an icon to be used as fallback for windows that haven't
  * had gtk_window_set_icon_list() called on them from a file
- * on disk. Warns on failure if @error is %NULL.
+ * on disk. Warns on failure if @err is %NULL.
  *
  * Returns: %TRUE if setting the icon succeeded.
+ *
+ * Since: 2.2
  **/
 gboolean
 gtk_window_set_default_icon_from_file (const gchar *filename,
@@ -3534,6 +3598,13 @@ gtk_window_map (GtkWidget *widget)
 
   if (window->frame)
     gdk_window_show (window->frame);
+
+  if (!disable_startup_notification &&
+      !sent_startup_notification)
+    {
+      sent_startup_notification = TRUE;
+      gdk_notify_startup_complete ();
+    }
 }
 
 static void
@@ -3708,8 +3779,7 @@ gtk_window_realize (GtkWidget *widget)
   if (gtk_window_get_skip_taskbar_hint (window))
     gdk_window_set_skip_taskbar_hint (widget->window, TRUE);
   
-  /* transient_for must be set to allow the modal hint */
-  if (window->transient_parent && window->modal)
+  if (window->modal)
     gdk_window_set_modal_hint (widget->window, TRUE);
   else
     gdk_window_set_modal_hint (widget->window, FALSE);
@@ -4091,7 +4161,9 @@ do_focus_change (GtkWidget *widget,
     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
   
   fevent->focus_change.type = GDK_FOCUS_CHANGE;
-  fevent->focus_change.window = g_object_ref (widget->window);
+  fevent->focus_change.window = widget->window;
+  if (widget->window)
+    g_object_ref (widget->window);
   fevent->focus_change.in = in;
   
   gtk_widget_event (widget, fevent);
@@ -4311,6 +4383,41 @@ gtk_window_real_set_focus (GtkWindow *window,
     }
 }
 
+/**
+ * _gtk_window_unset_focus_and_default:
+ * @window: a #GtkWindow
+ * @widget: a widget inside of @window
+ * 
+ * Checks whether the focus and default widgets of @window are
+ * @widget or a descendent of @widget, and if so, unset them.
+ **/
+void
+_gtk_window_unset_focus_and_default (GtkWindow *window,
+                                    GtkWidget *widget)
+
+{
+  GtkWidget *child;
+      
+  if (GTK_CONTAINER (widget->parent)->focus_child == widget)
+    {
+      child = window->focus_widget;
+      
+      while (child && child != widget)
+       child = child->parent;
+  
+      if (child == widget)
+       gtk_window_set_focus (GTK_WINDOW (window), NULL);
+    }
+      
+  child = window->default_widget;
+      
+  while (child && child != widget)
+    child = child->parent;
+      
+  if (child == widget)
+    gtk_window_set_default (window, NULL);
+}
+
 /*********************************
  * Functions related to resizing *
  *********************************/
@@ -4419,6 +4526,92 @@ get_effective_position (GtkWindow *window)
   return pos;
 }
 
+static int
+get_center_monitor_of_window (GtkWindow *window)
+{
+  /* We could try to sort out the relative positions of the monitors and
+   * stuff, or we could just be losers and assume you have a row
+   * or column of monitors.
+   */
+  return gdk_screen_get_n_monitors (gtk_window_check_screen (window)) / 2;
+}
+
+static int
+get_monitor_containing_pointer (GtkWindow *window)
+{
+  gint px, py;
+  gint monitor_num;
+  GdkScreen *window_screen;
+  GdkScreen *pointer_screen;
+
+  window_screen = gtk_window_check_screen (window);
+  gdk_display_get_pointer (gdk_screen_get_display (window_screen),
+                           &pointer_screen,
+                           &px, &py, NULL);
+
+  if (pointer_screen == window_screen)
+    monitor_num = gdk_screen_get_monitor_at_point (pointer_screen, px, py);
+  else
+    monitor_num = -1;
+
+  return monitor_num;
+}
+
+static void
+center_window_on_monitor (GtkWindow *window,
+                          gint       w,
+                          gint       h,
+                          gint      *x,
+                          gint      *y)
+{
+  GdkRectangle monitor;
+  int monitor_num;
+
+  monitor_num = get_monitor_containing_pointer (window);
+  
+  if (monitor_num == -1)
+    monitor_num = get_center_monitor_of_window (window);
+
+  gdk_screen_get_monitor_geometry (gtk_window_check_screen (window),
+                                  monitor_num, &monitor);
+  
+  *x = (monitor.width - w) / 2 + monitor.x;
+  *y = (monitor.height - h) / 2 + monitor.y;
+
+  /* Be sure we aren't off the monitor, ignoring _NET_WM_STRUT
+   * and WM decorations.
+   */
+  if (*x < monitor.x)
+    *x = monitor.x;
+  if (*y < monitor.y)
+    *y = monitor.y;
+}
+
+static void
+clamp_window_to_rectangle (gint               *x,
+                           gint               *y,
+                           gint                w,
+                           gint                h,
+                           const GdkRectangle *rect)
+{
+  gint outside_w, outside_h;
+  
+  outside_w = (*x + w) - (rect->x + rect->width);
+  if (outside_w > 0)
+    *x -= outside_w;
+
+  outside_h = (*y + h) - (rect->y + rect->height);
+  if (outside_h > 0)
+    *y -= outside_h; 
+
+  /* if larger than the screen, center on the screen. */
+  if (*x < rect->x)
+    *x += (rect->x - *x) / 2;
+  if (*y < rect->y)
+    *y += (rect->y - *y) / 2;
+}
+
+
 static void
 gtk_window_compute_configure_request (GtkWindow    *window,
                                       GdkRectangle *request,
@@ -4432,9 +4625,12 @@ gtk_window_compute_configure_request (GtkWindow    *window,
   GtkWindowPosition pos;
   GtkWidget *parent_widget;
   GtkWindowGeometryInfo *info;
+  GdkScreen *screen;
   int x, y;
   
   widget = GTK_WIDGET (window);
+
+  screen = gtk_window_check_screen (window);
   
   gtk_widget_size_request (widget, NULL);
   gtk_window_compute_configure_request_size (window, &w, &h);
@@ -4471,50 +4667,73 @@ gtk_window_compute_configure_request (GtkWindow    *window,
            */
         case GTK_WIN_POS_CENTER_ALWAYS:
         case GTK_WIN_POS_CENTER:
-          {
-           gint px, py, monitor_num;
-           GdkRectangle monitor;
-
-           gdk_window_get_pointer (gdk_screen_get_root_window (window->screen),
-                                   &px, &py, NULL);
-           
-           monitor_num = gdk_screen_get_monitor_at_point (window->screen, px, py);
-           if (monitor_num == -1)
-             monitor_num = 0;
-           
-           gdk_screen_get_monitor_geometry (window->screen, monitor_num, &monitor);
-           
-           x = (monitor.width - w) / 2 + monitor.x;
-           y = (monitor.height - h) / 2 + monitor.y;
-          }
+          center_window_on_monitor (window, w, h, &x, &y);
           break;
       
         case GTK_WIN_POS_CENTER_ON_PARENT:
           {
+            gint monitor_num;
+            GdkRectangle monitor;
             gint ox, oy;
             
             g_assert (GTK_WIDGET_MAPPED (parent_widget)); /* established earlier */
+
+            if (parent_widget->window != NULL)
+              monitor_num = gdk_screen_get_monitor_at_window (screen,
+                                                              parent_widget->window);
+            else
+              monitor_num = -1;
             
             gdk_window_get_origin (parent_widget->window,
                                    &ox, &oy);
             
             x = ox + (parent_widget->allocation.width - w) / 2;
             y = oy + (parent_widget->allocation.height - h) / 2;
+            
+            /* Clamp onto current monitor, ignoring _NET_WM_STRUT and
+             * WM decorations. If parent wasn't on a monitor, just
+             * give up.
+             */
+            if (monitor_num >= 0)
+              {
+                gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+                clamp_window_to_rectangle (&x, &y, w, h, &monitor);
+              }
           }
           break;
 
         case GTK_WIN_POS_MOUSE:
           {
-            gint screen_width = gdk_screen_get_width (window->screen);
-            gint screen_height = gdk_screen_get_height (window->screen);
-            int px, py;
+            gint screen_width = gdk_screen_get_width (screen);
+            gint screen_height = gdk_screen_get_height (screen);
+           gint monitor_num;
+           GdkRectangle monitor;
+            GdkScreen *pointer_screen;
+            gint px, py;
+            
+            gdk_display_get_pointer (gdk_screen_get_display (screen),
+                                     &pointer_screen,
+                                     &px, &py, NULL);
+
+            if (pointer_screen == screen)
+              monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
+            else
+              monitor_num = -1;
             
-            gdk_window_get_pointer (gdk_screen_get_root_window (window->screen),
-                                   &px, &py, NULL);
             x = px - w / 2;
             y = py - h / 2;
             x = CLAMP (x, 0, screen_width - w);
             y = CLAMP (y, 0, screen_height - h);
+
+            /* Clamp onto current monitor, ignoring _NET_WM_STRUT and
+             * WM decorations. Don't try to figure out what's going
+             * on if the mouse wasn't inside a monitor.
+             */
+            if (monitor_num >= 0)
+              {
+                gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+                clamp_window_to_rectangle (&x, &y, w, h, &monitor);
+              }
           }
           break;
 
@@ -4555,11 +4774,8 @@ gtk_window_constrain_position (GtkWindow    *window,
   if (window->position == GTK_WIN_POS_CENTER_ALWAYS)
     {
       gint center_x, center_y;
-      gint screen_width = gdk_screen_get_width (window->screen);
-      gint screen_height = gdk_screen_get_height (window->screen);
-      
-      center_x = (screen_width - new_width) / 2;
-      center_y = (screen_height - new_height) / 2;
+
+      center_window_on_monitor (window, new_width, new_height, &center_x, &center_y);
       
       *x = center_x;
       *y = center_y;
@@ -4886,10 +5102,9 @@ gtk_window_move_resize (GtkWindow *window)
                                  new_request.width, new_request.height);
            }
          else
-           if (widget->window)
-             gdk_window_move_resize (widget->window,
-                                     new_request.x, new_request.y,
-                                     new_request.width, new_request.height);
+           gdk_window_move_resize (widget->window,
+                                   new_request.x, new_request.y,
+                                   new_request.width, new_request.height);
        }
       else  /* only size changed */
        {
@@ -4897,9 +5112,8 @@ gtk_window_move_resize (GtkWindow *window)
            gdk_window_resize (window->frame,
                               new_request.width + window->frame_left + window->frame_right,
                               new_request.height + window->frame_top + window->frame_bottom);
-         if (widget->window)
-           gdk_window_resize (widget->window,
-                              new_request.width, new_request.height);
+         gdk_window_resize (widget->window,
+                            new_request.width, new_request.height);
        }
       
       /* Increment the number of have-not-yet-received-notify requests */
@@ -5572,6 +5786,7 @@ gtk_window_unmaximize (GtkWindow *window)
  * You can track the fullscreen state via the "window_state_event" signal
  * on #GtkWidget.
  * 
+ * Since: 2.2
  **/
 void
 gtk_window_fullscreen (GtkWindow *window)
@@ -5611,6 +5826,7 @@ gtk_window_fullscreen (GtkWindow *window)
  * You can track the fullscreen state via the "window_state_event" signal
  * on #GtkWidget.
  * 
+ * Since: 2.2
  **/
 void
 gtk_window_unfullscreen (GtkWindow *window)
@@ -5850,12 +6066,15 @@ gtk_window_begin_move_drag  (GtkWindow *window,
  * Sets the #GdkScreen where the @window is displayed; if
  * the window is already mapped, it will be unmapped, and
  * then remapped on the new screen.
+ *
+ * Since: 2.2
  */
 void
 gtk_window_set_screen (GtkWindow *window,
                       GdkScreen *screen)
 {
   GtkWidget *widget;
+  GdkScreen *previous_screen;
   gboolean was_mapped;
   
   g_return_if_fail (GTK_IS_WINDOW (window));
@@ -5865,23 +6084,39 @@ gtk_window_set_screen (GtkWindow *window,
     return;
 
   widget = GTK_WIDGET (window);
-  
+
+  previous_screen = window->screen;
   was_mapped = GTK_WIDGET_MAPPED (widget);
 
   if (was_mapped)
     gtk_widget_unmap (widget);
   if (GTK_WIDGET_REALIZED (widget))
     gtk_widget_unrealize (widget);
-  
+      
   gtk_window_free_key_hash (window);
   window->screen = screen;
   gtk_widget_reset_rc_styles (widget);
+  if (screen != previous_screen)
+    _gtk_widget_propagate_screen_changed (widget, previous_screen);
   g_object_notify (G_OBJECT (window), "screen");
 
   if (was_mapped)
     gtk_widget_map (widget);
 }
 
+static GdkScreen *
+gtk_window_check_screen (GtkWindow *window)
+{
+  if (window->screen)
+    return window->screen;
+  else
+    {
+      g_warning ("Screen for GtkWindow not set; you must always set\n"
+                "a screen for a GtkWindow before using the window");
+      return NULL;
+    }
+}
+
 /** 
  * gtk_window_get_screen:
  * @window: a #GtkWindow.
@@ -5889,6 +6124,8 @@ gtk_window_set_screen (GtkWindow *window,
  * Returns the #GdkScreen associated with @window.
  *
  * Return value: a #GdkScreen.
+ *
+ * Since: 2.2
  */
 GdkScreen*
 gtk_window_get_screen (GtkWindow *window)
@@ -6253,9 +6490,12 @@ gtk_window_parse_geometry (GtkWindow   *window,
   guint w, h;
   GdkGravity grav;
   gboolean size_set, pos_set;
+  GdkScreen *screen;
   
   g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
   g_return_val_if_fail (geometry != NULL, FALSE);
+
+  screen = gtk_window_check_screen (window);
   
   result = gtk_XParseGeometry (geometry, &x, &y, &w, &h);
 
@@ -6292,11 +6532,11 @@ gtk_window_parse_geometry (GtkWindow   *window,
 
   if (grav == GDK_GRAVITY_SOUTH_WEST ||
       grav == GDK_GRAVITY_SOUTH_EAST)
-    y = gdk_screen_get_height (window->screen) - h + y;
+    y = gdk_screen_get_height (screen) - h + y;
 
   if (grav == GDK_GRAVITY_SOUTH_EAST ||
       grav == GDK_GRAVITY_NORTH_EAST)
-    x = gdk_screen_get_width (window->screen) - w + x;
+    x = gdk_screen_get_width (screen) - w + x;
 
   /* we don't let you put a window offscreen; maybe some people would
    * prefer to be able to, but it's kind of a bogus thing to do.
@@ -6435,11 +6675,13 @@ add_to_key_hash (GtkWindow      *window,
 static GtkKeyHash *
 gtk_window_get_key_hash (GtkWindow *window)
 {
+  GdkScreen *screen = gtk_window_check_screen (window);
   GtkKeyHash *key_hash = g_object_get_data (G_OBJECT (window), "gtk-window-key-hash");
+  
   if (key_hash)
     return key_hash;
   
-  key_hash = _gtk_key_hash_new (gdk_keymap_get_for_display (gdk_screen_get_display (window->screen)),
+  key_hash = _gtk_key_hash_new (gdk_keymap_get_for_display (gdk_screen_get_display (screen)),
                                (GDestroyNotify)g_free);
   _gtk_window_keys_foreach (window, add_to_key_hash, key_hash);
   g_object_set_data (G_OBJECT (window), "gtk-window-key-hash", key_hash);
@@ -6592,3 +6834,26 @@ _gtk_window_set_has_toplevel_focus (GtkWindow *window,
       g_object_notify (G_OBJECT (window), "has_toplevel_focus");
     }
 }
+
+/**
+ * gtk_window_set_auto_startup_notification:
+ * @setting: %TRUE to automatically do startup notification
+ *
+ * By default, after showing the first #GtkWindow for each #GdkScreen,
+ * GTK+ calls gdk_screen_notify_startup_complete().  Call this
+ * function to disable the automatic startup notification. You might
+ * do this if your first window is a splash screen, and you want to
+ * delay notification until after your real main window has been
+ * shown, for example.
+ *
+ * In that example, you would disable startup notification
+ * temporarily, show your splash screen, then re-enable it so that
+ * showing the main window would automatically result in notification.
+ * 
+ * Since: 2.2
+ **/
+void
+gtk_window_set_auto_startup_notification (gboolean setting)
+{
+  disable_startup_notification = !setting;
+}