summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Almesberger <werner@almesberger.net>2016-08-18 01:45:24 -0300
committerWerner Almesberger <werner@almesberger.net>2016-08-18 01:49:20 -0300
commit07a60ddc211856fc7693cdd3654f53ee3da2be59 (patch)
treec608d41df89c68a3608454be3e5e3ee91aada7df
parent30d665115a8f7d72f659a1e77b6eb14e727fbe57 (diff)
downloadeeshow-07a60ddc211856fc7693cdd3654f53ee3da2be59.tar.gz
eeshow-07a60ddc211856fc7693cdd3654f53ee3da2be59.tar.bz2
eeshow-07a60ddc211856fc7693cdd3654f53ee3da2be59.zip
eeshow/gui/gui.c: begin moving input (mouse, keyboard) handling to input.c (WIP)
We had input state scattered all over the place. This cleans things up. We also merge the functions of left and (previously) middle button. This breaks history panning.
-rw-r--r--Makefile2
-rw-r--r--README7
-rw-r--r--gui/gui.c229
-rw-r--r--gui/input.c288
-rw-r--r--gui/input.h52
5 files changed, 418 insertions, 160 deletions
diff --git a/Makefile b/Makefile
index ce7b7ed..dcba627 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ NAME = eeshow
OBJS = main.o \
kicad/sch-parse.o kicad/sch-render.o kicad/lib-parse.o \
kicad/lib-render.o kicad/dwg.o kicad/delta.o \
- gui/gui.o gui/over.o gui/style.o gui/aoi.o gui/fmt-pango.o \
+ gui/gui.o gui/over.o gui/style.o gui/aoi.o gui/fmt-pango.o gui/input.o \
file/file.o file/git-util.o file/git-file.o file/git-hist.o \
gfx/style.o gfx/fig.o gfx/record.o gfx/cro.o gfx/diff.o gfx/gfx.o \
gfx/text.o gfx/misc.o \
diff --git a/README b/README
index e9bb49d..e916333 100644
--- a/README
+++ b/README
@@ -153,12 +153,13 @@ eeshow `sed -n '/pcbnew/q;/^LibName[0-9]*=/{s///;s/$/.lib/p;};d' anelok.pro` \
Mouse function in GUI mode
--------------------------
+Eeshow uses only the left mouse button and the scroll wheel.
+
Hover show additional details on revisions, sheets, global
labels
-Left-click jump to sheet (on sub-sheet, sheet stack, global
+Click jump to sheet (on sub-sheet, sheet stack, global
label hover box), open history, select revision
-Left-click and drag scroll history
-Middle-click and drag pan sheet
+Click and drag pan sheet, scroll history
Scroll wheel zoom in or out
diff --git a/gui/gui.c b/gui/gui.c
index ccbd7ba..4ba0e0c 100644
--- a/gui/gui.c
+++ b/gui/gui.c
@@ -43,6 +43,7 @@
#include "gui/aoi.h"
#include "gui/style.h"
#include "gui/over.h"
+#include "gui/input.h"
#include "gui/gui.h"
@@ -84,15 +85,9 @@ struct gui_hist {
struct gui_ctx {
GtkWidget *da;
- int curr_x; /* last on-screen mouse position */
- int curr_y;
-
unsigned zoom; /* scale by 1.0 / (1 << zoom) */
int x, y; /* center, in eeschema coordinates */
- bool panning;
- int pan_x, pan_y;
-
struct gui_hist *hist; /* revision history; NULL if none */
struct hist *vcs_hist; /* underlying VCS data; NULL if none */
@@ -307,40 +302,6 @@ static void eeschema_coord(const struct gui_ctx *ctx,
}
-/* ----- Panning ----------------------------------------------------------- */
-
-
-static void pan_begin(struct gui_ctx *ctx, int x, int y)
-{
- if (ctx->panning)
- return;
- ctx->panning = 1;
- ctx->pan_x = x;
- ctx->pan_y = y;
-}
-
-
-static void pan_update(struct gui_ctx *ctx, int x, int y)
-{
- if (!ctx->panning)
- return;
-
- ctx->x -= (x - ctx->pan_x) << ctx->zoom;
- ctx->y -= (y - ctx->pan_y) << ctx->zoom;
- ctx->pan_x = x;
- ctx->pan_y = y;
-
- redraw(ctx);
-}
-
-
-static void pan_end(struct gui_ctx *ctx, int x, int y)
-{
- pan_update(ctx, x, y);
- ctx->panning = 0;
-}
-
-
/* ----- Zoom -------------------------------------------------------------- */
@@ -813,114 +774,89 @@ static bool go_next_sheet(struct gui_ctx *ctx)
}
-/* ----- Event handlers ---------------------------------------------------- */
-
+/* ----- Input: sheet ------------------------------------------------------ */
-static bool botton_1_down = 0;
-
-static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
- gpointer data)
+static bool sheet_click(void *user, int x, int y)
{
- struct gui_ctx *ctx = data;
+ struct gui_ctx *ctx = user;
const struct gui_sheet *curr_sheet = ctx->curr_sheet;
- int x, y;
-
- ctx->curr_x = event->x;
- ctx->curr_y = event->y;
+ int ex, ey;
- canvas_coord(ctx, event->x, event->y, &x, &y);
+ canvas_coord(ctx, x, y, &ex, &ey);
- aoi_move(ctx->aois, event->x, event->y) ||
- aoi_move(curr_sheet->aois,
- x + curr_sheet->xmin, y + curr_sheet->ymin) ||
- aoi_hover(ctx->aois, event->x, event->y) ||
- aoi_hover(curr_sheet->aois,
- x + curr_sheet->xmin, y + curr_sheet->ymin);
- pan_update(ctx, event->x, event->y);
+ if (aoi_down(ctx->aois, x, y))
+ return aoi_up(ctx->aois, x, y);
+ if (aoi_down(curr_sheet->aois,
+ ex + curr_sheet->xmin, ey + curr_sheet->ymin))
+ return aoi_up(curr_sheet->aois,
+ ex + curr_sheet->xmin, ey + curr_sheet->ymin);
- return TRUE;
+ if (ctx->showing_history)
+ hide_history(ctx);
+ overlay_remove_all(&ctx->pop_overlays);
+ redraw(ctx);
+ return 1;
}
-static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
- gpointer data)
+static bool sheet_hover_update(void *user, int x, int y)
{
- struct gui_ctx *ctx = data;
+ struct gui_ctx *ctx = user;
const struct gui_sheet *curr_sheet = ctx->curr_sheet;
- int x, y;
+ int ex, ey;
- canvas_coord(ctx, event->x, event->y, &x, &y);
+ canvas_coord(ctx, x, y, &ex, &ey);
- switch (event->button) {
- case 1:
- /*
- * Double-click is sent as down-down-up, confusing the AoI
- * logic that assumes each "down" to have a matching "up".
- */
- if (botton_1_down)
- return TRUE;
- botton_1_down = 1;
+ if (aoi_move(ctx->aois, x, y))
+ return 1;
+ if (aoi_move(curr_sheet->aois,
+ ex + curr_sheet->xmin, ey + curr_sheet->ymin))
+ return 1;
+ if (aoi_hover(ctx->aois, x, y))
+ return 1;
+ return aoi_hover(curr_sheet->aois,
+ ex + curr_sheet->xmin, ey + curr_sheet->ymin);
+}
- if (aoi_down(ctx->aois, event->x, event->y))
- break;
- if (aoi_down(curr_sheet->aois,
- x + curr_sheet->xmin, y + curr_sheet->ymin))
- break;
- if (ctx->showing_history)
- hide_history(ctx);
- overlay_remove_all(&ctx->pop_overlays);
- redraw(ctx);
- break;
- case 2:
- pan_begin(ctx, event->x, event->y);
- break;
- case 3:
- break;
- }
- return TRUE;
+
+static void sheet_hover_end(void *user)
+{
}
-static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
- gpointer data)
+static void sheet_drag_move(void *user, int dx, int dy)
{
- struct gui_ctx *ctx = data;
- const struct gui_sheet *curr_sheet = ctx->curr_sheet;
- int x, y;
+ struct gui_ctx *ctx = user;
- canvas_coord(ctx, event->x, event->y, &x, &y);
+ ctx->x -= dx << ctx->zoom;
+ ctx->y -= dy << ctx->zoom;
+ redraw(ctx);
+}
- switch (event->button) {
- case 1:
- botton_1_down = 0;
- if (aoi_up(ctx->aois, event->x, event->y))
- break;
- if (aoi_up(curr_sheet->aois,
- x + curr_sheet->xmin, y + curr_sheet->ymin))
- break;
- break;
- case 2:
- pan_end(ctx, event->x, event->y);
- break;
- case 3:
- break;
- }
- return TRUE;
+static void sheet_scroll(void *user, int x, int y, int dy)
+{
+ struct gui_ctx *ctx = user;
+ int ex, ey;
+
+ canvas_coord(ctx, x, y, &ex, &ey);
+ if (dy < 0)
+ zoom_in(ctx, ex, ey);
+ else
+ zoom_out(ctx, ex, ey);
}
-static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
- gpointer data)
+static void sheet_key(void *user, int x, int y, int keyval)
{
- struct gui_ctx *ctx = data;
+ struct gui_ctx *ctx = user;
struct gui_sheet *sheet = ctx->curr_sheet;
- int x, y;
+ int ex, ey;
- canvas_coord(ctx, ctx->curr_x, ctx->curr_y, &x, &y);
+ canvas_coord(ctx, x, y, &ex, &ey);
- switch (event->keyval) {
+ switch (keyval) {
case '+':
case '=':
zoom_in(ctx, x, y);
@@ -958,29 +894,23 @@ static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
case GDK_KEY_q:
gtk_main_quit();
}
- return TRUE;
}
-static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
- gpointer data)
-{
- struct gui_ctx *ctx = data;
- int x, y;
+static const struct input_ops input_sheet_ops = {
+ .click = sheet_click,
+ .hover_begin = sheet_hover_update,
+ .hover_update = sheet_hover_update,
+ .hover_click = sheet_click,
+ .hover_end = sheet_hover_end,
+ .scroll = sheet_scroll,
+ .drag_begin = input_accept,
+ .drag_move = sheet_drag_move,
+ .key = sheet_key,
+};
- canvas_coord(ctx, event->x, event->y, &x, &y);
- switch (event->direction) {
- case GDK_SCROLL_UP:
- zoom_in(ctx, x, y);
- break;
- case GDK_SCROLL_DOWN:
- zoom_out(ctx, x, y);
- break;
- default:
- /* ignore */;
- }
- return TRUE;
-}
+
+/* ----- Event handlers ---------------------------------------------------- */
static void size_allocate_event(GtkWidget *widget, GdkRectangle *allocation,
@@ -1472,7 +1402,6 @@ int gui(unsigned n_args, char **args, bool recurse, int limit)
GtkWidget *window;
struct gui_ctx ctx = {
.zoom = 4, /* scale by 1 / 16 */
- .panning = 0,
.hist = NULL,
.vcs_hist = NULL,
.showing_history= 0,
@@ -1492,14 +1421,10 @@ int gui(unsigned n_args, char **args, bool recurse, int limit)
gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
gtk_window_set_title(GTK_WINDOW(window), "eeshow");
- gtk_widget_set_can_focus(ctx.da, TRUE);
-
gtk_widget_set_events(ctx.da,
- GDK_EXPOSE | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
- GDK_KEY_PRESS_MASK |
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_SCROLL_MASK |
- GDK_POINTER_MOTION_MASK);
+ GDK_EXPOSE | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+
+ input_setup(ctx.da);
gtk_widget_show_all(window);
@@ -1515,16 +1440,6 @@ int gui(unsigned n_args, char **args, bool recurse, int limit)
g_signal_connect(G_OBJECT(ctx.da), "draw",
G_CALLBACK(on_draw_event), &ctx);
- g_signal_connect(G_OBJECT(ctx.da), "motion_notify_event",
- G_CALLBACK(motion_notify_event), &ctx);
- g_signal_connect(G_OBJECT(ctx.da), "button_press_event",
- G_CALLBACK(button_press_event), &ctx);
- g_signal_connect(G_OBJECT(ctx.da), "button_release_event",
- G_CALLBACK(button_release_event), &ctx);
- g_signal_connect(G_OBJECT(ctx.da), "scroll_event",
- G_CALLBACK(scroll_event), &ctx);
- g_signal_connect(G_OBJECT(ctx.da), "key_press_event",
- G_CALLBACK(key_press_event), &ctx);
g_signal_connect(G_OBJECT(ctx.da), "size_allocate",
G_CALLBACK(size_allocate_event), &ctx);
@@ -1536,6 +1451,8 @@ int gui(unsigned n_args, char **args, bool recurse, int limit)
go_to_sheet(&ctx, ctx.new_hist->sheets);
gtk_widget_show_all(window);
+ input_push(&input_sheet_ops, &ctx);
+
/* for performance testing, use -N-depth */
if (limit >= 0)
gtk_main();
diff --git a/gui/input.c b/gui/input.c
new file mode 100644
index 0000000..3b430c3
--- /dev/null
+++ b/gui/input.c
@@ -0,0 +1,288 @@
+/*
+ * gui/input.c - Input operations
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+
+#include <gtk/gtk.h>
+
+#include "misc/util.h"
+#include "gui/input.h"
+
+
+#define DRAG_RADIUS 5
+
+
+static struct input {
+ const struct input_ops *ops;
+ void *user;
+
+ enum state {
+ input_normal,
+ input_clicking,
+ input_ignoring, /* click rejected by moving the cursor */
+ input_hovering,
+ input_dragging,
+ } state;
+
+ struct input *next;
+} *sp = NULL;
+
+static int curr_x, curr_y; /* last mouse position */
+static int clicked_x, clicked_y; /* button down position */
+
+
+/* ----- Mouse button ------------------------------------------------------ */
+
+
+static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
+ gpointer data)
+{
+ const struct input *old_sp = sp;
+
+ curr_x = event->x;
+ curr_y = event->y;
+
+ if (!sp)
+ return TRUE;
+
+ switch (sp->state) {
+ case input_normal:
+ if (sp->ops->hover_begin &&
+ sp->ops->hover_begin(sp->user, event->x, event->y))
+ sp->state = input_hovering;
+ assert(sp == old_sp);
+ break;
+ case input_clicking:
+ if (hypot(event->x - clicked_x, event->y - clicked_y) <
+ DRAG_RADIUS)
+ break;
+ if (sp->ops->drag_begin &&
+ sp->ops->drag_begin(sp->user, clicked_x, clicked_y))
+ sp->state = input_dragging;
+ else
+ sp->state = input_ignoring;
+ assert(sp == old_sp);
+ break;
+ case input_ignoring:
+ break;
+ case input_hovering:
+ if (!sp->ops->hover_update)
+ break;
+
+ /* Caution: hover_update may switch input layers */
+ if (sp->ops->hover_update(sp->user, event->x, event->y) &&
+ sp == old_sp) {
+ sp->state = input_normal;
+ if (sp->ops->hover_end)
+ sp->ops->hover_end(sp->user);
+ }
+ break;
+ case input_dragging:
+ if (sp->ops->drag_move)
+ sp->ops->drag_move(sp->user,
+ event->x - clicked_x, event->y - clicked_y);
+ clicked_x = event->x;
+ clicked_y = event->y;
+ break;
+ default:
+ abort();
+ }
+ return TRUE;
+}
+
+
+static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer data)
+{
+ const struct input *old_sp = sp;
+
+ if (event->button != 1)
+ return TRUE;
+
+ switch (sp->state) {
+ case input_normal:
+ sp->state = input_clicking;
+ clicked_x = event->x;
+ clicked_y = event->y;
+ break;
+ case input_clicking:
+ case input_ignoring:
+ case input_dragging:
+ /* ignore double-click */
+ break;
+ case input_hovering:
+ if (sp->ops->hover_click &&
+ sp->ops->hover_click(sp->user, event->x, event->y) &&
+ sp == old_sp) {
+ sp->state = input_ignoring;
+ if (sp->ops->hover_end)
+ sp->ops->hover_end(sp->user);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return TRUE;
+}
+
+
+static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer data)
+{
+ if (event->button != 1)
+ return TRUE;
+
+ switch (sp->state) {
+ case input_normal:
+ abort();
+ case input_clicking:
+ sp->state = input_normal;
+ if (sp->ops->click)
+ sp->ops->click(sp->user, clicked_x, clicked_y);
+ break;
+ case input_ignoring:
+ sp->state = input_normal;
+ break;
+ case input_dragging:
+ sp->state = input_normal;
+ if (sp->ops->drag_end)
+ sp->ops->drag_end(sp->user);
+ break;
+ case input_hovering:
+ break;
+ default:
+ abort();
+ }
+
+ return TRUE;
+}
+
+
+/* ----- Scroll wheel ------------------------------------------------------ */
+
+
+static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
+ gpointer data)
+{
+ if (!sp || !sp->ops->scroll)
+ return TRUE;
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ sp->ops->scroll(sp->user, event->x, event->y, -1);
+ break;
+ case GDK_SCROLL_DOWN:
+ sp->ops->scroll(sp->user, event->x, event->y, 1);
+ break;
+ default:
+ /* ignore */;
+ }
+ return TRUE;
+}
+
+
+/* ----- Keys -------------------------------------------------------------- */
+
+
+static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
+ gpointer data)
+{
+ if (sp && sp->ops->key)
+ sp->ops->key(sp->user, curr_x, curr_y, event->keyval);
+ return TRUE;
+}
+
+
+/* ----- Covenience function for hover_begin and drag_begin ---------------- */
+
+
+bool input_accept(void *user, int x, int y)
+{
+ return 1;
+}
+
+
+/* ----- Adding/removing interaction layers -------------------------------- */
+
+
+static void cleanup(void)
+{
+ if (!sp)
+ return;
+
+ switch (sp->state) {
+ case input_hovering:
+ if (sp->ops->hover_end)
+ sp->ops->hover_end(sp->user);
+ break;
+ case input_dragging:
+ if (sp->ops->drag_end)
+ sp->ops->drag_end(sp->user);
+ break;
+ default:
+ ;
+ }
+}
+
+
+void input_push(const struct input_ops *ops, void *user)
+{
+ struct input *new;
+
+ cleanup();
+
+ new = alloc_type(struct input);
+ new->ops = ops;
+ new->user = user;
+ new->state = input_normal;
+ new->next = sp;
+ sp = new;
+}
+
+
+void input_pop(void)
+{
+ struct input *next = sp->next;
+
+ cleanup();
+ free(sp);
+ sp = next;
+}
+
+
+/* ----- Initialization ---------------------------------------------------- */
+
+
+void input_setup(GtkWidget *da)
+{
+ gtk_widget_set_can_focus(da, TRUE);
+
+ gtk_widget_add_events(da,
+ GDK_KEY_PRESS_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+ g_signal_connect(G_OBJECT(da), "key_press_event",
+ G_CALLBACK(key_press_event), NULL);
+ g_signal_connect(G_OBJECT(da), "motion_notify_event",
+ G_CALLBACK(motion_notify_event), NULL);
+ g_signal_connect(G_OBJECT(da), "button_press_event",
+ G_CALLBACK(button_press_event), NULL);
+ g_signal_connect(G_OBJECT(da), "button_release_event",
+ G_CALLBACK(button_release_event), NULL);
+ g_signal_connect(G_OBJECT(da), "scroll_event",
+ G_CALLBACK(scroll_event), NULL);
+}
diff --git a/gui/input.h b/gui/input.h
new file mode 100644
index 0000000..7e32df6
--- /dev/null
+++ b/gui/input.h
@@ -0,0 +1,52 @@
+/*
+ * gui/input.h - Input operations
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef GUI_INPUT_H
+#define GUI_NPUT_H
+
+#include <stdbool.h>
+
+#include <gtk/gtk.h>
+
+
+/*
+ * All members of input_ops are optional, i.e., can be NULL.
+ *
+ * hover_begin and drag_begin must not call input_push or input_pop.
+ */
+
+struct input_ops {
+ bool (*click)(void *user, int x, int y);
+
+ bool (*hover_begin)(void *user, int x, int y);
+ bool (*hover_update)(void *user, int x, int y);
+ bool (*hover_click)(void *user, int x, int y);
+ void (*hover_end)(void *user);
+
+ bool (*drag_begin)(void *user, int x, int y);
+ void (*drag_move)(void *user, int dx, int dy);
+ void (*drag_end)(void *user);
+
+ void (*scroll)(void *user, int x, int y, int dy);
+ /* down = 1, up = -1 */
+
+ void (*key)(void *user, int x, int y, int keyval);
+};
+
+
+bool input_accept(void *user, int x, int y);
+
+void input_push(const struct input_ops *ops, void *user);
+void input_pop(void);
+void input_setup(GtkWidget *da);
+
+#endif /* !GUI_INPUT_H */