]> git.cworth.org Git - akamaru/blobdiff - dock.c
Add new akamaru example: a crazy icon dock.
[akamaru] / dock.c
diff --git a/dock.c b/dock.c
new file mode 100644 (file)
index 0000000..bc439ff
--- /dev/null
+++ b/dock.c
@@ -0,0 +1,226 @@
+/*                                           -*- mode: c; c-basic-offset: 2 -*-
+ */
+
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <cairo-xlib.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <math.h>
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+#include "akamaru.h"
+
+typedef struct Closure Closure;
+struct Closure {
+  Model model;
+  int num_icons;
+  GdkWindow **windows;
+  int drag_offset_x, drag_offset_y;
+  int anchor_x, anchor_y;
+  int spacing;
+};
+
+static gint
+timeout_callback (gpointer data)
+{
+  Closure *closure = data;
+  int i;
+
+  for (i = 0; i < closure->num_icons; i++) {
+    gdk_window_move (closure->windows[i],
+                    closure->model.objects[i + 1].position.x + 0.5,
+                    closure->model.objects[i + 1].position.y + 0.5);
+  }
+
+  model_step (&closure->model, 0.1);
+
+  return TRUE;
+}
+
+static GdkWindow *
+create_window (GdkScreen *screen, int x, int y, int width, int height)
+{
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gdk_screen_get_rgba_visual (screen);
+  attributes.colormap = gdk_screen_get_rgba_colormap (screen);
+  attributes.window_type = GDK_WINDOW_TEMP;
+
+  attributes.x = x;
+  attributes.y = y;
+  attributes.width = width;
+  attributes.height = height;
+  attributes.event_mask |=
+    GDK_EXPOSURE_MASK |
+    GDK_BUTTON_PRESS_MASK |
+    GDK_BUTTON_RELEASE_MASK |
+    GDK_ENTER_NOTIFY_MASK |
+    GDK_LEAVE_NOTIFY_MASK |
+    GDK_POINTER_MOTION_HINT_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  
+  return gdk_window_new (gdk_screen_get_root_window (screen),
+                        &attributes, attributes_mask);
+}
+
+static void
+model_init_dock (Model *model, int num_items, int x, int y, int spacing)
+{
+  const int num_objects = num_items + 1;
+  const int num_spacers = (num_objects - 1) * (num_objects - 2) / 2;
+  const int num_springs = num_objects - 1;
+  int i, j;
+  Object *object;
+  Spring *spring;
+  Spacer *spacer;
+
+  memset (model, 0, sizeof *model);
+  model->objects = g_new (Object, num_objects);
+  model->num_objects = num_objects;
+  model->springs = g_new (Spring, num_springs);
+  model->num_springs = num_springs;
+  model->spacers = g_new (Spacer, num_spacers);
+  model->num_spacers = num_spacers;
+  model->anchors = g_new (Anchor, 1);
+  model->num_anchors = 1;
+  model->k = 0.1;
+
+  model->polygons = g_new (Polygon, 1);
+  model->num_polygons = 1;
+  polygon_init_rectangle (&model->polygons[0], -4000, y, 4000, 2000);
+
+  model->anchors[0].x = x;
+  model->anchors[0].y = y;
+  model->anchors[0].object = &model->objects[0];
+
+  object_init (&model->objects[0], x, y, 1);
+
+  object = &model->objects[1];
+  spring = model->springs;
+  spacer = model->spacers;
+
+  for (i = 1; i < num_objects; i++, object++) {
+    object_init (&model->objects[i], 200 + i * spacing / 2, 300, 1);
+    spring_init (spring++, &model->objects[0], object, spacing);
+    for (j = 1; j < num_objects - i; j++) {
+      spacer_init (spacer++, object, object + j, spacing);
+    }
+  }
+}
+
+static GdkFilterReturn
+window_event (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+  Closure *closure = data;
+  GdkModifierType state;
+  XEvent *ev = (XEvent *) xevent;
+  int x, y, i;
+  Object *object;
+
+  switch (ev->type) {
+  case ButtonPress:
+    closure->drag_offset_x = ev->xbutton.x;
+    closure->drag_offset_y = ev->xbutton.y;
+    for (i = 0; i < closure->num_icons; i++) {
+      if (closure->windows[i] == event->any.window) {
+       object = &closure->model.objects[i + 1];
+       closure->model.mouse_anchor.x = object->position.x;
+       closure->model.mouse_anchor.y = object->position.y;
+       closure->model.mouse_anchor.object = object;
+      }
+    }
+    break;
+
+  case ButtonRelease:
+    closure->model.mouse_anchor.object = NULL;
+    break;
+
+  case MotionNotify:
+    gdk_window_get_pointer (gdk_get_default_root_window(), &x, &y, &state);
+    closure->model.mouse_anchor.x = x + 0.5 - closure->drag_offset_x;
+    closure->model.mouse_anchor.y = y + 0.5 - closure->drag_offset_y;
+    if (closure->model.mouse_anchor.y > closure->anchor_y)
+      closure->model.mouse_anchor.y = closure->anchor_y;
+    break;
+
+  default:
+    break;
+  }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static const char *icons[] = {
+  "svg/applications-office.svg",
+  "svg/camera-video.svg",
+  "svg/email.svg",
+  "svg/firefox-logo.svg",
+  "svg/gnome-dev-disc-dvdrom.svg",
+  "svg/gnome-terminal.svg",
+  "svg/help-browser.svg",
+  "svg/internet-group-chat.svg"
+};
+
+int main (int argc, char *argv[])
+{
+    Closure closure;
+    GdkScreen *screen;
+    const int num_icons = G_N_ELEMENTS (icons);
+    int x, y, width, height, i;
+    RsvgHandle *handle;
+    RsvgDimensionData dimension;
+    cairo_t *cr;
+    const int screen_width = 1024, screen_height = 768, spacing = 50;
+
+    gtk_init (&argc, &argv);
+
+    rsvg_init ();
+
+    screen = gdk_screen_get_default ();
+
+    closure.anchor_x = screen_width / 2;
+    closure.anchor_y = screen_height - 50;
+    closure.spacing = spacing;
+    closure.num_icons = num_icons;
+    closure.windows = g_new (GdkWindow *, num_icons);
+    for (i = 0; i < num_icons; i++) {
+
+      handle = rsvg_handle_new_from_file (icons[i], NULL);
+      rsvg_handle_get_dimensions (handle, &dimension);
+
+      x = (screen_width - spacing * num_icons) / 2 + i * spacing;
+      y = closure.anchor_y;
+      width = dimension.width;
+      height = dimension.height;
+      closure.windows[i] = create_window (screen, x, y, width, height);
+
+      gdk_window_show (closure.windows[i]);
+
+      cr = gdk_cairo_create (closure.windows[i]);
+      cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+      cairo_paint (cr);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+      rsvg_handle_render_cairo (handle, cr);
+      rsvg_handle_free (handle);
+      cairo_destroy (cr);
+
+      gdk_window_add_filter (closure.windows[i], window_event, &closure);
+    }
+
+    model_init_dock (&closure.model, num_icons,
+                    closure.anchor_x, closure.anchor_y, spacing);
+    g_timeout_add (20, timeout_callback, &closure);
+
+    gtk_main ();
+
+    rsvg_term ();
+
+    return 0;
+}