[dia/zbrown/test-build: 5/6] navigation: rework the popup window




commit e87cc2db6cbee0239ec8d4e768c2a336784ba8e6
Author: Zander Brown <zbrown gnome org>
Date:   Fri Sep 24 14:05:20 2021 +0100

    navigation: rework the popup window
    
    Unfortunatly we still use a global to track the popup but the preview is
    now a formal GtkWindow subclass we draw on directly (rather than a
    nested drawing area)
    
    More gtk3 thunking

 app/navigation.c  | 541 +++++++++++++++++++++++++++++++-----------------------
 lib/dia-autoptr.h |  21 ++-
 2 files changed, 326 insertions(+), 236 deletions(-)
---
diff --git a/app/navigation.c b/app/navigation.c
index b7cdc8a8b..f0f0c40f7 100644
--- a/app/navigation.c
+++ b/app/navigation.c
@@ -24,36 +24,23 @@
 
 #include <gtk/gtk.h>
 
+#include "dia-autoptr.h"
 #include "diagram.h"
 #include "display.h"
 #include "renderer/diacairo.h"
-
 #include "navigation.h"
 
-
-/**
- * navigation_popup_new:
- * @ddisp: the #DDisplay to navigate through.
- *
- * A button which triggers a popup navigation window.
- *
- * The popup window is created when the button is "pressed",
- * and destroyed when the button is "released". In the meantime, the
- * popup window grabs the pointer/focus. Moving the mouse adjust the
- * scrollbars of the given #DDisplay accordingly.
- *
- * Returns: a new #GtkButton.
- *
- * Since: dawn-of-time
- */
-
-
 #define THUMBNAIL_MAX_SIZE 150 /*(pixels) this may be a preference*/
+#define DIAGRAM_OFFSET 1 /*(diagram's unit) so we can see the little green boxes :)*/
+#define FRAME_THICKNESS 2 /*(pixels)*/
+#define STD_CURSOR_MIN 16 /*(pixels)*/
 
+#define DIA_TYPE_NAVIGATION_WINDOW dia_navigation_window_get_type ()
+G_DECLARE_FINAL_TYPE (DiaNavigationWindow, dia_navigation_window, DIA, NAVIGATION_WINDOW, GtkWindow)
 
