[gtk+/wip/attachment-parameters: 5/5] gdkwindow-x11: implement gdk_x11_window_set_attachment_parameters ()



commit d280b1fe7f4f306607a2440dff94aca10af046f0
Author: William Hua <william hua canonical com>
Date:   Sun Sep 20 17:11:16 2015 -0400

    gdkwindow-x11: implement gdk_x11_window_set_attachment_parameters ()

 gdk/gdkattachmentparameters.h |    2 +
 gdk/x11/gdkwindow-x11.c       |  436 +++++++++++++++++++++++++++++++++++++++++
 gtk/gtkmenuitem.c             |    4 +-
 3 files changed, 440 insertions(+), 2 deletions(-)
---
diff --git a/gdk/gdkattachmentparameters.h b/gdk/gdkattachmentparameters.h
index 0a5f1c3..ab4f120 100644
--- a/gdk/gdkattachmentparameters.h
+++ b/gdk/gdkattachmentparameters.h
@@ -14,7 +14,9 @@ enum _GdkAttachmentOption
 {
   GDK_ATTACHMENT_END_OPTIONS,
   GDK_ATTACHMENT_FORCE_FIRST_OPTION,
+  GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED,
   GDK_ATTACHMENT_FORCE_LAST_OPTION,
+  GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED,
   GDK_ATTACHMENT_ATTACH_TOP_EDGE,
   GDK_ATTACHMENT_ATTACH_LEFT_EDGE,
   GDK_ATTACHMENT_ATTACH_RIGHT_EDGE,
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index 1ace677..81433bb 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -39,6 +39,7 @@
 #include "gdkglcontext-x11.h"
 #include "gdkprivate-x11.h"
 #include "gdk-private.h"
+#include "gdkattachmentparametersprivate.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -5681,6 +5682,440 @@ gdk_x11_window_show_window_menu (GdkWindow *window,
   return TRUE;
 }
 
