Root/eeshow/gui/sheet.c

1/*
2 * gui/sheet.c - Sheet navigation
3 *
4 * Written 2016 by Werner Almesberger
5 * Copyright 2016 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <stddef.h>
14#include <stdbool.h>
15#include <stdlib.h>
16
17#include <gtk/gtk.h>
18
19#include "file/git-hist.h"
20#include "kicad/sch.h"
21#include "kicad/delta.h"
22#include "gui/fmt-pango.h"
23#include "gui/style.h"
24#include "gui/aoi.h"
25#include "gui/over.h"
26#include "gui/input.h"
27#include "gui/help.h"
28#include "gui/icons.h"
29#include "gui/common.h"
30
31
32#define ZOOM_FACTOR 1.26 /* 2^(1/3) */
33#define ZOOM_MAX 1
34#define ZOOM_MIN_SIZE 16
35#define ZOOM_MARGIN 0.95
36
37
38/* ----- Tools ------------------------------------------------------------- */
39
40
41static void canvas_coord(const struct gui_ctx *ctx,
42    int ex, int ey, int *x, int *y)
43{
44    GtkAllocation alloc;
45    int sx, sy;
46
47    gtk_widget_get_allocation(ctx->da, &alloc);
48    sx = ex - alloc.width / 2;
49    sy = ey - alloc.height / 2;
50    *x = sx / ctx->scale + ctx->x;
51    *y = sy / ctx->scale + ctx->y;
52}
53
54
55/* ----- Zoom -------------------------------------------------------------- */
56
57
58static bool zoom_in(struct gui_ctx *ctx, int x, int y)
59{
60    float old = ctx->scale;
61
62    if (ctx->scale == ZOOM_MAX)
63        return 0;
64    ctx->scale *= ZOOM_FACTOR;
65    if (ctx->scale > ZOOM_MAX)
66        ctx->scale = ZOOM_MAX;
67    ctx->x = x + (ctx->x - x) * old / ctx->scale;
68    ctx->y = y + (ctx->y - y) * old / ctx->scale;
69    redraw(ctx);
70    return 1;
71}
72
73
74static bool zoom_out(struct gui_ctx *ctx, int x, int y)
75{
76    float old = ctx->scale;
77
78    if (ctx->curr_sheet->w * ctx->scale <= ZOOM_MIN_SIZE)
79        return 0;
80    ctx->scale /= ZOOM_FACTOR;
81    if (ctx->curr_sheet->w <= ZOOM_MIN_SIZE)
82        ctx->scale = 1;
83    else if (ctx->curr_sheet->w * ctx->scale <= ZOOM_MIN_SIZE )
84        ctx->scale = (float) ZOOM_MIN_SIZE / ctx->curr_sheet->w;
85    ctx->x = x + (ctx->x - x) * old / ctx->scale;
86    ctx->y = y + (ctx->y - y) * old / ctx->scale;
87    redraw(ctx);
88    return 1;
89}
90
91
92static void curr_sheet_size(struct gui_ctx *ctx, int *w, int *h)
93{
94    const struct gui_sheet *sheet = ctx->curr_sheet;
95    int ax1, ay1, bx1, by1;
96
97    if (!ctx->old_hist) {
98        *w = sheet->w;
99        *h = sheet->h;
100    } else {
101        const struct gui_sheet *old =
102            find_corresponding_sheet(ctx->old_hist->sheets,
103            ctx->new_hist->sheets, sheet);
104
105        /*
106         * We're only interested in differences here, so no need for
107         * the usual "-1" in x1 = x0 + w - 1
108         */
109        ax1 = sheet->xmin + sheet->w;
110        ay1 = sheet->ymin + sheet->h;
111        bx1 = old->xmin + old->w;
112        by1 = old->ymin + old->h;
113        *w = (ax1 > bx1 ? ax1 : bx1) -
114            (sheet->xmin < old->xmin ? sheet->xmin : old->xmin);
115        *h = (ay1 > by1 ? ay1 : by1) -
116            (sheet->ymin < old->ymin ? sheet->ymin : old->ymin);
117    }
118}
119
120
121static void zoom_to_extents(struct gui_ctx *ctx)
122{
123    GtkAllocation alloc;
124    int w, h;
125    float sw, sh;
126
127    curr_sheet_size(ctx, &w, &h);
128    ctx->x = w / 2;
129    ctx->y = h / 2;
130
131    gtk_widget_get_allocation(ctx->da, &alloc);
132    
133    sw = w ? (float) ZOOM_MARGIN * alloc.width / w : 1;
134    sh = h ? (float) ZOOM_MARGIN * alloc.height / h : 1;
135    ctx->scale = sw < sh ? sw : sh;
136
137    redraw(ctx);
138}
139
140
141/* ----- Revision selection overlays --------------------------------------- */
142
143
144static bool show_history_details(void *user, bool on, int dx, int dy)
145{
146    struct gui_hist *h = user;
147    struct gui_ctx *ctx = h->ctx;
148    char *s;
149
150    if (on) {
151        s = vcs_git_long_for_pango(h->vcs_hist, fmt_pango);
152        overlay_text_raw(h->over, s);
153        free(s);
154    } else {
155        overlay_text(h->over, "%.40s", vcs_git_summary(h->vcs_hist));
156    }
157    redraw(ctx);
158    return 1;
159}
160
161
162static void set_diff_mode(struct gui_ctx *ctx, enum diff_mode mode)
163{
164    ctx->diff_mode = mode;
165    do_revision_overlays(ctx);
166    redraw(ctx);
167}
168
169
170static void show_history_cb(void *user)
171{
172    struct gui_hist *h = user;
173    struct gui_ctx *ctx = h->ctx;
174    enum selecting sel = sel_only;
175
176    if (ctx->old_hist) {
177        if (h == ctx->new_hist && ctx->diff_mode != diff_new) {
178            set_diff_mode(ctx, diff_new);
179            return;
180        }
181        if (h == ctx->old_hist && ctx->diff_mode != diff_old) {
182            set_diff_mode(ctx, diff_old);
183            return;
184        }
185        sel = h == ctx->new_hist ? sel_new : sel_old;
186    }
187    show_history(ctx, sel);
188}
189
190
191static void show_diff_cb(void *user)
192{
193    struct gui_ctx *ctx = user;
194
195    if (ctx->old_hist)
196        set_diff_mode(ctx,
197            ctx->diff_mode == diff_delta ? diff_new : diff_delta);
198    else
199        show_history(ctx, sel_split);
200}
201
202
203static void toggle_old_new(struct gui_ctx *ctx)
204{
205    set_diff_mode(ctx, ctx->diff_mode == diff_new ? diff_old : diff_new);
206}
207
208
209static void add_delta(struct gui_ctx *ctx)
210{
211    struct overlay *over;
212    struct overlay_style style;
213
214    over = overlay_add(&ctx->hist_overlays, &ctx->aois,
215        NULL, show_diff_cb, ctx);
216    style = overlay_style_default;
217    if (ctx->old_hist && ctx->diff_mode == diff_delta)
218        style.frame = RGBA(0, 0, 0, 1);
219    style.pad = 4;
220    overlay_style(over, &style);
221    if (use_delta)
222        overlay_icon(over, icon_delta);
223    else
224        overlay_icon(over, icon_diff);
225}
226
227
228static void revision_overlays_diff(struct gui_ctx *ctx)
229{
230    struct gui_hist *new = ctx->new_hist;
231    struct gui_hist *old = ctx->old_hist;
232    struct overlay_style style;
233
234    new->over = overlay_add(&ctx->hist_overlays, &ctx->aois,
235        show_history_details, show_history_cb, new);
236    style = overlay_style_diff_new;
237    if (ctx->diff_mode == diff_new)
238        style.frame = RGBA(0, 0, 0, 1);
239    overlay_style(new->over, &style);
240    show_history_details(new, 0, 0, 0);
241
242    add_delta(ctx);
243
244    old->over = overlay_add(&ctx->hist_overlays, &ctx->aois,
245        show_history_details, show_history_cb, old);
246    style = overlay_style_diff_old;
247    if (ctx->diff_mode == diff_old)
248        style.frame = RGBA(0, 0, 0, 1);
249    overlay_style(old->over, &style);
250    show_history_details(old, 0, 0, 0);
251}
252
253
254void do_revision_overlays(struct gui_ctx *ctx)
255{
256    overlay_remove_all(&ctx->hist_overlays);
257
258    if (ctx->old_hist) {
259        revision_overlays_diff(ctx);
260    } else {
261        ctx->new_hist->over = overlay_add(&ctx->hist_overlays,
262            &ctx->aois, show_history_details, show_history_cb,
263            ctx->new_hist);
264        overlay_style(ctx->new_hist->over, &overlay_style_default);
265        show_history_details(ctx->new_hist, 0, 0, 0);
266
267        add_delta(ctx);
268    }
269}
270
271
272/* ----- Sheet selection overlays ------------------------------------------ */
273
274
275static void close_subsheet(void *user)
276{
277    struct gui_sheet *sheet = user;
278    struct gui_ctx *ctx = sheet->ctx;
279
280    go_to_sheet(ctx, sheet);
281}
282
283
284static bool hover_sheet(void *user, bool on, int dx, int dy)
285{
286    struct gui_sheet *sheet = user;
287    struct gui_ctx *ctx = sheet->ctx;
288    const char *title = sheet->sch->title;
289
290    if (!title)
291        title = "(unnamed)";
292    if (on) {
293        const struct gui_sheet *s;
294        int n = 0, this = -1;
295
296        for (s = ctx->new_hist->sheets; s; s = s->next) {
297            n++;
298            if (s == sheet)
299                this = n;
300        }
301        overlay_text(sheet->over, "<b>%s</b>\n<big>%d / %d</big>",
302            title, this, n);
303    } else {
304        overlay_text(sheet->over, "<b>%s</b>", title);
305    }
306    redraw(ctx);
307    return 1;
308}
309
310
311static struct gui_sheet *find_parent_sheet(struct gui_sheet *sheets,
312    const struct gui_sheet *ref)
313{
314    struct gui_sheet *parent;
315    const struct sch_obj *obj;
316
317    for (parent = sheets; parent; parent = parent->next)
318        for (obj = parent->sch->objs; obj; obj = obj->next)
319            if (obj->type == sch_obj_sheet &&
320                obj->u.sheet.sheet == ref->sch)
321                return parent;
322    return NULL;
323}
324
325
326static void sheet_selector_recurse(struct gui_ctx *ctx, struct gui_sheet *sheet)
327{
328    struct gui_sheet *parent;
329
330    parent = find_parent_sheet(ctx->new_hist->sheets, sheet);
331    if (parent)
332        sheet_selector_recurse(ctx, parent);
333    sheet->over = overlay_add(&ctx->sheet_overlays, &ctx->aois,
334        hover_sheet, close_subsheet, sheet);
335    hover_sheet(sheet, 0, 0, 0);
336}
337
338
339static void do_sheet_overlays(struct gui_ctx *ctx)
340{
341    overlay_remove_all(&ctx->sheet_overlays);
342    sheet_selector_recurse(ctx, ctx->curr_sheet);
343}
344
345
346/* ----- Navigate sheets --------------------------------------------------- */
347
348
349void go_to_sheet(struct gui_ctx *ctx, struct gui_sheet *sheet)
350{
351    aoi_dehover();
352    overlay_remove_all(&ctx->pop_overlays);
353    overlay_remove_all(&ctx->pop_underlays);
354    if (!sheet->rendered) {
355        render_sheet(sheet);
356        mark_aois(ctx, sheet);
357    }
358    ctx->curr_sheet = sheet;
359    if (ctx->old_hist)
360        render_delta(ctx);
361    if (ctx->vcs_hist)
362        do_revision_overlays(ctx);
363    do_sheet_overlays(ctx);
364    zoom_to_extents(ctx);
365}
366
367
368static bool go_up_sheet(struct gui_ctx *ctx)
369{
370    struct gui_sheet *parent;
371
372    parent = find_parent_sheet(ctx->new_hist->sheets, ctx->curr_sheet);
373    if (!parent)
374        return 0;
375    go_to_sheet(ctx, parent);
376    return 1;
377}
378
379
380static bool go_prev_sheet(struct gui_ctx *ctx)
381{
382    struct gui_sheet *sheet;
383
384    for (sheet = ctx->new_hist->sheets; sheet; sheet = sheet->next)
385        if (sheet->next && sheet->next == ctx->curr_sheet) {
386            go_to_sheet(ctx, sheet);
387            return 1;
388        }
389    return 0;
390}
391
392
393static bool go_next_sheet(struct gui_ctx *ctx)
394{
395    if (!ctx->curr_sheet->next)
396        return 0;
397    go_to_sheet(ctx, ctx->curr_sheet->next);
398    return 1;
399}
400
401
402/* ----- Input ------------------------------------------------------------- */
403
404
405static bool sheet_click(void *user, int x, int y)
406{
407    struct gui_ctx *ctx = user;
408    const struct gui_sheet *curr_sheet = ctx->curr_sheet;
409    int ex, ey;
410
411    canvas_coord(ctx, x, y, &ex, &ey);
412
413    if (ctx->old_hist && ctx->diff_mode == diff_old)
414        curr_sheet = find_corresponding_sheet(ctx->old_hist->sheets,
415            ctx->new_hist->sheets, ctx->curr_sheet);
416
417    if (aoi_click(&ctx->aois, x, y))
418        return 1;
419    if (aoi_click(&curr_sheet->aois,
420        ex + curr_sheet->xmin, ey + curr_sheet->ymin))
421        return 1;
422
423    overlay_remove_all(&ctx->pop_overlays);
424    overlay_remove_all(&ctx->pop_underlays);
425    redraw(ctx);
426    return 1;
427}
428
429
430static bool sheet_hover_update(void *user, int x, int y)
431{
432    struct gui_ctx *ctx = user;
433    const struct gui_sheet *curr_sheet = ctx->curr_sheet;
434    int ex, ey;
435
436    canvas_coord(ctx, x, y, &ex, &ey);
437
438    if (ctx->old_hist && ctx->diff_mode == diff_old)
439        curr_sheet = find_corresponding_sheet(ctx->old_hist->sheets,
440            ctx->new_hist->sheets, ctx->curr_sheet);
441
442    if (aoi_hover(&ctx->aois, x, y))
443        return 1;
444    return aoi_hover(&curr_sheet->aois,
445        ex + curr_sheet->xmin, ey + curr_sheet->ymin);
446}
447
448
449static bool sheet_drag_begin(void *user, int x, int y)
450{
451    dehover_glabel(user);
452    return 1;
453}
454
455
456static void sheet_drag_move(void *user, int dx, int dy)
457{
458    struct gui_ctx *ctx = user;
459
460    ctx->x -= dx / ctx->scale;
461    ctx->y -= dy / ctx->scale;
462    redraw(ctx);
463}
464
465
466static void sheet_drag_end(void *user)
467{
468    input_update();
469}
470
471
472static void sheet_scroll(void *user, int x, int y, int dy)
473{
474    struct gui_ctx *ctx = user;
475    int ex, ey;
476
477
478    canvas_coord(ctx, x, y, &ex, &ey);
479    if (dy < 0) {
480        if (!zoom_in(ctx, ex, ey))
481            return;
482    } else {
483        if (!zoom_out(ctx, ex, ey))
484            return;
485    }
486    dehover_glabel(ctx);
487    input_update();
488}
489
490
491static void sheet_key(void *user, int x, int y, int keyval)
492{
493    struct gui_ctx *ctx = user;
494    struct gui_sheet *sheet = ctx->curr_sheet;
495    int ex, ey;
496
497    canvas_coord(ctx, x, y, &ex, &ey);
498
499    switch (keyval) {
500    case '+':
501    case '=':
502        zoom_in(ctx, x, y);
503        break;
504    case '-':
505        zoom_out(ctx, x, y);
506        break;
507    case '*':
508        zoom_to_extents(ctx);
509        break;
510
511    case GDK_KEY_Home:
512    case GDK_KEY_KP_Home:
513        if (sheet != ctx->new_hist->sheets)
514            go_to_sheet(ctx, ctx->new_hist->sheets);
515        break;
516    case GDK_KEY_BackSpace:
517    case GDK_KEY_Delete:
518    case GDK_KEY_KP_Delete:
519        go_up_sheet(ctx);
520        break;
521    case GDK_KEY_Page_Up:
522    case GDK_KEY_KP_Page_Up:
523        go_prev_sheet(ctx);
524        break;
525    case GDK_KEY_Page_Down:
526    case GDK_KEY_KP_Page_Down:
527        go_next_sheet(ctx);
528        break;
529    case GDK_KEY_Up:
530    case GDK_KEY_KP_Up:
531        show_history(ctx, sel_new);
532        break;
533    case GDK_KEY_Down:
534    case GDK_KEY_KP_Down:
535        show_history(ctx, sel_old);
536        break;
537    case GDK_KEY_Tab:
538    case GDK_KEY_KP_Tab:
539        toggle_old_new(ctx);
540        break;
541
542    case GDK_KEY_Escape:
543        dehover_glabel(user);
544        ctx->glabel = NULL;
545        redraw(ctx);
546        break;
547
548    case GDK_KEY_a:
549        use_delta = !use_delta;
550        do_revision_overlays(ctx);
551        redraw(ctx);
552        break;
553
554    case GDK_KEY_n:
555        ctx->diff_mode = diff_new;
556        redraw(ctx);
557        break;
558    case GDK_KEY_o:
559        ctx->diff_mode = diff_old;
560        redraw(ctx);
561        break;
562    case GDK_KEY_d:
563        ctx->diff_mode = diff_delta;
564        redraw(ctx);
565        break;
566
567    case GDK_KEY_h:
568        help();
569        break;
570
571    case GDK_KEY_q:
572        gtk_main_quit();
573    }
574}
575
576
577static const struct input_ops sheet_input_ops = {
578    .click = sheet_click,
579    .hover_begin = sheet_hover_update,
580    .hover_update = sheet_hover_update,
581    .hover_click = sheet_click,
582    .scroll = sheet_scroll,
583    .drag_begin = sheet_drag_begin,
584    .drag_move = sheet_drag_move,
585    .drag_end = sheet_drag_end,
586    .key = sheet_key,
587};
588
589
590/* ----- Event handlers ---------------------------------------------------- */
591
592
593static void size_allocate_event(GtkWidget *widget, GdkRectangle *allocation,
594    gpointer data)
595{
596    struct gui_ctx *ctx = data;
597
598    zoom_to_extents(ctx);
599}
600
601
602/* ----- Initialization ---------------------------------------------------- */
603
604
605void sheet_setup(struct gui_ctx *ctx)
606{
607    g_signal_connect(G_OBJECT(ctx->da), "size_allocate",
608        G_CALLBACK(size_allocate_event), ctx);
609    input_push(&sheet_input_ops, ctx);
610}
611

Archive Download this file

Branches:
master



interactive