-struct _NavigationWindow
-{
-  GtkWidget * popup_window;
+
+struct _DiaNavigationWindow {
+  GtkWindow parent;
 
   /*popup size (drawing_area)*/
   int width;
@@ -68,8 +55,8 @@ struct _NavigationWindow
   GdkCursor * cursor;
 
   /*factors to translate thumbnail coordinates to adjustement values*/
-  gdouble hadj_coef;
-  gdouble vadj_coef;
+  double hadj_coef;
+  double vadj_coef;
 
   /*diagram thumbnail's buffer*/
   cairo_surface_t *surface;
@@ -78,99 +65,90 @@ struct _NavigationWindow
   DDisplay * ddisp;
 };
 
-typedef struct _NavigationWindow NavigationWindow;
-
-static NavigationWindow _nav;
-static NavigationWindow * nav = &_nav;
-
+G_DEFINE_TYPE (DiaNavigationWindow, dia_navigation_window, GTK_TYPE_WINDOW)
 
-#define DIAGRAM_OFFSET 1 /*(diagram's unit) so we can see the little green boxes :)*/
-#define FRAME_THICKNESS 2 /*(pixels)*/
-#define STD_CURSOR_MIN 16 /*(pixels)*/
-
-
-static void on_button_navigation_popup_pressed  (GtkButton * button, gpointer _ddisp);
-static void on_button_navigation_popup_released (GtkButton * button, gpointer unused);
-
-static void reset_sc_adj (GtkAdjustment * adj, gdouble lower, gdouble upper, gdouble page);
+enum {
+  PROP_0,
+  PROP_DISPLAY,
+  LAST_PROP
+};
+static GParamSpec *pspecs[LAST_PROP] = { NULL, };
 
-static gboolean on_da_expose_event         (GtkWidget * widget, GdkEventExpose * event, gpointer unused);
-static gboolean on_da_motion_notify_event  (GtkWidget * widget, GdkEventMotion * event, gpointer unused);
-static gboolean on_da_button_release_event (GtkWidget * widget, GdkEventButton * event, gpointer 
popup_window);
 
+static void
+dia_navigation_window_set_property (GObject      *object,
+                                    guint         property_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (object);
+
+  switch (property_id) {
+    case PROP_DISPLAY:
+      self->ddisp = g_value_get_pointer (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
 
-static char * nav_xpm[] = {
-  "10 10 2 1",
-  "  c None",
-  ". c #000000",
-  "    ..    ",
-  "   ....   ",
-  "    ..    ",
-  " .  ..  . ",
-  "..........",
-  "..........",
-  " .  ..  . ",
-  "    ..    ",
-  "   ....   ",
-  "    ..    "
-};
 
-GtkWidget *
-navigation_popup_new (DDisplay *ddisp)
+static void
+dia_navigation_window_get_property (GObject    *object,
+                                    guint       property_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
 {
-  GtkWidget * button;
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (object);
+
+  switch (property_id) {
+    case PROP_DISPLAY:
+      g_value_set_pointer (value, self->ddisp);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
 
-  GtkWidget * image;
-  GdkPixmap * pixmap;
-  GdkBitmap * mask = NULL;
-  GtkStyle  * style;
 
-  button = gtk_button_new ();
-  gtk_container_set_border_width (GTK_CONTAINER (button), 0);
-  gtk_button_set_relief (GTK_BUTTON(button), GTK_RELIEF_NONE);
-  g_signal_connect (G_OBJECT (button), "pressed",
-                    G_CALLBACK (on_button_navigation_popup_pressed), ddisp);
-  /*if you are fast, the button catches it before the drawing_area:*/
-  g_signal_connect (G_OBJECT (button), "released",
-                    G_CALLBACK (on_button_navigation_popup_released), NULL);
+/* resets adjustement to diagram size */
+static void
+reset_sc_adj (GtkAdjustment *adj, double lower, double upper, double page)
+{
+  gtk_adjustment_set_page_size (adj, page);
 
-  style = gtk_widget_get_style (button);
-  pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL,
-                                                 gtk_widget_get_colormap(button),
-                                                 &mask,
-                                                 &(style->bg[GTK_STATE_NORMAL]),
-                                                 nav_xpm);
+  gtk_adjustment_set_lower (adj, lower);
+  gtk_adjustment_set_upper (adj, upper);
 
-  image = gtk_image_new_from_pixmap (pixmap, mask);
-  g_clear_object (&pixmap);
-  g_clear_object (&mask);
+  if (gtk_adjustment_get_value (adj) < lower) {
+    gtk_adjustment_set_value (adj, lower);
+  }
 
-  gtk_container_add (GTK_CONTAINER (button), image);
-  gtk_widget_show (image);
+  if (gtk_adjustment_get_value (adj) > (upper - page)) {
+    gtk_adjustment_set_value (adj, upper - page);
+  }
 
-  return button;
+  gtk_adjustment_changed (adj);
 }
 
 
 static void
-on_button_navigation_popup_pressed (GtkButton * button, gpointer _ddisp)
+dia_navigation_window_constructed (GObject *object)
 {
-  GtkWidget * popup_window;
-  GtkWidget * frame;
-
-  GtkWidget * drawing_area;
-
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (object);
   DiagramData * data;
 
   DiaRectangle rect;/*diagram's extents*/
-  real zoom;/*zoom factor for thumbnail rendering*/
+  double zoom;/*zoom factor for thumbnail rendering*/
 
   DiaCairoRenderer *renderer;
 
-  memset (nav, 0, sizeof(NavigationWindow));
+  G_OBJECT_CLASS (dia_navigation_window_parent_class)->constructed (object);
+
   /*--Retrieve the diagram's data*/
-  nav->ddisp  = (DDisplay *) _ddisp;
-  data = nav->ddisp->diagram->data;
+  data = self->ddisp->diagram->data;
 
   /*--Calculate sizes*/
   {
@@ -179,7 +157,7 @@ on_button_navigation_popup_pressed (GtkButton * button, gpointer _ddisp)
     GtkAdjustment * adj;
     GtkAllocation alloc;
 
-    nav->max_size = THUMBNAIL_MAX_SIZE;
+    self->max_size = THUMBNAIL_MAX_SIZE;
 
     /*size: Diagram <--> thumbnail*/
     rect.top    = data->extents.top    - DIAGRAM_OFFSET;
@@ -187,245 +165,356 @@ on_button_navigation_popup_pressed (GtkButton * button, gpointer _ddisp)
     rect.bottom = data->extents.bottom + DIAGRAM_OFFSET + 1;
     rect.right  = data->extents.right  + DIAGRAM_OFFSET + 1;
 
-    zoom = nav->max_size / MAX( (rect.right - rect.left) , (rect.bottom - rect.top) );
+    zoom = self->max_size / MAX( (rect.right - rect.left) , (rect.bottom - rect.top) );
 
-    nav->width  = MIN( nav->max_size, (rect.right  - rect.left) * zoom);
-    nav->height = MIN( nav->max_size, (rect.bottom - rect.top)  * zoom);
+    self->width  = MIN (self->max_size, (rect.right  - rect.left) * zoom);
+    self->height = MIN (self->max_size, (rect.bottom - rect.top)  * zoom);
 
     /*size: display canvas <--> frame cursor*/
-    diagram_width  = (int) ddisplay_transform_length (nav->ddisp, (rect.right - rect.left));
-    diagram_height = (int) ddisplay_transform_length (nav->ddisp, (rect.bottom - rect.top));
+    diagram_width  = (int) ddisplay_transform_length (self->ddisp, (rect.right - rect.left));
+    diagram_height = (int) ddisplay_transform_length (self->ddisp, (rect.bottom - rect.top));
 
     if (diagram_width * diagram_height == 0)
       return; /* don't crash with no size, i.e. empty diagram */
 
-    gtk_widget_get_allocation (nav->ddisp->canvas, &alloc);
+    gtk_widget_get_allocation (self->ddisp->canvas, &alloc);
 
     canvas_width   = alloc.width;
     canvas_height  = alloc.height;
 
-    nav->frame_w = nav->width  * canvas_width  / diagram_width;
-    nav->frame_h = nav->height * canvas_height / diagram_height;
+    self->frame_w = self->width  * canvas_width  / diagram_width;
+    self->frame_h = self->height * canvas_height / diagram_height;
 
     /*reset adjustements to diagram size,*/
     /*(dia allows to grow the canvas bigger than the diagram's actual size)    */
     /*and store the ratio thumbnail/adjustement(speedup on motion)*/
-    adj = nav->ddisp->hsbdata;
-    reset_sc_adj (adj, rect.left, rect.right, canvas_width / nav->ddisp->zoom_factor);
-    nav->hadj_coef = (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) - 
gtk_adjustment_get_lower (adj)) / (nav->width - nav->frame_w);
-
-    adj = nav->ddisp->vsbdata;
-    reset_sc_adj (adj, rect.top, rect.bottom, canvas_height / nav->ddisp->zoom_factor);
-    nav->vadj_coef = (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) - 
gtk_adjustment_get_lower (adj)) / (nav->height - nav->frame_h);
+    adj = self->ddisp->hsbdata;
+    reset_sc_adj (adj, rect.left, rect.right, canvas_width / self->ddisp->zoom_factor);
+    self->hadj_coef = (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) - 
gtk_adjustment_get_lower (adj)) /
+                        (self->width - self->frame_w);
+
+    adj = self->ddisp->vsbdata;
+    reset_sc_adj (adj, rect.top, rect.bottom, canvas_height / self->ddisp->zoom_factor);
+    self->vadj_coef = (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) - 
gtk_adjustment_get_lower (adj)) /
+                        (self->height - self->frame_h);
   }
 
-  /*--GUI*/
-  /*popup window, and cute frame*/
-  popup_window = gtk_window_new (GTK_WINDOW_POPUP);
-  nav->popup_window = popup_window;
-  gtk_window_set_position (GTK_WINDOW (popup_window), GTK_WIN_POS_MOUSE);
-
-  frame = gtk_frame_new (NULL);
-  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
-
-  /*drawing area*/
-  drawing_area = gtk_drawing_area_new ();
-  gtk_widget_set_size_request (drawing_area, nav->width, nav->height);
-
-  gtk_widget_set_events (drawing_area, 0
-                         | GDK_EXPOSURE_MASK
-                         | GDK_POINTER_MOTION_MASK
-                         | GDK_BUTTON_RELEASE_MASK
-                         );
-
-  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
-                    G_CALLBACK (on_da_expose_event), NULL);
-  g_signal_connect (G_OBJECT (drawing_area), "motion_notify_event",
-                    G_CALLBACK (on_da_motion_notify_event), NULL);
-  g_signal_connect (G_OBJECT (drawing_area), "button_release_event",
-                    G_CALLBACK (on_da_button_release_event), NULL);
-
-  /*packing*/
-  gtk_container_add (GTK_CONTAINER (frame), drawing_area);
-  gtk_container_add (GTK_CONTAINER (popup_window), frame);
-  gtk_widget_show (drawing_area);
-  gtk_widget_show (frame);
-  gtk_widget_show (popup_window);
-
-  /*cursor*/
-  if (MIN(nav->frame_h, nav->frame_w) > STD_CURSOR_MIN) {
-    nav->cursor = gdk_cursor_new (GDK_FLEUR);
-  } else { /*the miniframe is very small, so we use a minimalist cursor*/
-    gchar cursor_none_data[] = { 0x00 };
-    GdkBitmap * bitmap;
-    GdkColor fg = { 0, 65535, 65535, 65535};
-    GdkColor bg = { 0, 0, 0, 0 };
-
-    bitmap = gdk_bitmap_create_from_data(NULL, cursor_none_data, 1, 1);
-    nav->cursor = gdk_cursor_new_from_pixmap(bitmap, bitmap, &fg, &bg, 1, 1);
-    g_clear_object (&bitmap);
-  }
-
-  /*grab the pointer*/
-  gdk_pointer_grab (gtk_widget_get_window (drawing_area), TRUE,
-                    GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK,
-                    gtk_widget_get_window (drawing_area),
-                    nav->cursor,
-                    GDK_CURRENT_TIME);
+  gtk_widget_set_size_request (GTK_WIDGET (self), self->width, self->height);
 
   /* surface to draw the thumbnail on */
-  nav->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                            nav->width, nav->height);
+  self->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                             self->width,
+                                             self->height);
 
   renderer = g_object_new (DIA_CAIRO_TYPE_RENDERER, NULL);
   renderer->scale = zoom;