+static gint
+get_axis (GdkAttachmentOption option)
+{
+  switch (option)
+    {
+    case GDK_ATTACHMENT_ATTACH_LEFT_EDGE:
+    case GDK_ATTACHMENT_ATTACH_RIGHT_EDGE:
+    case GDK_ATTACHMENT_ATTACH_FORWARD_EDGE:
+    case GDK_ATTACHMENT_ATTACH_BACKWARD_EDGE:
+    case GDK_ATTACHMENT_ALIGN_LEFT_EDGES:
+    case GDK_ATTACHMENT_ALIGN_RIGHT_EDGES:
+    case GDK_ATTACHMENT_ALIGN_FORWARD_EDGES:
+    case GDK_ATTACHMENT_ALIGN_BACKWARD_EDGES:
+    case GDK_ATTACHMENT_CENTER_HORIZONTALLY:
+    case GDK_ATTACHMENT_CENTER_ON_LEFT_EDGE:
+    case GDK_ATTACHMENT_CENTER_ON_RIGHT_EDGE:
+    case GDK_ATTACHMENT_CENTER_ON_FORWARD_EDGE:
+    case GDK_ATTACHMENT_CENTER_ON_BACKWARD_EDGE:
+      return 0;
+
+    case GDK_ATTACHMENT_ATTACH_TOP_EDGE:
+    case GDK_ATTACHMENT_ATTACH_BOTTOM_EDGE:
+    case GDK_ATTACHMENT_ALIGN_TOP_EDGES:
+    case GDK_ATTACHMENT_ALIGN_BOTTOM_EDGES:
+    case GDK_ATTACHMENT_CENTER_VERTICALLY:
+    case GDK_ATTACHMENT_CENTER_ON_TOP_EDGE:
+    case GDK_ATTACHMENT_CENTER_ON_BOTTOM_EDGE:
+      return 1;
+
+    default:
+      break;
+    }
+
+  return -1;
+}
+
+static GdkAttachmentOption
+get_base_option (GdkAttachmentOption option,
+                 gboolean            is_right_to_left)
+{
+  switch (option)
+    {
+    case GDK_ATTACHMENT_ATTACH_FORWARD_EDGE:
+      return is_right_to_left ? GDK_ATTACHMENT_ATTACH_LEFT_EDGE : GDK_ATTACHMENT_ATTACH_RIGHT_EDGE;
+
+    case GDK_ATTACHMENT_ATTACH_BACKWARD_EDGE:
+      return is_right_to_left ? GDK_ATTACHMENT_ATTACH_RIGHT_EDGE : GDK_ATTACHMENT_ATTACH_LEFT_EDGE;
+
+    case GDK_ATTACHMENT_ALIGN_FORWARD_EDGES:
+      return is_right_to_left ? GDK_ATTACHMENT_ALIGN_LEFT_EDGES : GDK_ATTACHMENT_ALIGN_RIGHT_EDGES;
+
+    case GDK_ATTACHMENT_ALIGN_BACKWARD_EDGES:
+      return is_right_to_left ? GDK_ATTACHMENT_ALIGN_RIGHT_EDGES : GDK_ATTACHMENT_ALIGN_LEFT_EDGES;
+
+    case GDK_ATTACHMENT_CENTER_ON_FORWARD_EDGE:
+      return is_right_to_left ? GDK_ATTACHMENT_CENTER_ON_LEFT_EDGE : GDK_ATTACHMENT_CENTER_ON_RIGHT_EDGE;
+
+    case GDK_ATTACHMENT_CENTER_ON_BACKWARD_EDGE:
+      return is_right_to_left ? GDK_ATTACHMENT_CENTER_ON_RIGHT_EDGE : GDK_ATTACHMENT_CENTER_ON_LEFT_EDGE;
+
+    default:
+      break;
+    }
+
+  return option;
+}
+
+static gboolean
+is_satisfiable (const GdkAttachmentParameters *parameters,
+                GdkAttachmentOption            option,
+                gint                           width,
+                gint                           height,
+                const GdkRectangle            *bounds,
+                gint                          *axis,
+                gint                          *value)
+{
+  gint a;
+  gint v;
+
+  g_return_val_if_fail (parameters, FALSE);
+  g_return_val_if_fail (parameters->has_attachment_rectangle, FALSE);
+
+  if (!parameters || !parameters->has_attachment_rectangle)
+    return FALSE;
+
+  if (!axis)
+    axis = &a;
+
+  if (!value)
+    value = &v;
+
+  switch (get_base_option (option, parameters->is_right_to_left))
+    {
+    case GDK_ATTACHMENT_END_OPTIONS:
+      g_warning ("%s (): unexpected GDK_ATTACHMENT_END_OPTIONS", G_STRFUNC);
+      return FALSE;
+
+    case GDK_ATTACHMENT_FORCE_FIRST_OPTION:
+      g_warning ("%s (): unexpected GDK_ATTACHMENT_FORCE_FIRST_OPTION", G_STRFUNC);
+      return FALSE;
+
+    case GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED:
+      g_warning ("%s (): unexpected GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED", G_STRFUNC);
+      return FALSE;
+
+    case GDK_ATTACHMENT_FORCE_LAST_OPTION:
+      g_warning ("%s (): unexpected GDK_ATTACHMENT_FORCE_LAST_OPTION", G_STRFUNC);
+      return FALSE;
+
+    case GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED:
+      g_warning ("%s (): unexpected GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED", G_STRFUNC);
+      return FALSE;
+
+    case GDK_ATTACHMENT_ATTACH_TOP_EDGE:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             - parameters->attachment_margin.top
+             + parameters->window_margin.bottom
+             + parameters->window_padding.bottom
+             - height;
+      break;
+
+    case GDK_ATTACHMENT_ATTACH_LEFT_EDGE:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             - parameters->attachment_margin.left
+             + parameters->window_margin.right
+             + parameters->window_padding.right
+             - width;
+      break;
+
+    case GDK_ATTACHMENT_ATTACH_RIGHT_EDGE:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             + parameters->attachment_rectangle.width
+             + parameters->attachment_margin.right
+             - parameters->window_margin.left
+             - parameters->window_padding.left;
+      break;
+
+    case GDK_ATTACHMENT_ATTACH_BOTTOM_EDGE:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             + parameters->attachment_rectangle.height
+             + parameters->attachment_margin.bottom
+             - parameters->window_margin.top
+             - parameters->window_padding.top;
+      break;
+
+    case GDK_ATTACHMENT_ALIGN_TOP_EDGES:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             - parameters->window_margin.top
+             - parameters->window_padding.top;
+      break;
+
+    case GDK_ATTACHMENT_ALIGN_LEFT_EDGES:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             - parameters->window_margin.left
+             - parameters->window_padding.left;
+      break;
+
+    case GDK_ATTACHMENT_ALIGN_RIGHT_EDGES:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             + parameters->attachment_rectangle.width
+             + parameters->window_margin.right
+             + parameters->window_padding.right
+             - width;
+      break;
+
+    case GDK_ATTACHMENT_ALIGN_BOTTOM_EDGES:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             + parameters->attachment_rectangle.height
+             + parameters->window_margin.bottom
+             + parameters->window_padding.bottom
+             - height;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_HORIZONTALLY:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             + parameters->attachment_rectangle.width / 2
+             - parameters->window_margin.left
+             - parameters->window_padding.left
+             - (width - parameters->window_margin.left
+                      - parameters->window_margin.right
+                      - parameters->window_padding.left
+                      - parameters->window_padding.right) / 2;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_VERTICALLY:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             + parameters->attachment_rectangle.height / 2
+             - parameters->window_margin.top
+             - parameters->window_padding.top
+             - (height - parameters->window_margin.top
+                       - parameters->window_margin.bottom
+                       - parameters->window_padding.top
+                       - parameters->window_padding.bottom) / 2;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_ON_TOP_EDGE:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             - parameters->window_margin.top
+             - parameters->window_padding.top
+             - (height - parameters->window_margin.top
+                       - parameters->window_margin.bottom
+                       - parameters->window_padding.top
+                       - parameters->window_padding.bottom) / 2;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_ON_LEFT_EDGE:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             - parameters->window_margin.left
+             - parameters->window_padding.left
+             - (width - parameters->window_margin.left
+                      - parameters->window_margin.right
+                      - parameters->window_padding.left
+                      - parameters->window_padding.right) / 2;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_ON_RIGHT_EDGE:
+      *axis = 0;
+      *value = parameters->attachment_origin.x
+             + parameters->attachment_rectangle.x
+             + parameters->attachment_rectangle.width
+             - parameters->window_margin.left
+             - parameters->window_padding.left
+             - (width - parameters->window_margin.left
+                      - parameters->window_margin.right
+                      - parameters->window_padding.left
+                      - parameters->window_padding.right) / 2;
+      break;
+
+    case GDK_ATTACHMENT_CENTER_ON_BOTTOM_EDGE:
+      *axis = 1;
+      *value = parameters->attachment_origin.y
+             + parameters->attachment_rectangle.y
+             + parameters->attachment_rectangle.height
+             - parameters->window_margin.top
+             - parameters->window_padding.top
+             - (height - parameters->window_margin.top
+                       - parameters->window_margin.bottom
+                       - parameters->window_padding.top
+                       - parameters->window_padding.bottom) / 2;
+      break;
+
+    default:
+      g_warning ("%s (): unknown option", G_STRFUNC);
+      return FALSE;
+    }
+
+  return !bounds || (*axis == 0 && bounds->x <= *value && *value + width <= bounds->x + bounds->width)
+                 || (*axis == 1 && bounds->y <= *value && *value + height <= bounds->y + bounds->height);
+}
+
+static void
+gdk_x11_window_set_attachment_parameters (GdkWindow                     *window,
+                                          const GdkAttachmentParameters *parameters)
+{
+  GdkScreen *screen;
+  gint monitor;
+  GdkRectangle bounds;
+  gint width;
+  gint height;
+  GList *i;
+  GList *j;
+  GList *k;
+  GdkAttachmentOption primary_option;
+  GdkAttachmentOption secondary_option;
+  gint primary_axis;
+  gint secondary_axis;
+  gboolean primary_force;
+  gboolean secondary_force;
+  gint primary_value;
+  gint secondary_value;
+  gint position[2];
+
+  if (!parameters || !parameters->has_attachment_rectangle)
+    return;
+
+  screen = gdk_window_get_screen (window);
+  monitor = gdk_screen_get_monitor_at_window (screen, window);
+  gdk_screen_get_monitor_workarea (screen, monitor, &bounds);
+  width = gdk_window_get_width (window);
+  height = gdk_window_get_height (window);
+
+  for (i = parameters->primary_options; i; i = i->next)
+    {
+      primary_option = GPOINTER_TO_INT (i->data);
+      primary_force = FALSE;
+
+      if (primary_option == GDK_ATTACHMENT_END_OPTIONS)
+        {
+          g_warning ("%s (): unexpected GDK_ATTACHMENT_END_OPTIONS", G_STRFUNC);
+          i = NULL;
+          break;
+        }
+      else if (primary_option == GDK_ATTACHMENT_FORCE_FIRST_OPTION ||
+               primary_option == GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED)
+        {
+          g_warn_if_fail (primary_option != GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED);
+          primary_option = GPOINTER_TO_INT (parameters->primary_options->data);
+          primary_force = TRUE;
+        }
+      else if (primary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION ||
+               primary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED)
+        {
+          g_warn_if_fail (primary_option != GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED);
+
+          if (!i->prev)
+            {
+              g_warning ("%s (): started with GDK_ATTACHMENT_FORCE_LAST_OPTION", G_STRFUNC);
+              continue;
+            }
+
+          primary_option = GPOINTER_TO_INT (i->prev->data);
+          primary_force = TRUE;
+        }
+
+      if (is_satisfiable (parameters,
+                          primary_option,
+                          width,
+                          height,
+                          &bounds,
+                          &primary_axis,
+                          &primary_value) || primary_force)
+        {
+          for (j = parameters->secondary_options; j; j = j->next)
+            {
+              secondary_option = GPOINTER_TO_INT (j->data);
+              secondary_force = FALSE;
+              secondary_axis = get_axis (secondary_option);
+
+              if (secondary_axis >= 0 && secondary_axis == primary_axis)
+                continue;
+
+              if (secondary_option == GDK_ATTACHMENT_END_OPTIONS)
+                {
+                  g_warning ("%s (): unexpected GDK_ATTACHMENT_END_OPTIONS", G_STRFUNC);
+                  j = NULL;
+                  break;
+                }
+              else if (secondary_option == GDK_ATTACHMENT_FORCE_FIRST_OPTION ||
+                       secondary_option == GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED)
+                {
+                  if (secondary_option == GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED && 
!primary_force)
+                    continue;
+
+                  for (k = parameters->secondary_options; k; k = k->next)
+                    {
+                      secondary_option = GPOINTER_TO_INT (k->data);
+                      secondary_force = TRUE;
+                      secondary_axis = get_axis (secondary_option);
+
+                      if (secondary_axis >= 0 && secondary_axis != primary_axis)
+                        break;
+                    }
+
+                  if (!k)
+                    continue;
+                }
+              else if (secondary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION ||
+                       secondary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED)
+                {
+                  if (!j->prev)
+                    {
+                      if (secondary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION)
+                        g_warning ("%s (): started with GDK_ATTACHMENT_FORCE_LAST_OPTION", G_STRFUNC);
+                      else if (secondary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED)
+                        g_warning ("%s (): started with GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED", 
G_STRFUNC);
+
+                      continue;
+                    }
+
+                  if (secondary_option == GDK_ATTACHMENT_FORCE_LAST_OPTION_IF_PRIMARY_FORCED && 
!primary_force)
+                    continue;
+
+                  for (k = j->prev; k; k = k->prev)
+                    {
+                      secondary_option = GPOINTER_TO_INT (k->data);
+                      secondary_force = TRUE;
+                      secondary_axis = get_axis (secondary_option);
+
+                      if (secondary_axis >= 0 && secondary_axis != primary_axis)
+                        break;
+                    }
+
+                  if (!k)
+                    continue;
+                }
+
+              if (is_satisfiable (parameters,
+                                  secondary_option,
+                                  width,
+                                  height,
+                                  &bounds,
+                                  &secondary_axis,
+                                  &secondary_value) || secondary_force)
+                {
+                  position[primary_axis] = primary_value;
+                  position[secondary_axis] = secondary_value;
+
+                  break;
+                }
+            }
+
+          if (j)
+            break;
+        }
+    }
+
+  if (i)
+    gdk_window_move (window, position[0], position[1]);
+}
+
 static void
 gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
 {
@@ -5771,4 +6206,5 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->create_gl_context = gdk_x11_window_create_gl_context;
   impl_class->invalidate_for_new_frame = gdk_x11_window_invalidate_for_new_frame;
   impl_class->get_unscaled_size = gdk_x11_window_get_unscaled_size;
+  impl_class->set_attachment_parameters = gdk_x11_window_set_attachment_parameters;
 }
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index d1ea696..53058a2 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -1980,7 +1980,7 @@ gtk_menu_item_real_popup_submenu (GtkWidget *widget,
               gdk_attachment_parameters_add_secondary_options (parameters,
                                                                GDK_ATTACHMENT_ALIGN_BACKWARD_EDGES,
                                                                GDK_ATTACHMENT_ALIGN_FORWARD_EDGES,
-                                                               GDK_ATTACHMENT_FORCE_FIRST_OPTION,
+                                                               
GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED,
                                                                NULL);
 
               break;
@@ -2011,7 +2011,7 @@ gtk_menu_item_real_popup_submenu (GtkWidget *widget,
               gdk_attachment_parameters_add_secondary_options (parameters,
                                                                GDK_ATTACHMENT_ALIGN_TOP_EDGES,
                                                                GDK_ATTACHMENT_ALIGN_BOTTOM_EDGES,
-                                                               GDK_ATTACHMENT_FORCE_FIRST_OPTION,
+                                                               
GDK_ATTACHMENT_FORCE_FIRST_OPTION_IF_PRIMARY_FORCED,
                                                                NULL);
 
               break;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]