Root/
| 1 | /* |
| 2 | * gui/render.c - Render schematics and GUI elements |
| 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 <string.h> |
| 16 | |
| 17 | #include <cairo/cairo.h> |
| 18 | #include <gtk/gtk.h> |
| 19 | |
| 20 | #include "misc/util.h" |
| 21 | #include "gfx/style.h" |
| 22 | #include "gfx/cro.h" |
| 23 | #include "gfx/gfx.h" |
| 24 | #include "kicad/pl.h" |
| 25 | #include "kicad/sch.h" |
| 26 | #include "kicad/delta.h" |
| 27 | #include "gfx/diff.h" |
| 28 | #include "gfx/diff.h" |
| 29 | #include "gui/aoi.h" |
| 30 | #include "gui/over.h" |
| 31 | #include "gui/common.h" |
| 32 | |
| 33 | |
| 34 | #define VCS_OVERLAYS_X 5 |
| 35 | #define VCS_OVERLAYS_Y 5 |
| 36 | |
| 37 | #define SHEET_OVERLAYS_X -10 |
| 38 | #define SHEET_OVERLAYS_Y 10 |
| 39 | |
| 40 | #define GLABEL_HIGHLIGHT_PAD 6 |
| 41 | |
| 42 | |
| 43 | bool use_delta = 0; |
| 44 | |
| 45 | |
| 46 | /* ----- Helper functions -------------------------------------------------- */ |
| 47 | |
| 48 | |
| 49 | void redraw(const struct gui_ctx *ctx) |
| 50 | { |
| 51 | gtk_widget_queue_draw(ctx->da); |
| 52 | } |
| 53 | |
| 54 | |
| 55 | /* ----- Highlight glabel -------------------------------------------------- */ |
| 56 | |
| 57 | |
| 58 | /* |
| 59 | * cd, cx, cy are simplified versions of what cro.c uses. Since we don't |
| 60 | * support glabel highlighting in diff mode, we don't need the xe and ye offset |
| 61 | * components. |
| 62 | */ |
| 63 | |
| 64 | static inline int cd(int x, float scale) |
| 65 | { |
| 66 | return x * scale; |
| 67 | } |
| 68 | |
| 69 | |
| 70 | static inline int cx(int x, int xo, float scale) |
| 71 | { |
| 72 | return xo + x * scale; |
| 73 | } |
| 74 | |
| 75 | |
| 76 | static inline int cy(int y, int yo, float scale) |
| 77 | { |
| 78 | return yo + y * scale; |
| 79 | } |
| 80 | |
| 81 | |
| 82 | static void highlight_glabel(const struct gui_ctx *ctx, |
| 83 | const struct gui_sheet *sheet, cairo_t *cr, int xo, int yo, float f) |
| 84 | { |
| 85 | const struct sch_obj *obj; |
| 86 | |
| 87 | if (!ctx->glabel) |
| 88 | return; |
| 89 | |
| 90 | cairo_set_source_rgb(cr, 1, 0.8, 1); |
| 91 | for (obj = sheet->sch->objs; obj; obj = obj->next) { |
| 92 | const struct dwg_bbox *bbox = &obj->u.text.bbox; |
| 93 | |
| 94 | if (obj->type != sch_obj_glabel) |
| 95 | continue; |
| 96 | if (strcmp(obj->u.text.s, ctx->glabel)) |
| 97 | continue; |
| 98 | |
| 99 | cairo_rectangle(cr, |
| 100 | cx(bbox->x, xo, f) - GLABEL_HIGHLIGHT_PAD, |
| 101 | cy(bbox->y, yo, f) - GLABEL_HIGHLIGHT_PAD, |
| 102 | cd(bbox->w, f) + 2 * GLABEL_HIGHLIGHT_PAD, |
| 103 | cd(bbox->h, f) + 2 * GLABEL_HIGHLIGHT_PAD); |
| 104 | cairo_fill(cr); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | |
| 109 | /* ----- Draw to screen ---------------------------------------------------- */ |
| 110 | |
| 111 | |
| 112 | /* |
| 113 | * @@@ the highlighting of sub-sheets possibly containing changes is very |
| 114 | * unreliable since sheet_eq (from delta) responds to a lot of purely |
| 115 | * imaginary changes. However, this will be a good way to exercise and improve |
| 116 | * delta. |
| 117 | */ |
| 118 | |
| 119 | static struct area *changed_sheets(const struct gui_ctx *ctx, |
| 120 | int xo, int yo, float f) |
| 121 | { |
| 122 | const struct gui_sheet *new = ctx->curr_sheet; |
| 123 | const struct sch_obj *obj; |
| 124 | struct area *areas = NULL; |
| 125 | |
| 126 | for (obj = new->sch->objs; obj; obj = obj->next) { |
| 127 | const struct gui_sheet *new_sub; |
| 128 | const struct gui_sheet *old_sub; |
| 129 | |
| 130 | if (obj->type != sch_obj_sheet) |
| 131 | continue; |
| 132 | if (!obj->u.sheet.sheet) |
| 133 | continue; |
| 134 | |
| 135 | for (new_sub = ctx->new_hist->sheets; |
| 136 | new_sub && new_sub->sch != obj->u.sheet.sheet; |
| 137 | new_sub = new_sub->next); |
| 138 | if (!new_sub) |
| 139 | continue; |
| 140 | old_sub = find_corresponding_sheet(ctx->old_hist->sheets, |
| 141 | ctx->new_hist->sheets, new_sub); |
| 142 | |
| 143 | if (!sheet_eq(new_sub->sch, old_sub->sch)) |
| 144 | add_area(&areas, cx(obj->x, xo, f), cy(obj->y, yo, f), |
| 145 | cx(obj->x + obj->u.sheet.w, xo, f), |
| 146 | cy(obj->y + obj->u.sheet.h, yo, f), 0xffff00); |
| 147 | } |
| 148 | return areas; |
| 149 | } |
| 150 | |
| 151 | |
| 152 | static void hack(const struct gui_ctx *ctx, cairo_t *cr, |
| 153 | int xo, int yo, float f) |
| 154 | { |
| 155 | const struct gui_sheet *new = ctx->curr_sheet; |
| 156 | const struct gui_sheet *old = find_corresponding_sheet( |
| 157 | ctx->old_hist->sheets, ctx->new_hist->sheets, ctx->curr_sheet); |
| 158 | struct area *areas = NULL; |
| 159 | |
| 160 | areas = changed_sheets(ctx, xo, yo, f); |
| 161 | diff_to_canvas(cr, ctx->x, ctx->y, ctx->scale, |
| 162 | old->gfx_ctx, new->gfx_ctx, areas); |
| 163 | free_areas(&areas); |
| 164 | } |
| 165 | |
| 166 | |
| 167 | static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, |
| 168 | gpointer user_data) |
| 169 | { |
| 170 | const struct gui_ctx *ctx = user_data; |
| 171 | const struct gui_sheet *sheet = ctx->curr_sheet; |
| 172 | GtkAllocation alloc; |
| 173 | float f = ctx->scale; |
| 174 | int x, y; |
| 175 | |
| 176 | gtk_widget_get_allocation(ctx->da, &alloc); |
| 177 | x = -(sheet->xmin + ctx->x) * f + alloc.width / 2; |
| 178 | y = -(sheet->ymin + ctx->y) * f + alloc.height / 2; |
| 179 | |
| 180 | cro_canvas_prepare(cr); |
| 181 | if (!ctx->old_hist || ctx->diff_mode == diff_new) { |
| 182 | highlight_glabel(ctx, sheet, cr, x, y, f); |
| 183 | cro_canvas_draw(sheet->gfx_ctx, cr, x, y, f); |
| 184 | } else if (ctx->diff_mode == diff_old) { |
| 185 | sheet = find_corresponding_sheet(ctx->old_hist->sheets, |
| 186 | ctx->new_hist->sheets, ctx->curr_sheet); |
| 187 | highlight_glabel(ctx, sheet, cr, x, y, f); |
| 188 | cro_canvas_draw(sheet->gfx_ctx, cr, x, y, f); |
| 189 | } else if (use_delta) { |
| 190 | struct area *areas = changed_sheets(ctx, x, y, f); |
| 191 | const struct area *area; |
| 192 | |
| 193 | cairo_set_source_rgb(cr, 1, 1, 0); |
| 194 | for (area = areas; area; area = area->next) { |
| 195 | cairo_rectangle(cr, area->xa, area->ya, |
| 196 | area->xb - area->xa, area->yb - area->ya); |
| 197 | cairo_fill(cr); |
| 198 | } |
| 199 | free_areas(&areas); |
| 200 | |
| 201 | /* @@@ fix geometry later */ |
| 202 | cro_canvas_draw(ctx->delta_ab.gfx_ctx, cr, x, y, f); |
| 203 | cro_canvas_draw(ctx->delta_a.gfx_ctx, cr, x, y, f); |
| 204 | cro_canvas_draw(ctx->delta_b.gfx_ctx, cr, x, y, f); |
| 205 | } else { |
| 206 | hack(ctx, cr, x, y, f); |
| 207 | } |
| 208 | |
| 209 | overlay_draw_all(ctx->sheet_overlays, cr, |
| 210 | SHEET_OVERLAYS_X, SHEET_OVERLAYS_Y); |
| 211 | overlay_draw_all_d(ctx->hist_overlays, cr, |
| 212 | VCS_OVERLAYS_X, |
| 213 | VCS_OVERLAYS_Y + (ctx->showing_history ? ctx->hist_y_offset : 0), |
| 214 | 0, 1); |
| 215 | overlay_draw_all_d(ctx->pop_underlays, cr, ctx->pop_x, ctx->pop_y, |
| 216 | ctx->pop_dx, ctx->pop_dy); |
| 217 | overlay_draw_all_d(ctx->pop_overlays, cr, |
| 218 | ctx->pop_x + ctx->pop_dx * GLABEL_STACK_PADDING, |
| 219 | ctx->pop_y + ctx->pop_dy * GLABEL_STACK_PADDING, |
| 220 | ctx->pop_dx, ctx->pop_dy); |
| 221 | |
| 222 | return FALSE; |
| 223 | } |
| 224 | |
| 225 | |
| 226 | /* ----- Pre-rendering ----------------------------------------------------- */ |
| 227 | |
| 228 | |
| 229 | void render_sheet(struct gui_sheet *sheet) |
| 230 | { |
| 231 | char *argv[] = { "gui", NULL }; |
| 232 | |
| 233 | gfx_init(&cro_canvas_ops, 1, argv); |
| 234 | if (sheet->hist && sheet->hist->pl) /* @@@ no pl_render for delta */ |
| 235 | pl_render(sheet->hist->pl, sheet->hist->sch_ctx.sheets, |
| 236 | sheet->sch); |
| 237 | sch_render(sheet->sch); |
| 238 | cro_canvas_end(gfx_ctx, |
| 239 | &sheet->w, &sheet->h, &sheet->xmin, &sheet->ymin); |
| 240 | sheet->gfx_ctx = gfx_ctx; |
| 241 | sheet->rendered = 1; |
| 242 | // gfx_end(); |
| 243 | } |
| 244 | |
| 245 | |
| 246 | void render_delta(struct gui_ctx *ctx) |
| 247 | { |
| 248 | #if 1 |
| 249 | /* @@@ needs updating for curr/last vs. new/old */ |
| 250 | struct sheet *sch_a, *sch_b, *sch_ab; |
| 251 | struct gui_sheet *a = ctx->curr_sheet; |
| 252 | struct gui_sheet *b = find_corresponding_sheet( |
| 253 | ctx->old_hist->sheets, ctx->new_hist->sheets, ctx->curr_sheet); |
| 254 | |
| 255 | sch_a = alloc_type(struct sheet); |
| 256 | sch_b = alloc_type(struct sheet); |
| 257 | sch_ab = alloc_type(struct sheet); |
| 258 | |
| 259 | delta(a->sch, b->sch, sch_a, sch_b, sch_ab); |
| 260 | ctx->delta_a.sch = sch_a, |
| 261 | ctx->delta_b.sch = sch_b, |
| 262 | ctx->delta_ab.sch = sch_ab, |
| 263 | |
| 264 | ctx->delta_a.ctx = ctx->delta_b.ctx = ctx->delta_ab.ctx = NULL; |
| 265 | ctx->delta_a.hist = ctx->delta_b.hist = ctx->delta_ab.hist = NULL; |
| 266 | |
| 267 | render_sheet(&ctx->delta_a); |
| 268 | render_sheet(&ctx->delta_b); |
| 269 | render_sheet(&ctx->delta_ab); |
| 270 | |
| 271 | cro_color_override(ctx->delta_ab.gfx_ctx, COLOR_LIGHT_GREY); |
| 272 | cro_color_override(ctx->delta_b.gfx_ctx, COLOR_RED); |
| 273 | cro_color_override(ctx->delta_a.gfx_ctx, COLOR_GREEN2); |
| 274 | |
| 275 | // @@@ clean up when leaving sheet |
| 276 | #endif |
| 277 | |
| 278 | if (!b->rendered) { |
| 279 | render_sheet(b); |
| 280 | mark_aois(ctx, b); |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | |
| 285 | /* ----- Setup ------------------------------------------------------------- */ |
| 286 | |
| 287 | |
| 288 | void render_setup(struct gui_ctx *ctx) |
| 289 | { |
| 290 | g_signal_connect(G_OBJECT(ctx->da), "draw", |
| 291 | G_CALLBACK(on_draw_event), ctx); |
| 292 | } |
| 293 |
Branches:
master