-  renderer->surface = cairo_surface_reference (nav->surface);
+  renderer->surface = cairo_surface_reference (self->surface);
 
   /*render the data*/
   data_render (data, DIA_RENDERER (renderer), NULL, NULL, NULL);
 
   g_clear_object (&renderer);
 
-  nav->is_first_expose = TRUE;/*set to request to draw the miniframe*/
+  self->is_first_expose = TRUE;/*set to request to draw the miniframe*/
 }
 
 
-/* resets adjustement to diagram size */
 static void
-reset_sc_adj (GtkAdjustment * adj, gdouble lower, gdouble upper, gdouble page)
+dia_navigation_window_dispose (GObject *object)
 {
-  gtk_adjustment_set_page_size (adj, page);
-
-  gtk_adjustment_set_lower (adj, lower);
-  gtk_adjustment_set_upper (adj, upper);
-
-  if (gtk_adjustment_get_value (adj) < lower) {
-    gtk_adjustment_set_value (adj, lower);
-  }
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (object);
 
-  if (gtk_adjustment_get_value (adj) > (upper - page)) {
-    gtk_adjustment_set_value (adj, upper - page);
-  }
+  g_clear_pointer (&self->cursor, gdk_cursor_unref);
+  g_clear_pointer (&self->surface, cairo_surface_destroy);
 
-  gtk_adjustment_changed (adj);
+  G_OBJECT_CLASS (dia_navigation_window_parent_class)->dispose (object);
 }
 
 
 static gboolean
