summaryrefslogtreecommitdiff
path: root/dock.c
diff options
context:
space:
mode:
Diffstat (limited to 'dock.c')
-rw-r--r--dock.c564
1 files changed, 403 insertions, 161 deletions
diff --git a/dock.c b/dock.c
index ecf663a..786085f 100644
--- a/dock.c
+++ b/dock.c
@@ -1,4 +1,6 @@
/* -*- mode: c; c-basic-offset: 2 -*-
+ *
+ * This is kiba, a useless but fun GNOME dock.
*/
#include <gtk/gtk.h>
@@ -9,69 +11,72 @@
#include <string.h>
#include <sys/time.h>
#include <math.h>
-#include <librsvg/rsvg.h>
-#include <librsvg/rsvg-cairo.h>
+#include <gconf/gconf-client.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include "akamaru.h"
-typedef struct Closure Closure;
-struct Closure {
+#define KIBA_GCONF_PATH "/apps/kiba"
+#define PIXMAP_PATH "/usr/share/pixmaps"
+
+typedef struct Dock Dock;
+typedef struct Launcher Launcher;
+
+struct Dock {
+ GtkWidget *window;
Model model;
- int num_icons;
- GdkWindow **windows;
- int drag_offset_x, drag_offset_y;
+ int width, height;
int spacing;
- int height;
+ int num_launchers;
+ GList *launchers;
+ int drag_offset_x, drag_offset_y;
+
+ GConfClient *gconf_client;
+};
+
+struct Launcher {
+ GdkWindow *window;
+ GdkPixbuf *pixbuf;
+ Dock *dock;
+ Object *object;
+ char *name;
+ char *exec;
+ char *comment;
+ char *icon;
};
+GQuark kiba_error_quark (void)
+{
+ static GQuark q = 0;
+
+ if (q == 0)
+ q = g_quark_from_static_string ("kiba-quark");
+
+ return q;
+}
+
+#define KIBA_ERROR kiba_error_quark ()
+
static gint
timeout_callback (gpointer data)
{
- Closure *closure = data;
+ Dock *dock = data;
+ GList *l;
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);
+ for (l = dock->launchers; l != NULL; l = l->next) {
+ Launcher *launcher = l->data;
+ gdk_window_move (launcher->window,
+ launcher->object->position.x + 0.5,
+ launcher->object->position.y + 0.5);
}
for (i = 0; i < 6; i++)
- model_step (&closure->model, 0.01);
+ model_step (&dock->model, 0.01);
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_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 width, int height, int spacing)
@@ -102,10 +107,16 @@ model_init_dock (Model *model, int num_items,
model->k = 0.8;
model->constrain_iterations = 1;
- model->polygons = g_new (Polygon, 1);
- model->num_polygons = 1;
+ model->polygons = g_new (Polygon, 3);
+ model->num_polygons = 3;
+ left_edge = (width - (num_items - 1) * spacing) / 2;
polygon_init_enclosing_rectangle (&model->polygons[0],
0, 0, width - 50, height - 50);
+ polygon_init_rectangle (&model->polygons[1],
+ 0, height - 70, left_edge, height);
+ polygon_init_rectangle (&model->polygons[2],
+ width - left_edge + 2, height - 70,
+ width, height);
model->anchors[0].x = width / 2;
model->anchors[0].y = height - 50;
@@ -121,8 +132,8 @@ model_init_dock (Model *model, int num_items,
for (i = 1; i < num_objects; i++, object++) {
object_init (&model->objects[i],
- left_edge + (i - 1) * spread, height - 100, 10);
- spring_init (spring++, &model->objects[0], object, spacing);
+ left_edge + (i - 1) * spread, height - 100, 12);
+ spring_init (spring++, &model->objects[0], object, 0);
for (j = 1; j < num_objects - i; j++) {
spacer_init (spacer++, object, object + j, spacing);
}
@@ -132,36 +143,34 @@ model_init_dock (Model *model, int num_items,
static GdkFilterReturn
window_event (GdkXEvent *xevent, GdkEvent *event, gpointer data)
{
- Closure *closure = data;
+ Dock *dock = data;
+ GList *l;
GdkModifierType state;
XEvent *ev = (XEvent *) xevent;
- int x, y, i;
- Object *object;
+ int x, y;
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;
+ dock->drag_offset_x = ev->xbutton.x;
+ dock->drag_offset_y = ev->xbutton.y;
+ for (l = dock->launchers; l != NULL; l = l->next) {
+ Launcher *launcher = l->data;
+ if (launcher->window == event->any.window) {
+ dock->model.mouse_anchor.x = launcher->object->position.x;
+ dock->model.mouse_anchor.y = launcher->object->position.y;
+ dock->model.mouse_anchor.object = launcher->object;
}
}
break;
case ButtonRelease:
- closure->model.mouse_anchor.object = NULL;
+ dock->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 - closure->drag_offset_x;
- closure->model.mouse_anchor.y = y - closure->drag_offset_y;
- if (closure->model.mouse_anchor.y > closure->height)
- closure->model.mouse_anchor.y = closure->height;
+ dock->model.mouse_anchor.x = x - dock->drag_offset_x;
+ dock->model.mouse_anchor.y = y - dock->drag_offset_y;
break;
default:
@@ -171,126 +180,359 @@ window_event (GdkXEvent *xevent, GdkEvent *event, gpointer data)
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"
-};
+static gboolean
+dock_paint (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ Dock *dock = user_data;
+ cairo_t *cr;
+ cairo_pattern_t *gradient;
+ const int hmargin = 5, vmargin = 40, radius = 5;
+
+ cr = gdk_cairo_create (widget->window);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_move_to (cr, hmargin, dock->height);
+ cairo_line_to (cr, hmargin, vmargin + radius);
+ cairo_arc (cr, hmargin + radius, vmargin + radius,
+ radius, M_PI, 3 * M_PI / 2);
+ cairo_line_to (cr, dock->width - hmargin - radius, vmargin);
+ cairo_arc (cr, dock->width - hmargin - radius, vmargin + radius,
+ radius, 3 * M_PI / 2, 2 * M_PI);
+ cairo_line_to (cr, dock->width - hmargin, dock->height);
+
+ gradient = cairo_pattern_create_linear (dock->width / 2 - 1, vmargin,
+ dock->width / 2 + 1, dock->height);
+ cairo_pattern_add_color_stop_rgba (gradient, 0, 1, 1, 1, 0.4);
+ cairo_pattern_add_color_stop_rgba (gradient, 1, 1, 1, 1, 0.6);
+ cairo_set_source (cr, gradient);
+ cairo_fill_preserve (cr);
+
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_set_line_width (cr, 1);
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
static void
-create_dock (GdkScreen *screen, int num_icons, int spacing)
+parse_desktop_file (Launcher *launcher, const char *uri, GError **error)
{
- const int padding = 20;
- GdkWindow *dock;
- int dock_width, dock_height;
- int screen_width, screen_height;
- cairo_t *cr;
- cairo_pattern_t *gradient;
- const int hmargin = 5, vmargin = 40, radius = 5;
-
- screen_width = gdk_screen_get_width (screen);
- screen_height = gdk_screen_get_height (screen);
-
- dock_width = num_icons * spacing + 2 * padding;
- dock_height = spacing + padding;
- dock = create_window (screen,
- (screen_width - dock_width + spacing) / 2,
- screen_height - dock_height,
- dock_width, dock_height);
-
- gdk_window_show (dock);
-
- cr = gdk_cairo_create (dock);
- cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
- cairo_paint (cr);
-
- cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
- cairo_move_to (cr, hmargin, dock_height);
- cairo_line_to (cr, hmargin, vmargin + radius);
- cairo_arc (cr, hmargin + radius, vmargin + radius,
- radius, M_PI, 3 * M_PI / 2);
- cairo_line_to (cr, dock_width - hmargin - radius, vmargin);
- cairo_arc (cr, dock_width - hmargin - radius, vmargin + radius,
- radius, 3 * M_PI / 2, 2 * M_PI);
- cairo_line_to (cr, dock_width - hmargin, dock_height);
-
- gradient = cairo_pattern_create_linear (dock_width / 2 - 1, vmargin,
- dock_width / 2 + 1, dock_height);
- cairo_pattern_add_color_stop_rgba (gradient, 0, 1, 1, 1, 0.4);
- cairo_pattern_add_color_stop_rgba (gradient, 1, 1, 1, 1, 0.6);
- cairo_set_source (cr, gradient);
- cairo_fill_preserve (cr);
-
- cairo_set_source_rgba (cr, 1, 1, 1, 1);
- cairo_set_line_width (cr, 1);
- cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
- cairo_stroke (cr);
-
- cairo_destroy (cr);
+ static const char file_uri_prefix[] = "file://";
+ static const char desktop_file_suffix[] = ".desktop";
+ static const char desktop_entry_group[] = "Desktop Entry";
+ static const struct { const char *name; size_t offset; } fields[] = {
+ { "Icon", G_STRUCT_OFFSET (Launcher, icon) },
+ { "Exec", G_STRUCT_OFFSET (Launcher, exec) },
+ { "Name", G_STRUCT_OFFSET (Launcher, name) },
+ { "Comment", G_STRUCT_OFFSET (Launcher, comment) },
+ };
+
+ GKeyFile *file;
+ const char *filename ;
+ char *s;
+ int i;
+
+ if (!g_str_has_suffix (uri, desktop_file_suffix)) {
+ g_set_error (error, KIBA_ERROR, 0,
+ "%s is not a .desktop file", uri);
+ return;
+ }
+ if (g_str_has_prefix (uri, file_uri_prefix))
+ filename = uri + strlen (file_uri_prefix);
+ else
+ filename = uri;
+
+ file = g_key_file_new ();
+ if (!g_key_file_load_from_file (file, filename, 0, error))
+ return;
+
+ for (i = 0; i < G_N_ELEMENTS (fields); i++) {
+ s = g_key_file_get_value (file, desktop_entry_group,
+ fields[i].name, error);
+ if (s == NULL)
+ return;
+
+ G_STRUCT_MEMBER (char *, launcher, fields[i].offset) = s;
+ }
}
-int main (int argc, char *argv[])
+static gboolean
+load_icon (Launcher *launcher, GError **error)
{
- 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 spacing = 50;
+ char *path;
+ char *suffix;
- gtk_init (&argc, &argv);
+ path = g_strdup_printf (PIXMAP_PATH "/%s", launcher->icon);
+ launcher->pixbuf = gdk_pixbuf_new_from_file (path, error);
+ g_free (path);
+
+ if (launcher->pixbuf != NULL)
+ return TRUE;
+
+ g_clear_error (error);
+ launcher->pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
+ launcher->icon, 48, 0, error);
+ if (launcher->pixbuf != NULL)
+ return TRUE;
+
+ suffix = strstr (launcher->icon, ".png");
+ if (suffix == NULL)
+ return FALSE;
+
+ *suffix = '\0';
+ g_clear_error (error);
+ launcher->pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
+ launcher->icon, 48, 0, error);
+
+ return launcher->pixbuf != NULL;
+}
+
+static void
+launcher_create_window (Launcher *launcher, GError **error)
+{
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ GdkScreen *screen;
+ screen = gdk_screen_get_default ();
+ cairo_t *cr;
- rsvg_init ();
+ if (!load_icon (launcher, error))
+ return;
- screen = gdk_screen_get_default ();
- width = gdk_screen_get_width (screen);
- height = gdk_screen_get_height (screen);
+ 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;
- closure.spacing = spacing;
- closure.height = height - 50;
- closure.num_icons = num_icons;
- closure.windows = g_new (GdkWindow *, num_icons);
+ attributes.x = launcher->object->position.x;
+ attributes.y = launcher->object->position.y;
+ attributes.width = gdk_pixbuf_get_width (launcher->pixbuf);
+ attributes.height = gdk_pixbuf_get_height (launcher->pixbuf);
+ 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_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
- model_init_dock (&closure.model, num_icons, width, height, spacing);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
- create_dock (screen, num_icons, spacing);
+ launcher->window = gdk_window_new (gdk_screen_get_root_window (screen),
+ &attributes, attributes_mask);
+ gdk_window_show (launcher->window);
- for (i = 0; i < num_icons; i++) {
+ cr = gdk_cairo_create (launcher->window);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_destroy (cr);
- handle = rsvg_handle_new_from_file (icons[i], NULL);
- rsvg_handle_get_dimensions (handle, &dimension);
+ gdk_pixbuf_render_to_drawable_alpha
+ (launcher->pixbuf,
+ launcher->window,
+ 0, 0, 0, 0,
+ gdk_pixbuf_get_width (launcher->pixbuf),
+ gdk_pixbuf_get_height (launcher->pixbuf),
+ GDK_PIXBUF_ALPHA_FULL, 0,
+ GDK_RGB_DITHER_NONE, 0, 0);
- x = closure.model.objects[i + 1].position.x;
- y = closure.model.objects[i + 1].position.y;
- closure.windows[i] =
- create_window (screen, x, y, dimension.width, dimension.height);
+ gdk_window_add_filter (launcher->window, window_event, launcher->dock);
+}
+
+static Launcher *
+launcher_new (Dock *dock, Object *object, const char *gconf_path)
+{
+ Launcher *launcher;
+ GError *error = NULL;
+ char *desktop_file;
+ char *key;
+
+ launcher = g_new (Launcher, 1);
+
+ key = g_strdup_printf ("%s/file", gconf_path);
+ desktop_file = gconf_client_get_string (dock->gconf_client, key, &error);
+ g_free (key);
+ if (error != NULL) {
+ printf ("error getting desktop file path for launcher: %s\n",
+ error->message);
+ g_error_free (error);
+ g_free (launcher);
+ return NULL;
+ }
- gdk_window_show (closure.windows[i]);
+ parse_desktop_file (launcher, desktop_file, &error);
+ free (desktop_file);
+ if (error != NULL) {
+ printf ("error parsing desktop file: %s\n", error->message);
+ g_error_free (error);
+ g_free (launcher);
+ return NULL;
+ }
+
+ launcher->object = object;
+ launcher->dock = dock;
+
+ launcher_create_window (launcher, &error);
+ if (error != NULL) {
+ printf ("error creating launcher window: %s\n", error->message);
+ g_error_free (error);
+ g_free (launcher);
+ return NULL;
+ }
+
+ return launcher;
+}
- 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);
+static void
+dock_drag_data_received(GtkWidget *widget, GdkDragContext *drag_context,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint time, gpointer data)
+{
+ Dock *dock = data;
+ GError *error = NULL;
+ char *key;
+
+ gtk_drag_finish (drag_context, TRUE, FALSE, time);
+ g_strstrip ((char *) selection_data->data);
+ printf ("got desktop file: %s\n", selection_data->data);
+
+ key = g_strdup_printf (KIBA_GCONF_PATH "/launchers/%x/file", time);
+ gconf_client_set_string (dock->gconf_client,
+ key, (char *) selection_data->data, &error);
+ if (error != NULL) {
+ printf ("failed to set gconf key %s: %s\n", key, error->message);
+ g_error_free (error);
+ }
- gdk_window_add_filter (closure.windows[i], window_event, &closure);
+ if (error == NULL) {
+ gconf_engine_associate_schema (dock->gconf_client->engine,
+ key,
+ "/schemas" KIBA_GCONF_PATH "/launchers/file",
+ &error);
+ if (error != NULL) {
+ printf ("failed to set schema for %s: %s\n", key, error->message);
+ g_error_free (error);
}
+ }
- g_timeout_add (20, timeout_callback, &closure);
+ g_free (key);
+}
- gtk_main ();
+static GtkWidget *
+create_dock_window (Dock *dock)
+{
+ const int padding = 20;
+ GtkWidget *window;
+ GdkScreen *screen;
+ GdkColormap *colormap;
+ int screen_width, screen_height;
+ static const GtkTargetEntry targets[] = {
+ { "text/plain", 0, 0 }
+ };
+
+ screen = gdk_screen_get_default ();
+
+ screen_width = gdk_screen_get_width (screen);
+ screen_height = gdk_screen_get_height (screen);
+
+ dock->width = dock->num_launchers * dock->spacing + 2 * padding;
+ dock->height = dock->spacing + padding;
+
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+ colormap = gdk_screen_get_rgba_colormap (screen);
+ gtk_widget_set_colormap(GTK_WIDGET (window), colormap);
+
+ gtk_drag_dest_set(window,
+ GTK_DEST_DEFAULT_ALL,
+ targets, G_N_ELEMENTS (targets),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY);
+ gtk_drag_dest_add_uri_targets (window);
+
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ dock->width, dock->height);
+ gtk_window_move (GTK_WINDOW (window),
+ (screen_width - dock->width + dock->spacing) / 2,
+ screen_height - dock->height);
+
+ g_signal_connect (window, "expose-event",
+ G_CALLBACK (dock_paint), dock);
+ g_signal_connect (window, "drag-data-received",
+ G_CALLBACK (dock_drag_data_received), dock);
+ gtk_widget_set_app_paintable (window, TRUE);
+ gtk_widget_realize (window);
+ gdk_window_set_back_pixmap (window->window, NULL, FALSE);
+ gtk_widget_show (window);
+
+ return window;
+}
+
+static Dock *
+dock_new (void)
+{
+ Dock *dock;
+ GSList *launchers, *l;
+ int num_launchers;
+ GError *error = NULL;
+ GdkScreen *screen;
+ int width, height, i;
+ Launcher *launcher;
+
+ dock = g_new0 (Dock, 1);
+
+ dock->gconf_client = gconf_client_get_default ();
+
+ gconf_client_add_dir (dock->gconf_client, KIBA_GCONF_PATH,
+ GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+ launchers = gconf_client_all_dirs (dock->gconf_client,
+ KIBA_GCONF_PATH "/launchers",
+ &error);
+ if (error != NULL) {
+ printf ("error getting launchers: %s\n", error->message);
+ g_free (error->message);
+ }
+
+ num_launchers = g_slist_length (launchers);
+
+ screen = gdk_screen_get_default ();
+ width = gdk_screen_get_width (screen);
+ height = gdk_screen_get_height (screen);
+
+ dock->spacing = 50;
+ dock->num_launchers = num_launchers;
+ dock->window = create_dock_window (dock);
- rsvg_term ();
+ model_init_dock (&dock->model, num_launchers, width, height, dock->spacing);
+
+ for (l = launchers, i = 0; l != NULL; l = l->next, i++) {
+ char *path = l->data;
+ launcher = launcher_new (dock, &dock->model.objects[i + 1], path);
+ if (launcher == NULL)
+ continue;
+ dock->launchers = g_list_prepend (dock->launchers, launcher);
+ }
+
+ g_timeout_add (20, timeout_callback, dock);
+
+ return dock;
+}
+
+int main (int argc, char *argv[])
+{
+ Dock *dock;
+
+ gtk_init (&argc, &argv);
+
+ dock = dock_new ();
+
+ gtk_main ();
return 0;
}