]> git.notmuchmail.org Git - akamaru/blob - dock.c
Indent to 8 columns rather than 2
[akamaru] / dock.c
1 /*                                           -*- mode: c; c-basic-offset: 8 -*-
2  */
3
4 #include <gtk/gtk.h>
5 #include <cairo.h>
6 #include <cairo-xlib.h>
7 #include <gdk/gdkx.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/time.h>
11 #include <math.h>
12 #include <librsvg/rsvg.h>
13 #include <librsvg/rsvg-cairo.h>
14
15 #include "akamaru.h"
16
17 typedef struct Closure Closure;
18 struct Closure {
19         Model model;
20         int num_icons;
21         GdkWindow **windows;
22         int drag_offset_x, drag_offset_y;
23         int spacing;
24         int height;
25 };
26
27 static gint
28 timeout_callback (gpointer data)
29 {
30         Closure *closure = data;
31         int i;
32
33         for (i = 0; i < closure->num_icons; i++) {
34                 gdk_window_move (closure->windows[i],
35                                  closure->model.objects[i + 1].position.x + 0.5,
36                                  closure->model.objects[i + 1].position.y + 0.5);
37         }
38
39         for (i = 0; i < 4; i++)
40                 model_step (&closure->model, 0.03);
41
42         return TRUE;
43 }
44
45 static GdkWindow *
46 create_window (GdkScreen *screen, int x, int y, int width, int height)
47 {
48         GdkWindowAttr attributes;
49         gint attributes_mask;
50
51         attributes.wclass = GDK_INPUT_OUTPUT;
52         attributes.visual = gdk_screen_get_rgba_visual (screen);
53         attributes.colormap = gdk_screen_get_rgba_colormap (screen);
54         attributes.window_type = GDK_WINDOW_TEMP;
55
56         attributes.x = x;
57         attributes.y = y;
58         attributes.width = width;
59         attributes.height = height;
60         attributes.event_mask =
61                 GDK_EXPOSURE_MASK |
62                 GDK_BUTTON_PRESS_MASK |
63                 GDK_BUTTON_RELEASE_MASK |
64                 GDK_ENTER_NOTIFY_MASK |
65                 GDK_LEAVE_NOTIFY_MASK |
66                 GDK_POINTER_MOTION_MASK |
67                 GDK_POINTER_MOTION_HINT_MASK;
68
69         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
70   
71         return gdk_window_new (gdk_screen_get_root_window (screen),
72                                &attributes, attributes_mask);
73 }
74
75 static void
76 model_init_dock (Model *model, int num_items,
77                  int width, int height, int spacing)
78 {
79         const int num_objects = num_items + 1;
80         const int num_spacers = (num_objects - 1) * (num_objects - 2) / 2;
81         const int num_springs = num_objects - 1;
82         const int spread = spacing + 20;
83         int i, j, left_edge;
84         Object *object;
85         Spring *spring;
86         Spacer *spacer;
87
88         memset (model, 0, sizeof *model);
89         model->objects = g_new (Object, num_objects);
90         model->num_objects = num_objects;
91         model->springs = g_new (Spring, num_springs);
92         model->num_springs = num_springs;
93         model->spacers = g_new (Spacer, num_spacers);
94         model->num_spacers = num_spacers;
95         model->anchors = g_new (Anchor, 1);
96         model->num_anchors = 1;
97         model->k = 0.1;
98
99         model->polygons = g_new (Polygon, 1);
100         model->num_polygons = 1;
101         polygon_init_enclosing_rectangle (&model->polygons[0],
102                                           0, 0, width - 50, height - 50);
103
104         model->anchors[0].x = width / 2;
105         model->anchors[0].y = height - 50;
106         model->anchors[0].object = &model->objects[0];
107
108         object_init (&model->objects[0],
109                      model->anchors[0].x, model->anchors[0].y, 0);
110
111         object = &model->objects[1];
112         spring = model->springs;
113         spacer = model->spacers;
114         left_edge = (width - (num_items - 1) * spread) / 2;
115
116         for (i = 1; i < num_objects; i++, object++) {
117                 object_init (&model->objects[i],
118                              left_edge + (i - 1) * spread, height - 100, 1);
119                 spring_init (spring++, &model->objects[0], object, spacing);
120                 for (j = 1; j < num_objects - i; j++) {
121                         spacer_init (spacer++, object, object + j, spacing);
122                 }
123         }
124 }
125
126 static GdkFilterReturn
127 window_event (GdkXEvent *xevent, GdkEvent *event, gpointer data)
128 {
129         Closure *closure = data;
130         GdkModifierType state;
131         XEvent *ev = (XEvent *) xevent;
132         int x, y, i;
133         Object *object;
134
135         switch (ev->type) {
136         case ButtonPress:
137                 closure->drag_offset_x = ev->xbutton.x;
138                 closure->drag_offset_y = ev->xbutton.y;
139                 for (i = 0; i < closure->num_icons; i++) {
140                         if (closure->windows[i] == event->any.window) {
141                                 object = &closure->model.objects[i + 1];
142                                 closure->model.mouse_anchor.x = object->position.x;
143                                 closure->model.mouse_anchor.y = object->position.y;
144                                 closure->model.mouse_anchor.object = object;
145                         }
146                 }
147                 break;
148
149         case ButtonRelease:
150                 closure->model.mouse_anchor.object = NULL;
151                 break;
152
153         case MotionNotify:
154                 gdk_window_get_pointer (gdk_get_default_root_window(), &x, &y, &state);
155                 closure->model.mouse_anchor.x = x - closure->drag_offset_x;
156                 closure->model.mouse_anchor.y = y - closure->drag_offset_y;
157                 if (closure->model.mouse_anchor.y > closure->height)
158                         closure->model.mouse_anchor.y = closure->height;
159                 break;
160
161         default:
162                 break;
163         }
164
165         return GDK_FILTER_CONTINUE;
166 }
167
168 static const char *icons[] = {
169         "svg/applications-office.svg",
170         "svg/camera-video.svg",
171         "svg/email.svg",
172         "svg/firefox-logo.svg",
173         "svg/gnome-dev-disc-dvdrom.svg",
174         "svg/gnome-terminal.svg",
175         "svg/help-browser.svg",
176         "svg/internet-group-chat.svg"
177 };
178
179 int main (int argc, char *argv[])
180 {
181         Closure closure;
182         GdkScreen *screen;
183         const int num_icons = G_N_ELEMENTS (icons);
184         int x, y, width, height, i;
185         RsvgHandle *handle;
186         RsvgDimensionData dimension;
187         cairo_t *cr;
188         const int spacing = 50;
189
190         gtk_init (&argc, &argv);
191
192         rsvg_init ();
193
194         screen = gdk_screen_get_default ();
195         width = gdk_screen_get_width (screen);
196         height = gdk_screen_get_height (screen);
197
198         closure.spacing = spacing;
199         closure.height = height - 50;
200         closure.num_icons = num_icons;
201         closure.windows = g_new (GdkWindow *, num_icons);
202
203         model_init_dock (&closure.model, num_icons, width, height, spacing);
204
205         for (i = 0; i < num_icons; i++) {
206
207                 handle = rsvg_handle_new_from_file (icons[i], NULL);
208                 rsvg_handle_get_dimensions (handle, &dimension);
209
210                 x = closure.model.objects[i + 1].position.x;
211                 y = closure.model.objects[i + 1].position.y;
212                 closure.windows[i] =
213                         create_window (screen, x, y, dimension.width, dimension.height);
214
215                 gdk_window_show (closure.windows[i]);
216
217                 cr = gdk_cairo_create (closure.windows[i]);
218                 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
219                 cairo_paint (cr);
220                 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
221                 rsvg_handle_render_cairo (handle, cr);
222                 rsvg_handle_free (handle);
223                 cairo_destroy (cr);
224
225                 gdk_window_add_filter (closure.windows[i], window_event, &closure);
226         }
227
228         g_timeout_add (20, timeout_callback, &closure);
229
230         gtk_main ();
231
232         rsvg_term ();
233
234         return 0;
235 }