-on_da_expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer unused)
+dia_navigation_window_draw (GtkWidget *widget, cairo_t *ctx)
 {
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (widget);
   GtkAdjustment * adj;
   int x, y;
-  cairo_t *ctx;
 
-  ctx = gdk_cairo_create (gtk_widget_get_window (widget));
   cairo_set_line_width (ctx, FRAME_THICKNESS);
   cairo_set_line_cap (ctx, CAIRO_LINE_CAP_BUTT);
   cairo_set_line_join (ctx, CAIRO_LINE_JOIN_MITER);
 
   /*refresh the part outdated by the event*/
-  cairo_set_source_surface (ctx, nav->surface,
-                            event->area.x, event->area.y);
-  cairo_rectangle (ctx, event->area.x, event->area.y,
-                        event->area.width, event->area.height);
-  cairo_fill (ctx);
+  cairo_set_source_surface (ctx, self->surface, 0, 0);
+  cairo_paint (ctx);
 
-  adj = nav->ddisp->hsbdata;
-  x = (gtk_adjustment_get_value (adj) - gtk_adjustment_get_lower (adj)) / (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_lower (adj)) * (nav->width) +1;
+  adj = self->ddisp->hsbdata;
+  x = (gtk_adjustment_get_value (adj) - gtk_adjustment_get_lower (adj)) /
+          (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj)) *
+              (self->width) +1;
 
-  adj = nav->ddisp->vsbdata;
-  y = (gtk_adjustment_get_value (adj) - gtk_adjustment_get_lower (adj)) / (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_lower (adj)) * (nav->height) +1;
+  adj = self->ddisp->vsbdata;
+  y = (gtk_adjustment_get_value (adj) - gtk_adjustment_get_lower (adj)) /
+          (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj)) *
+              (self->height) +1;
 
   /*draw directly on the window, do not buffer the miniframe*/
   cairo_set_source_rgb (ctx, 0, 0, 0);
-  cairo_rectangle (ctx, x, y, nav->frame_w, nav->frame_h);
+  cairo_rectangle (ctx, x, y, self->frame_w, self->frame_h);
   cairo_stroke (ctx);
 
-  nav->is_first_expose = FALSE;
+  self->is_first_expose = FALSE;
 
   return FALSE;
 }
 
 
+#if !GTK_CHECK_VERSION (3, 0, 0)
 static gboolean
-on_da_motion_notify_event (GtkWidget * drawing_area, GdkEventMotion * event, gpointer unused)
+dia_navigation_window_expose_event (GtkWidget *widget, GdkEventExpose *event)
 {
+  cairo_t *ctx;
+  gboolean res;
+
+  ctx = gdk_cairo_create (GDK_DRAWABLE (gtk_widget_get_window (widget)));
+
+  res = dia_navigation_window_draw (widget, ctx);
+
+  cairo_destroy (ctx);
+
+  return res;
+}
+#endif
+
+
+static gboolean
+dia_navigation_window_motion_notify_event (GtkWidget      *widget,
+                                           GdkEventMotion *event)
+{
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (widget);
   GtkAdjustment * adj;
   gboolean value_changed;
 
-  int w = nav->frame_w;
-  int h = nav->frame_h;
+  int w = self->frame_w;
+  int h = self->frame_h;
 
   int x, y;/*top left of the miniframe*/
 
   /* Don't try to move if there's no room for it.*/
-  if (w >= nav->width-1 && h >= nav->height-1) return FALSE;
+  if (w >= self->width-1 && h >= self->height-1) return FALSE;
 
-  x = CLAMP (event->x - w/2 , 0, nav->width  - w);
-  y = CLAMP (event->y - h/2 , 0, nav->height - h);
+  x = CLAMP (event->x - w/2 , 0, self->width  - w);
+  y = CLAMP (event->y - h/2 , 0, self->height - h);
 
-  adj = nav->ddisp->hsbdata;
+  adj = self->ddisp->hsbdata;
   value_changed = FALSE;
-  if (w/2 <= event->x && event->x <= (nav->width - w/2)){
-    gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj) + x * nav->hadj_coef);
+  if (w/2 <= event->x && event->x <= (self->width - w/2)){
+    gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj) + x * self->hadj_coef);
     value_changed = TRUE;
   }
   else if (x == 0 && gtk_adjustment_get_value (adj) != gtk_adjustment_get_lower (adj)){/*you've been too 
fast! :)*/
     gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj));
     value_changed = TRUE;
   }
-  else if (x == (nav->width - w) && gtk_adjustment_get_value (adj) != (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_page_size (adj))){/*idem*/
+  else if (x == (self->width - w) && gtk_adjustment_get_value (adj) != (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_page_size (adj))){/*idem*/
     gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
     value_changed = TRUE;
   }
   if (value_changed) gtk_adjustment_value_changed(adj);
 
-  adj = nav->ddisp->vsbdata;
+  adj = self->ddisp->vsbdata;
   value_changed = FALSE;
-  if (h/2 <= event->y && event->y <= (nav->height - h/2)){
-    gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj) + y * nav->vadj_coef);
+  if (h/2 <= event->y && event->y <= (self->height - h/2)){
+    gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj) + y * self->vadj_coef);
     value_changed = TRUE;
   }
   else if (y == 0 && gtk_adjustment_get_value (adj) != gtk_adjustment_get_lower (adj)){/*you've been too 
fast! :)*/
     gtk_adjustment_set_value (adj, gtk_adjustment_get_lower (adj));
     value_changed = TRUE;
   }
-  else if (y == (nav->height - h) && gtk_adjustment_get_value (adj) != (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_page_size (adj))){/*idem*/
+  else if (y == (self->height - h) && gtk_adjustment_get_value (adj) != (gtk_adjustment_get_upper (adj) - 
gtk_adjustment_get_page_size (adj))){/*idem*/
     gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
     value_changed = TRUE;
   }
-  if (value_changed) gtk_adjustment_value_changed(adj);
+  if (value_changed) {
+    gtk_adjustment_value_changed (adj);
+  }
 
   /* Trigger redraw */
-  gdk_window_invalidate_rect (gtk_widget_get_window (drawing_area), NULL, TRUE);
+  gtk_widget_queue_draw (widget);
 
   return FALSE;
 }
 
 
 static gboolean
-on_da_button_release_event (GtkWidget * widget, GdkEventButton * event, gpointer unused)
+dia_navigation_window_button_release_event (GtkWidget      *widget,
+                                            GdkEventButton *event)
 {
-  /* Apparently there are circumstances where this is run twice for one popup
-   * Protected calls to avoid crashing on second pass.
-   */
-  if (nav->cursor)
-    gdk_cursor_unref (nav->cursor);
-  nav->cursor = NULL;
-
-  if (nav->popup_window)
-    gtk_widget_destroy (nav->popup_window);
-  nav->popup_window = NULL;
-
-/*returns the focus on the canvas*/
-  gtk_widget_grab_focus(nav->ddisp->canvas);
+  DiaNavigationWindow *self = DIA_NAVIGATION_WINDOW (widget);
+
+  g_object_ref (self);
+
+  gtk_widget_destroy (widget);
+
+  /* returns the focus on the canvas */
+  gtk_widget_grab_focus (self->ddisp->canvas);
+
+  g_object_unref (self);
+
   return FALSE;
 }
 
+
+static void
+dia_navigation_window_class_init (DiaNavigationWindowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->set_property = dia_navigation_window_set_property;
+  object_class->get_property = dia_navigation_window_get_property;
+  object_class->constructed = dia_navigation_window_constructed;
+  object_class->dispose = dia_navigation_window_dispose;
+
+#if GTK_CHECK_VERSION (3, 0, 0)
+  widget_class->draw = dia_navigation_window_draw;
+#else
+  widget_class->expose_event = dia_navigation_window_expose_event;
+#endif
+  widget_class->motion_notify_event = dia_navigation_window_motion_notify_event;
+  widget_class->button_release_event = dia_navigation_window_button_release_event;
+
+  pspecs[PROP_DISPLAY] =
+    g_param_spec_pointer ("display",
+                          "Display",
+                          "The DDisplay",
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, LAST_PROP, pspecs);
+}
+
+
+static void
+dia_navigation_window_init (DiaNavigationWindow *self)
+{
+  gtk_widget_add_events (GTK_WIDGET (self),
+                         GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
+}
+
+
+static void
+dia_navigation_window_popup (DiaNavigationWindow *self)
+{
+  gtk_widget_show (GTK_WIDGET (self));
+
+  /*cursor*/
+  if (MIN (self->frame_h, self->frame_w) > STD_CURSOR_MIN) {
+    self->cursor = gdk_cursor_new (GDK_FLEUR);
+  } else { /*the miniframe is very small, so we use a minimalist cursor*/
+    self->cursor = gdk_cursor_new_from_name (gtk_widget_get_display (GTK_WIDGET (self)),
+                                             "none");
+  }
+
+  /*grab the pointer*/
+  gdk_pointer_grab (gtk_widget_get_window (GTK_WIDGET (self)),
+                    TRUE,
+                    GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK,
+                    gtk_widget_get_window (GTK_WIDGET (self)),
+                    self->cursor,
+                    GDK_CURRENT_TIME);
+}
+
+
+static const char *nav_xpm[] = {
+  "10 10 2 1",
+  "  c None",
+  ". c #000000",
+  "    ..    ",
+  "   ....   ",
+  "    ..    ",
+  " .  ..  . ",
+  "..........",
+  "..........",
+  " .  ..  . ",
+  "    ..    ",
+  "   ....   ",
+  "    ..    "
+};
+
+
+static DiaNavigationWindow *current_popup;
+
+static void
+on_button_navigation_popup_pressed (GtkButton * button, gpointer _ddisp)
+{
+  /*--GUI*/
+  /*popup window, and cute frame*/
+  current_popup = g_object_new (DIA_TYPE_NAVIGATION_WINDOW,
+                               "type", GTK_WINDOW_POPUP,
+                               "window-position", GTK_WIN_POS_MOUSE,
+                               "transient-for", gtk_widget_get_toplevel (GTK_WIDGET (button)),
+#if GTK_CHECK_VERSION (3, 0, 0)
+                               "attached-to", button,
+#endif
+                               "display", _ddisp,
+                               NULL);
+  g_object_add_weak_pointer (G_OBJECT (current_popup), (gpointer *) &current_popup);
+
+  dia_navigation_window_popup (current_popup);
+}
+
+
 static void
 on_button_navigation_popup_released (GtkButton * button, gpointer z)
 {
   /* don't popdown before having drawn once */
-  if (!nav->is_first_expose) /* needed for gtk+-2.6.x, but work for 2.6 too. */
-    on_da_button_release_event (NULL, NULL, NULL);
+  if (current_popup && !current_popup->is_first_expose) {
+    /* needed for gtk+-2.6.x, but work for 2.6 too. */
+    gtk_widget_destroy (GTK_WIDGET (current_popup));
+  }
+}
+
+
+/**
+ * navigation_popup_new:
+ * @ddisp: the #DDisplay to navigate through.
+ *
+ * A button which triggers a popup navigation window.
+ *
+ * The popup window is created when the button is "pressed",
+ * and destroyed when the button is "released". In the meantime, the
+ * popup window grabs the pointer/focus. Moving the mouse adjust the
+ * scrollbars of the given #DDisplay accordingly.
+ *
+ * Returns: a new #GtkButton.
+ *
+ * Since: dawn-of-time
+ */
+GtkWidget *
+navigation_popup_new (DDisplay *ddisp)
+{
+  GtkWidget *button;
+  GtkWidget *image;
+  GdkPixbuf *pixbuf;
+
+  button = gtk_button_new ();
+  gtk_container_set_border_width (GTK_CONTAINER (button), 0);
+  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+  g_signal_connect (G_OBJECT (button),
+                    "pressed", G_CALLBACK (on_button_navigation_popup_pressed),
+                    ddisp);
+  /*if you are fast, the button catches it before the drawing_area:*/
+  g_signal_connect (G_OBJECT (button),
+                    "released", G_CALLBACK (on_button_navigation_popup_released),
+                    NULL);
+
+  pixbuf = gdk_pixbuf_new_from_xpm_data (nav_xpm);
+
+  image = gtk_image_new_from_pixbuf (pixbuf);
+
+  gtk_container_add (GTK_CONTAINER (button), image);
+  gtk_widget_show (image);
+
+  g_clear_object (&pixbuf);
+
+  return button;
 }
diff --git a/lib/dia-autoptr.h b/lib/dia-autoptr.h
index d47c0306f..f7004e040 100644
--- a/lib/dia-autoptr.h
+++ b/lib/dia-autoptr.h
@@ -27,22 +27,23 @@
 G_BEGIN_DECLS
 
 
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkBin, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkBuilder, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkSpinButton, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkComboBox, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkHBox, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkTreeView, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkDrawingArea, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkButton, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkCellRenderer, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkCellRendererText, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkBin, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkComboBox, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkContainer, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkVBox, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkDialog, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkDrawingArea, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkEventBox, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkTable, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkRadioButton, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkHBox, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkMisc, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkButton, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkRadioButton, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkSpinButton, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkTable, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkTreeView, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkVBox, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GtkWindow, g_object_unref)
 
 G_END_DECLS


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