gerbv  2.10.1-dev~93f1b5
callbacks.c
Go to the documentation of this file.
1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This file is a part of gerbv.
4  *
5  * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *
7  * $Id$
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
29 #include "gerbv.h"
30 #include "drill.h"
31 
32 #if !defined(WIN32) && !defined(QUARTZ)
33 #include <gdk/gdkx.h>
34 #endif
35 
36 #ifdef HAVE_STDLIB_H
37 #include <stdlib.h>
38 #endif
39 
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 
44 #ifdef HAVE_TIME_H
45 #include <time.h>
46 #endif
47 
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 
52 #include <math.h>
53 #include "common.h"
54 #include "main.h"
55 
56 #include "attribute.h"
57 #include "callbacks.h"
58 #include "interface.h"
59 #include "project.h"
60 #include "render.h"
61 #include "selection.h"
62 #include "table.h"
63 
64 #include "draw-gdk.h"
65 
66 #include "draw.h"
67 #ifdef WIN32
68 #include <cairo-win32.h>
69 #elif QUARTZ
70 #include <cairo-quartz.h>
71 #else
72 #include <cairo-xlib.h>
73 #endif
74 
75 #define dprintf \
76  if (DEBUG) \
77  printf
78 
79 /* This default extension should really not be changed, but if it absolutely
80  * must change, the ../win32/gerbv.nsi.in *must* be changed to reflect that.
81  * Just grep for the extension (gvp) and change it in two places in that file.
82  */
83 #define GERBV_PROJECT_FILE_EXT ".gvp"
84 const char* gerbv_project_file_name = N_("Gerbv Project");
85 const char* gerbv_project_file_pat = "*" GERBV_PROJECT_FILE_EXT;
86 
87 static gint callbacks_get_selected_row_index(void);
88 static void callbacks_units_changed(gerbv_gui_unit_t unit);
89 static void callbacks_update_statusbar_coordinates(gint x, gint y);
90 static void callbacks_update_ruler_scales(void);
91 static void callbacks_render_type_changed(void);
92 static void show_no_layers_warning(void);
93 
94 static double screen_units(double);
95 static const char* screen_units_str(void);
96 
97 static double line_length(double, double, double, double);
98 static double arc_length(double, double);
99 
100 static void aperture_state_report(gerbv_net_t*, gerbv_image_t*, gerbv_project_t*);
101 static void aperture_report(gerbv_aperture_t*[], int, double, double, gerbv_image_t*, gerbv_project_t*);
102 static void drill_report(gerbv_aperture_t*[], int);
103 static void parea_report(gerbv_net_t*, gerbv_image_t*, gerbv_project_t*);
104 static void net_layer_file_report(gerbv_net_t*, gerbv_image_t*, gerbv_project_t*);
105 static void analyze_window_size_restore(GtkWidget*);
106 static void analyze_window_size_store(GtkWidget*, gpointer);
107 
108 static void update_selected_object_message(gboolean userTriedToSelect);
109 
110 gchar*
111 utf8_strncpy(gchar* dst, const gchar* src, gsize byte_len) {
112  /* -1 for '\0' in buffer */
113  glong char_len = g_utf8_strlen(src, byte_len - 1);
114  return g_utf8_strncpy(dst, src, char_len);
115 }
116 
117 void
118 utf8_snprintf(gchar* dst, gsize byte_len, const gchar* fmt, ...) {
119  va_list ap;
120 
121  va_start(ap, fmt);
122  gchar* str = g_strdup_vprintf(fmt, ap);
123  va_end(ap);
124  utf8_strncpy(dst, str, byte_len);
125  g_free(str);
126 }
127 
128 /* --------------------------------------------------------- */
129 
130 static void
131 show_no_layers_warning(void) {
132  gchar* str = g_new(gchar, MAX_DISTLEN);
133  utf8_strncpy(str, _("No layers are currently loaded. A layer must be loaded first."), MAX_DISTLEN - 7);
134  utf8_snprintf(screen.statusbar.diststr, MAX_DISTLEN, "<b>%s</b>", str);
135  g_free(str);
136 
138 }
139 
140 /* --------------------------------------------------------- */
146 void
147 callbacks_new_project_activate(GtkMenuItem* menuitem, gpointer user_data) {
148  if (mainProject->last_loaded >= 0) {
150  _("Do you want to close any open layers "
151  "and start a new project?"),
152  _("Starting a new project will cause all currently "
153  "open layers to be closed. Any unsaved changes "
154  "will be lost."),
155  FALSE, NULL, GTK_STOCK_CLOSE, GTK_STOCK_CANCEL
156  ))
157  return;
158  }
159  /* Unload all layers and then clear layer window */
160  gerbv_unload_all_layers(mainProject);
161  callbacks_update_layer_tree();
162  selection_clear(&screen.selectionInfo);
163  update_selected_object_message(FALSE);
164 
165  /* Destroy project info */
166  if (mainProject->project) {
167  g_free(mainProject->project);
168  mainProject->project = NULL;
169  }
170  render_refresh_rendered_image_on_screen();
171 }
172 
173 /* --------------------------------------------------------- */
179 void
180 open_project(char* project_filename) {
181 
182  /* TODO: check if layers is modified and show it to user. */
183 
184  if (mainProject->last_loaded >= 0
186  _("Do you want to close any open layers and load "
187  "an existing project?"),
188  _("Loading a project will cause all currently open "
189  "layers to be closed. Any unsaved changes "
190  "will be lost."),
191  FALSE, NULL, GTK_STOCK_CLOSE, GTK_STOCK_CANCEL
192  )) {
193 
194  return;
195  }
196 
197  /* Update the last folder */
198  g_free(mainProject->path);
199  mainProject->path = g_strdup(project_filename);
200 
201  gerbv_unload_all_layers(mainProject);
202  main_open_project_from_filename(mainProject, project_filename);
203 }
204 
205 /* --------------------------------------------------------- */
213 void
214 open_files(GSList* filenames) {
215  GSList* fns = NULL; /* File names to ask */
216  GSList* fns_is_mod = NULL; /* File name layer is modified */
217  GSList* fns_cnt = NULL; /* File names count */
218  GSList* fns_lay_num = NULL; /* Layer number for fns */
219  GSList* cnt = NULL; /* File names count unsorted by layers,
220  0 -- file not yet loaded as layer */
221  gint answer;
222 
223  if (filenames == NULL)
224  return;
225 
226  /* Check if there is a Gerbv project in the list.
227  * If there is least open only that and ignore the rest. */
228  for (GSList* fn = filenames; fn; fn = fn->next) {
229  gboolean is_project = FALSE;
230  if (0 == project_is_gerbv_project(fn->data, &is_project) && is_project) {
231  open_project(fn->data);
232 
233  gerbv_render_zoom_to_fit_display(mainProject, &screenRenderInfo);
234  render_refresh_rendered_image_on_screen();
235  callbacks_update_layer_tree();
236 
237  return;
238  }
239  }
240 
241  /* Count opened filenames and place result in list */
242  for (GSList* fn = filenames; fn; fn = fn->next) {
243  gint c = 0;
244 
245  for (gint fidx = 0; fidx <= mainProject->last_loaded; ++fidx) {
246  gchar* fpn = mainProject->file[fidx]->fullPathname;
247 
248  if (strlen(fpn) == strlen(fn->data) && 0 == g_ascii_strncasecmp(fpn, fn->data, strlen(fn->data))) {
249  c++;
250  }
251  }
252 
253  cnt = g_slist_append(cnt, GINT_TO_POINTER(c));
254  }
255 
256  /* Make fns, fns_is_mod and fns_cnt lists sorted by layers */
257  for (gint fidx = 0; fidx <= mainProject->last_loaded; ++fidx) {
258  gchar* fpn = mainProject->file[fidx]->fullPathname;
259 
260  for (GSList* fn = filenames; fn; fn = fn->next) {
261  if (strlen(fpn) == strlen(fn->data) && 0 == g_ascii_strncasecmp(fpn, fn->data, strlen(fn->data))) {
262  fns = g_slist_append(fns, fn->data);
263  fns_is_mod = g_slist_append(fns_is_mod, GINT_TO_POINTER(mainProject->file[fidx]->layer_dirty));
264  fns_cnt = g_slist_append(fns_cnt, g_slist_nth_data(cnt, g_slist_position(filenames, fn)));
265  fns_lay_num = g_slist_append(fns_lay_num, GINT_TO_POINTER(fidx));
266 
267  break;
268  }
269  }
270  }
271 
272  answer = GTK_RESPONSE_NONE;
273  if (g_slist_length(fns) > 0)
274  answer = interface_reopen_question(fns, fns_is_mod, fns_cnt, fns_lay_num);
275 
276  switch (answer) {
277 
278  case GTK_RESPONSE_CANCEL:
279  case GTK_RESPONSE_NONE:
280  case GTK_RESPONSE_DELETE_EVENT:
281  /* Dialog is closed or Esc is pressed, skip all */
282  break;
283 
284  case GTK_RESPONSE_YES: /* Reload layer was selected */
285  for (GSList* fn = fns; fn; fn = fn->next) {
286  if (fn->data != NULL)
287  gerbv_revert_file(
288  mainProject, GPOINTER_TO_INT(g_slist_nth_data(fns_lay_num, g_slist_position(fns, fn)))
289  );
290  }
291  break;
292 
293  case GTK_RESPONSE_OK: /* Open as a new layer was selected */
294  /* To open as new only _one_ instance of file, check filenames
295  * by selected files in fns */
296  for (GSList* fn = filenames; fn; fn = fn->next) {
297  if (NULL != g_slist_find(fns, fn->data))
299  }
300  break;
301  }
302 
303  /* Add not loaded files (cnt == 0) in the end */
304  for (GSList* fn = filenames; fn; fn = fn->next) {
305  if (0 == GPOINTER_TO_INT(g_slist_nth_data(cnt, g_slist_position(filenames, fn))))
307  }
308 
309  g_slist_free(fns);
310  g_slist_free(fns_is_mod);
311  g_slist_free(fns_cnt);
312  g_slist_free(fns_lay_num);
313  g_slist_free(cnt);
314 
315  gerbv_render_zoom_to_fit_display(mainProject, &screenRenderInfo);
316  render_refresh_rendered_image_on_screen();
317  callbacks_update_layer_tree();
318 }
319 
320 /* --------------------------------------------------------- */
326 void
327 callbacks_open_activate(GtkMenuItem* menuitem, gpointer user_data) {
328  GSList* fns = NULL;
329  screen.win.gerber = gtk_file_chooser_dialog_new(
330  _("Open Gerbv project, Gerber, drill, "
331  "or pick&place files"),
332  NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
333  NULL
334  );
335 
336  gtk_file_chooser_set_select_multiple((GtkFileChooser*)screen.win.gerber, TRUE);
337  gtk_file_chooser_set_current_folder((GtkFileChooser*)screen.win.gerber, mainProject->path);
338  gtk_widget_show(screen.win.gerber);
339  if (gtk_dialog_run((GtkDialog*)screen.win.gerber) == GTK_RESPONSE_ACCEPT) {
340  fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(screen.win.gerber));
341  /* Update the last folder */
342  g_free(mainProject->path);
343  mainProject->path = gtk_file_chooser_get_current_folder((GtkFileChooser*)screen.win.gerber);
344  }
345  gtk_widget_destroy(screen.win.gerber);
346 
347  open_files(fns);
348  g_slist_free_full(fns, g_free);
349 }
350 
351 /* --------------------------------------------------------- */
352 void
353 callbacks_revert_activate(GtkMenuItem* menuitem, gpointer user_data) {
354  gerbv_revert_all_files(mainProject);
355  selection_clear(&screen.selectionInfo);
356  update_selected_object_message(FALSE);
357  render_refresh_rendered_image_on_screen();
358  callbacks_update_layer_tree();
359 }
360 
361 /* --------------------------------------------------------- */
362 void
363 callbacks_save_project_activate(GtkMenuItem* menuitem, gpointer user_data) {
364  if (mainProject->project)
365  main_save_project_from_filename(mainProject, mainProject->project);
366  else
367  callbacks_generic_save_activate(menuitem, (gpointer)CALLBACKS_SAVE_PROJECT_AS);
368  callbacks_update_layer_tree();
369  return;
370 }
371 
372 /* --------------------------------------------------------- */
373 void
374 callbacks_save_layer_activate(GtkMenuItem* menuitem, gpointer user_data) {
375  /* first figure out which layer in the layer side menu is selected */
376  gint index = callbacks_get_selected_row_index();
377 
378  /* Now save that layer */
379  if (index >= 0) {
380  if (!gerbv_save_layer_from_index(mainProject, index, mainProject->file[index]->fullPathname)) {
381  interface_show_alert_dialog(_("Gerbv cannot export this file type"), NULL, FALSE, NULL);
382  mainProject->file[index]->layer_dirty = FALSE;
383  callbacks_update_layer_tree();
384  return;
385  }
386  }
387  callbacks_update_layer_tree();
388  return;
389 }
390 
391 struct l_image_info {
392  gerbv_image_t* image;
393  gerbv_user_transformation_t* transform;
394 };
395 
396 /* --------------------------------------------------------- */
401 merge_images(int type) {
402  gint i, filecount, img;
403  gerbv_image_t* out;
404  gerbv_layertype_t layertype;
405 
406  struct l_image_info {
407  gerbv_image_t* image;
408  gerbv_user_transformation_t* transform;
409  } * images;
410 
411  images = (struct l_image_info*)g_new0(struct l_image_info, 1);
412  out = NULL;
413  switch (type) {
414  case CALLBACKS_SAVE_FILE_DRILLM: layertype = GERBV_LAYERTYPE_DRILL; break;
415  case CALLBACKS_SAVE_FILE_RS274XM: layertype = GERBV_LAYERTYPE_RS274X; break;
416  default: GERB_COMPILE_ERROR(_("Unknown Layer type for merge")); goto err;
417  }
418  dprintf("Looking for matching files\n");
419  for (i = img = filecount = 0; i < mainProject->max_files; ++i) {
420  if (mainProject->file[i] && mainProject->file[i]->isVisible
421  && (mainProject->file[i]->image->layertype == layertype)) {
422  ++filecount;
423  dprintf("Adding '%s'\n", mainProject->file[i]->name);
424  images[img].image = mainProject->file[i]->image;
425  images[img++].transform = &mainProject->file[i]->transform;
426  images = (struct l_image_info*)g_renew(struct l_image_info, images, img + 1);
427  }
428  }
429  if (filecount < 2) {
430  GERB_COMPILE_ERROR(_("Not Enough Files of same type to merge"));
431  goto err;
432  }
433  dprintf("Now merging files\n");
434  for (i = 0; i < img; ++i) {
435  gerbv_user_transformation_t* thisTransform;
436  gerbv_user_transformation_t identityTransform = { 0, 0, 1, 1, 0, FALSE, FALSE, FALSE };
437  thisTransform = images[i].transform;
438  if (NULL == thisTransform)
439  thisTransform = &identityTransform;
440  if (0 == i)
441  out = gerbv_image_duplicate_image(images[i].image, thisTransform);
442  else
443  gerbv_image_copy_image(images[i].image, thisTransform, out);
444  }
445 err:
446  g_free(images);
447  return out;
448 }
449 
450 /* --------------------------------------------------------- */
451 int
452 visible_file_name(
453  gchar** file_name, gchar** dir_name, gerbv_layertype_t layer_type, /* -1 for all types */
454  const gchar* file_extension, const gchar* untitled_file_extension
455 ) {
456  unsigned int count = 0;
457  gerbv_fileinfo_t* first_vis_file = NULL;
458 
459  for (int i = 0; i < mainProject->max_files; ++i) {
460  if (mainProject->file[i] && mainProject->file[i]->isVisible
461  && (layer_type == (gerbv_layertype_t)-1 || layer_type == mainProject->file[i]->image->layertype)) {
462  if (first_vis_file == NULL) {
463  first_vis_file = mainProject->file[i];
464  /* Always directory of first visible file */
465  if (dir_name)
466  *dir_name = g_path_get_dirname(first_vis_file->fullPathname);
467  }
468 
469  if (++count == 2 && file_name) {
470  *file_name = g_strdup_printf("%s%s", pgettext("file name", "untitled"), untitled_file_extension);
471  }
472  }
473  }
474 
475  if (count == 1 && file_name)
476  *file_name = g_strdup_printf("%s%s", first_vis_file->name, file_extension);
477 
478  return count;
479 }
480 
481 /* --------------------------------------------------------- */
482 void
483 callbacks_generic_save_activate(GtkMenuItem* menuitem, gpointer user_data) {
484  gchar* new_file_name = NULL;
485  gchar* file_name = NULL;
486  gchar* dir_name = NULL;
487  gboolean error_visible_layers = FALSE;
488  gchar* windowTitle = NULL;
489  gerbv_fileinfo_t* act_file;
490  gint file_index;
491  gint processType = GPOINTER_TO_INT(user_data);
492  GtkFileFilter* filter;
493  GtkSpinButton* spin_but;
494  GtkTooltips* tooltips;
495  GtkWidget* label;
496  GtkWidget* hbox;
497  static gint dpi = 0;
498 
499  file_index = callbacks_get_selected_row_index();
500  if (file_index < 0) {
502  _("No layer is currently active"), _("Please select a layer and try again."), FALSE, NULL
503  );
504  return;
505  }
506 
507  act_file = mainProject->file[file_index];
508 
509  screen.win.gerber = gtk_file_chooser_dialog_new("", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL, NULL);
510  GtkFileChooser* file_chooser_p = GTK_FILE_CHOOSER(screen.win.gerber);
511  gtk_file_chooser_set_do_overwrite_confirmation(file_chooser_p, TRUE);
512 
513  hbox = gtk_hbox_new(0, 0);
514  spin_but = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(0, 0, 1));
515  label = gtk_label_new("");
516  tooltips = gtk_tooltips_new();
517  gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(spin_but), 0, 0, 1);
518  gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 5);
519  gtk_box_pack_end(GTK_BOX(GTK_DIALOG(screen.win.gerber)->vbox), hbox, 0, 0, 2);
520 
521  switch (processType) {
522  case CALLBACKS_SAVE_PROJECT_AS:
523  windowTitle = g_strdup(_("Save project as..."));
524  if (mainProject->project) {
525  file_name = g_path_get_basename(mainProject->project);
526 
527  dir_name = g_path_get_dirname(mainProject->project);
528  } else {
529  file_name = g_strdup_printf("%s%s", pgettext("file name", "untitled"), GERBV_PROJECT_FILE_EXT);
530  dir_name = g_path_get_dirname(act_file->fullPathname);
531  }
532 
533  filter = gtk_file_filter_new();
534  gtk_file_filter_set_name(filter, _(gerbv_project_file_name));
535  gtk_file_filter_add_pattern(filter, gerbv_project_file_pat);
536  gtk_file_chooser_add_filter(file_chooser_p, filter);
537 
538  filter = gtk_file_filter_new();
539  gtk_file_filter_set_name(filter, _("All"));
540  gtk_file_filter_add_pattern(filter, "*");
541  gtk_file_chooser_add_filter(file_chooser_p, filter);
542 
543  break;
544  case CALLBACKS_SAVE_FILE_PS:
545  windowTitle = g_strdup_printf(_("Export visible layers to %s file as..."), _("PS"));
546  if (0 == visible_file_name(&file_name, &dir_name, -1, ".ps", ".ps")) {
547  error_visible_layers = TRUE;
548  break;
549  }
550 
551  break;
552  case CALLBACKS_SAVE_FILE_PDF:
553  windowTitle = g_strdup_printf(_("Export visible layers to %s file as..."), _("PDF"));
554  if (0 == visible_file_name(&file_name, &dir_name, -1, ".pdf", ".pdf")) {
555  error_visible_layers = TRUE;
556  break;
557  }
558 
559  break;
560  case CALLBACKS_SAVE_FILE_SVG:
561  windowTitle = g_strdup_printf(_("Export visible layers to %s file as..."), _("SVG"));
562  if (0 == visible_file_name(&file_name, &dir_name, -1, ".svg", ".svg")) {
563  error_visible_layers = TRUE;
564  break;
565  }
566 
567  break;
568  case CALLBACKS_SAVE_FILE_DXF:
569 #if HAVE_LIBDXFLIB
570  windowTitle =
571  g_strdup_printf(_("Export \"%s\" layer #%d to DXF file as..."), act_file->name, file_index + 1);
572  file_name = g_strconcat(act_file->name, ".dxf", NULL);
573  dir_name = g_path_get_dirname(act_file->fullPathname);
574 #endif
575  break;
576  case CALLBACKS_SAVE_FILE_PNG:
577  windowTitle = g_strdup_printf(_("Export visible layers to %s file as..."), _("PNG"));
578  if (0 == visible_file_name(&file_name, &dir_name, -1, ".png", ".png")) {
579  error_visible_layers = TRUE;
580  break;
581  }
582 
583  gtk_label_set_text(GTK_LABEL(label), _("DPI:"));
584  gtk_spin_button_set_range(spin_but, 0, 6000);
585  gtk_spin_button_set_increments(spin_but, 10, 100);
586  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(label), _("DPI value, autoscaling if 0"), NULL);
587  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(spin_but), _("DPI value, autoscaling if 0"), NULL);
588  gtk_spin_button_set_value(spin_but, dpi);
589  gtk_widget_show_all(hbox);
590 
591  break;
592  case CALLBACKS_SAVE_FILE_RS274X:
593  windowTitle = g_strdup_printf(
594  _("Export \"%s\" layer #%d to "
595  "RS-274X file as..."),
596  act_file->name, file_index + 1
597  );
598 
599  if (GERBV_LAYERTYPE_RS274X != act_file->image->layertype)
600  file_name = g_strconcat(act_file->name, ".gbr", NULL);
601  else
602  file_name = g_strdup(act_file->name);
603 
604  dir_name = g_path_get_dirname(act_file->fullPathname);
605  break;
606  case CALLBACKS_SAVE_FILE_RS274XM:
607  windowTitle =
608  g_strdup(_("Export merged visible layers to "
609  "RS-274X file as..."));
610  if (2 > visible_file_name(&file_name, &dir_name, GERBV_LAYERTYPE_RS274X, "", ".gbr")) {
611  error_visible_layers = TRUE;
612  break;
613  }
614  case CALLBACKS_SAVE_FILE_DRILL:
615  windowTitle = g_strdup_printf(
616  _("Export \"%s\" layer #%d to "
617  "Excellon drill file as..."),
618  act_file->name, file_index + 1
619  );
620 
621  if (GERBV_LAYERTYPE_DRILL != act_file->image->layertype)
622  file_name = g_strconcat(act_file->name, ".drl", NULL);
623  else
624  file_name = g_strdup(act_file->name);
625 
626  dir_name = g_path_get_dirname(act_file->fullPathname);
627  break;
628  case CALLBACKS_SAVE_FILE_DRILLM:
629  windowTitle =
630  g_strdup(_("Export merged visible layers to "
631  "Excellon drill file as..."));
632  if (2 > visible_file_name(&file_name, &dir_name, GERBV_LAYERTYPE_DRILL, "", ".drl")) {
633  error_visible_layers = TRUE;
634  }
635  break;
636  case CALLBACKS_SAVE_FILE_IDRILL:
637  windowTitle = g_strdup_printf(
638  _("Export \"%s\" layer #%d to ISEL NCP drill file as..."), act_file->name, file_index + 1
639  );
640  file_name = g_strconcat(act_file->name, ".ncp", NULL);
641  dir_name = g_path_get_dirname(act_file->fullPathname);
642 
643  break;
644  case CALLBACKS_SAVE_FILE_GEDA_PCB:
645  windowTitle =
646  g_strdup_printf(_("Export \"%s\" layer #%d to gEDA PCB file as..."), act_file->name, file_index + 1);
647  file_name = g_strconcat(act_file->name, ".pcb", NULL);
648  dir_name = g_path_get_dirname(act_file->fullPathname);
649  break;
650  case CALLBACKS_SAVE_LAYER_AS:
651  windowTitle = g_strdup_printf(_("Save \"%s\" layer #%d as..."), act_file->name, file_index + 1);
652  file_name = g_strdup(act_file->name);
653  dir_name = g_path_get_dirname(act_file->fullPathname);
654  break;
655  }
656 
657  if (file_name != NULL) {
658  gtk_file_chooser_set_current_name(file_chooser_p, file_name);
659  g_free(file_name);
660  }
661  if (dir_name != NULL) {
662  gtk_file_chooser_set_current_folder(file_chooser_p, dir_name);
663  g_free(dir_name);
664  }
665 
666  gtk_dialog_add_buttons(
667  GTK_DIALOG(screen.win.gerber), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL
668  );
669 
670  gtk_window_set_title(GTK_WINDOW(screen.win.gerber), windowTitle);
671  g_free(windowTitle);
672 
673  if (error_visible_layers) {
674  switch (processType) {
675  case CALLBACKS_SAVE_FILE_RS274XM:
677  _("Not enough Gerber layers are visible"),
678  _("Two or more Gerber layers must be visible "
679  "for export with merge."),
680  FALSE, NULL, NULL, GTK_STOCK_CANCEL
681  );
682  break;
683  case CALLBACKS_SAVE_FILE_DRILLM:
685  _("Not enough Excellon layers are visible"),
686  _("Two or more Excellon layers must be visible "
687  "for export with merge."),
688  FALSE, NULL, NULL, GTK_STOCK_CANCEL
689  );
690  break;
691  default:
693  _("No layers are visible"),
694  _("One or more "
695  "layers must be visible for export."),
696  FALSE, NULL, NULL, GTK_STOCK_CANCEL
697  );
698  }
699 
700  gtk_widget_destroy(screen.win.gerber);
701  callbacks_update_layer_tree();
702 
703  return;
704  }
705 
706  gtk_widget_show(screen.win.gerber);
707  if (gtk_dialog_run(GTK_DIALOG(screen.win.gerber)) == GTK_RESPONSE_ACCEPT) {
708  new_file_name = gtk_file_chooser_get_filename(file_chooser_p);
709  dpi = gtk_spin_button_get_value_as_int(spin_but);
710  }
711  gtk_widget_destroy(screen.win.gerber);
712 
713  if (!new_file_name) {
714  callbacks_update_layer_tree();
715 
716  return;
717  }
718 
719  switch (processType) {
720  case CALLBACKS_SAVE_PROJECT_AS:
721  main_save_as_project_from_filename(mainProject, new_file_name);
722  rename_main_window(new_file_name, NULL);
723  break;
724  case CALLBACKS_SAVE_FILE_PS:
726  break;
727  case CALLBACKS_SAVE_FILE_PDF: gerbv_export_pdf_file_from_project_autoscaled(mainProject, new_file_name); break;
728  case CALLBACKS_SAVE_FILE_SVG: gerbv_export_svg_file_from_project_autoscaled(mainProject, new_file_name); break;
729  case CALLBACKS_SAVE_FILE_DXF:
730 #if HAVE_LIBDXFLIB
731  if (gerbv_export_dxf_file_from_image(new_file_name, act_file->image, &act_file->transform)) {
732  GERB_MESSAGE(
733  _("\"%s\" layer #%d saved as DXF in \"%s\""), act_file->name, file_index + 1, new_file_name
734  );
735  }
736 #endif
737  break;
738  case CALLBACKS_SAVE_FILE_PNG:
739  if (dpi == 0) {
741  mainProject, screenRenderInfo.displayWidth, screenRenderInfo.displayHeight, new_file_name
742  );
743  } else { /* Non zero DPI */
745  gerbv_render_get_boundingbox(mainProject, &bb);
746  gfloat w = bb.right - bb.left;
747  gfloat h = bb.bottom - bb.top;
748  gerbv_render_info_t renderInfo = {
749  dpi,
750  dpi,
751  bb.left - (w * GERBV_DEFAULT_BORDER_COEFF) / 2.0,
752  bb.top - (h * GERBV_DEFAULT_BORDER_COEFF) / 2.0,
754  w * dpi * (1 + GERBV_DEFAULT_BORDER_COEFF),
755  h * dpi * (1 + GERBV_DEFAULT_BORDER_COEFF),
756  };
757  gerbv_export_png_file_from_project(mainProject, &renderInfo, new_file_name);
758  }
759 
760  break;
761  case CALLBACKS_SAVE_LAYER_AS:
762  gerbv_save_layer_from_index(mainProject, file_index, new_file_name);
763 
764  /* Rename the file path in the index, so future saves will
765  * reference the new file path */
766  g_free(act_file->fullPathname);
767  act_file->fullPathname = g_strdup(new_file_name);
768  g_free(act_file->name);
769  act_file->name = g_path_get_basename(new_file_name);
770 
771  break;
772  case CALLBACKS_SAVE_FILE_RS274X:
773  if (gerbv_export_rs274x_file_from_image(new_file_name, act_file->image, &act_file->transform)) {
774  GERB_MESSAGE(
775  _("\"%s\" layer #%d saved as Gerber in \"%s\""), act_file->name, file_index + 1, new_file_name
776  );
777  }
778  break;
779  case CALLBACKS_SAVE_FILE_DRILL:
780  if (gerbv_export_drill_file_from_image(new_file_name, act_file->image, &act_file->transform)) {
781  GERB_MESSAGE(
782  _("\"%s\" layer #%d saved as drill in \"%s\""), act_file->name, file_index + 1, new_file_name
783  );
784  }
785  break;
786  case CALLBACKS_SAVE_FILE_IDRILL:
787  if (gerbv_export_isel_drill_file_from_image(new_file_name, act_file->image, &act_file->transform)) {
788  GERB_MESSAGE(
789  _("\"%s\" layer #%d saved as ISEL NCP drill "
790  "in \"%s\""),
791  act_file->name, file_index + 1, new_file_name
792  );
793  }
794  break;
795  case CALLBACKS_SAVE_FILE_GEDA_PCB:
796  if (gerbv_export_geda_pcb_file_from_image(new_file_name, act_file->image, &act_file->transform)) {
797  GERB_MESSAGE(
798  _("\"%s\" layer #%d saved as gEDA PCB "
799  "in \"%s\""),
800  act_file->name, file_index + 1, new_file_name
801  );
802  }
803  break;
804  case CALLBACKS_SAVE_FILE_RS274XM:
805  {
806  gerbv_image_t* image;
807  gerbv_user_transformation_t t = { 0, 0, 1, 1, 0, FALSE, FALSE, FALSE };
808  if (NULL != (image = merge_images(processType))) {
809  if (gerbv_export_rs274x_file_from_image(new_file_name, image, &t)) {
810  GERB_MESSAGE(
811  _("Merged visible Gerber layers "
812  "and saved in \"%s\""),
813  new_file_name
814  );
815  }
816  gerbv_destroy_image(image);
817  }
818  break;
819  }
820  case CALLBACKS_SAVE_FILE_DRILLM:
821  {
822  gerbv_image_t* image;
823  gerbv_user_transformation_t t = { 0, 0, 1, 1, 0, FALSE, FALSE, FALSE };
824  if (NULL != (image = merge_images(processType))) {
825  gerbv_export_drill_file_from_image(new_file_name, image, &t);
826  gerbv_destroy_image(image);
827  GERB_MESSAGE(
828  _("Merged visible drill layers "
829  "and saved in \"%s\""),
830  new_file_name
831  );
832  }
833  break;
834  }
835  }
836 
837  g_free(new_file_name);
838  callbacks_update_layer_tree();
839 
840  return;
841 }
842 
843 /* --------------------------------------------------------- */
844 #if GTK_CHECK_VERSION(2, 10, 0)
845 
846 static void
847 callbacks_begin_print(GtkPrintOperation* operation, GtkPrintContext* context, gpointer user_data) {
848  gtk_print_operation_set_n_pages(operation, 1);
849 }
850 
851 /* --------------------------------------------------------- */
852 static void
853 callbacks_print_render_page(GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr, gpointer user_data) {
854  GtkPrintSettings* pSettings = gtk_print_operation_get_print_settings(operation);
855  gerbv_render_info_t renderInfo = { 1.0,
856  1.0,
857  0,
858  0,
860  (gint)gtk_print_context_get_width(context),
861  (gint)gtk_print_context_get_height(context) };
862  cairo_t* cr;
863 
864  /* have to assume x and y resolutions are the same for now, since we
865  don't support differing scales in the gerb_render_info_t struct yet */
866  gdouble xres = gtk_print_context_get_dpi_x(context);
867  gdouble yres = gtk_print_context_get_dpi_y(context);
868  gdouble scalePercentage = gtk_print_settings_get_scale(pSettings);
869  renderInfo.scaleFactorX = scalePercentage / 100 * xres;
870  renderInfo.scaleFactorY = scalePercentage / 100 * yres;
871 
872  gerbv_render_translate_to_fit_display(mainProject, &renderInfo);
873  cr = gtk_print_context_get_cairo_context(context);
874  gerbv_render_all_layers_to_cairo_target_for_vector_output(mainProject, cr, &renderInfo);
875 }
876 
877 /* --------------------------------------------------------- */
878 void
879 callbacks_print_activate(GtkMenuItem* menuitem, gpointer user_data) {
880  GtkPrintOperation* print;
881  /*GtkPrintOperationResult res;*/
882 
883  print = gtk_print_operation_new();
884 
885  g_signal_connect(print, "begin_print", G_CALLBACK(callbacks_begin_print), NULL);
886  g_signal_connect(print, "draw_page", G_CALLBACK(callbacks_print_render_page), NULL);
887 
888  // GtkPrintSettings *pSettings = gtk_print_operation_get_print_settings (print);
889 
890  (void)gtk_print_operation_run(
891  print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, (GtkWindow*)screen.win.topLevelWindow, NULL
892  );
893 
894  g_object_unref(print);
895 }
896 #endif /* GTK_CHECK_VERSION(2,10,0) */
897 
898 /* --------------------------------------------------------- */
899 void
900 callbacks_fullscreen_toggled(GtkMenuItem* menuitem, gpointer user_data) {
901  // struct GtkWindow *win = (struct GtkWindow *)(screen.win.topLevelWindow);
902  GdkWindowState state = gdk_window_get_state(gtk_widget_get_window(screen.win.topLevelWindow));
903  if (state & GDK_WINDOW_STATE_FULLSCREEN)
904  gtk_window_unfullscreen(GTK_WINDOW(screen.win.topLevelWindow));
905  else
906  gtk_window_fullscreen(GTK_WINDOW(screen.win.topLevelWindow));
907 }
908 
909 /* --------------------------------------------------------- */
910 void
911 callbacks_show_toolbar_toggled(GtkMenuItem* menuitem, gpointer user_data) {
912  gtk_widget_set_visible(user_data, GTK_CHECK_MENU_ITEM(menuitem)->active);
913 }
914 
915 /* --------------------------------------------------------- */
916 void
917 callbacks_show_sidepane_toggled(GtkMenuItem* menuitem, gpointer user_data) {
918  gtk_widget_set_visible(user_data, GTK_CHECK_MENU_ITEM(menuitem)->active);
919 }
920 
921 /* --------------------------------------------------------- */
922 void
923 callbacks_show_selection_on_invisible(GtkMenuItem* menuitem, gpointer user_data) {
924  mainProject->show_invisible_selection = GTK_CHECK_MENU_ITEM(menuitem)->active;
925  render_refresh_rendered_image_on_screen();
926 }
927 
928 /* --------------------------------------------------------- */
929 void
930 callbacks_show_cross_on_drill_holes(GtkMenuItem* menuitem, gpointer user_data) {
931  screenRenderInfo.show_cross_on_drill_holes = GTK_CHECK_MENU_ITEM(menuitem)->active;
932  render_refresh_rendered_image_on_screen();
933 }
934 
935 /* --------------------------------------------------------- */
939 void
940 callbacks_toggle_layer_visibility_activate(GtkMenuItem* menuitem, gpointer user_data) {
941  int i = GPOINTER_TO_INT(user_data);
942 
943  switch (i) {
944  case LAYER_SELECTED:
946  /* No break */
947  default:
948  if (0 <= i && i <= mainProject->last_loaded) {
950  } else {
951  /* Not in range */
952  return;
953  }
954  break;
955  case LAYER_ALL_ON:
956  for (i = 0; i <= mainProject->last_loaded; i++) {
957  mainProject->file[i]->isVisible = TRUE;
958  }
959  break;
960  case LAYER_ALL_OFF:
961  for (i = 0; i <= mainProject->last_loaded; i++) {
962  mainProject->file[i]->isVisible = FALSE;
963  }
964  break;
965  }
966 
967  callbacks_update_layer_tree();
968 
969  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
970  render_refresh_rendered_image_on_screen();
971  } else {
972  render_recreate_composite_surface(screen.drawing_area);
973  callbacks_force_expose_event_for_screen();
974  }
975 }
976 
977 /* --------------------------------------------------------- */
978 void
979 callbacks_zoom_in_activate(GtkMenuItem* menuitem, gpointer user_data) {
980  render_zoom_display(ZOOM_IN, 0, 0, 0);
981 }
982 
983 /* --------------------------------------------------------- */
984 void
985 callbacks_zoom_out_activate(GtkMenuItem* menuitem, gpointer user_data) {
986  render_zoom_display(ZOOM_OUT, 0, 0, 0);
987 }
988 
989 /* --------------------------------------------------------- */
990 void
991 callbacks_fit_to_window_activate(GtkMenuItem* menuitem, gpointer user_data) {
992  gerbv_render_zoom_to_fit_display(mainProject, &screenRenderInfo);
993  render_refresh_rendered_image_on_screen();
994 }
995 
996 /* --------------------------------------------------------- */
997 
998 static const char*
999 error_type_string(gerbv_message_type_t type) {
1000  switch (type) {
1001  case GERBV_MESSAGE_FATAL: return _("FATAL");
1002  case GERBV_MESSAGE_ERROR: return _("ERROR");
1003  case GERBV_MESSAGE_WARNING: return _("Warning");
1004  case GERBV_MESSAGE_NOTE: return _("Note");
1005  default: return "Unknown";
1006  }
1007 }
1008 
1009 /* --------------------------------------------------------- */
1016 void
1017 callbacks_analyze_active_gerbers_activate(GtkMenuItem* menuitem, gpointer user_data) {
1018  gerbv_aperture_list_t* aperture_list;
1019  gchar* str;
1020  int i;
1021 
1022  /* Get a report of stats & errors accumulated from all layers */
1024 
1025  /* General info report */
1026  GString* general_report_str = g_string_new(NULL);
1027  if (stat->layer_count == 0) {
1028  g_string_printf(general_report_str, _("No Gerber layers visible!"));
1029  } else {
1030  if (stat->error_list->error_text == NULL) {
1031  g_string_printf(
1032  general_report_str,
1033  ngettext(
1034  "No errors found in %d visible "
1035  "Gerber layer.",
1036  "No errors found in %d visible "
1037  "Gerber layers.",
1038  stat->layer_count
1039  ),
1040  stat->layer_count
1041  );
1042  } else {
1043  g_string_printf(
1044  general_report_str,
1045  ngettext(
1046  "Found errors in %d visible "
1047  "Gerber layer.",
1048  "Found errors in %d visible "
1049  "Gerber layers.",
1050  stat->layer_count
1051  ),
1052  stat->layer_count
1053  );
1054  }
1055  }
1056 
1057  GtkWidget* general_label = gtk_label_new(general_report_str->str);
1058  g_string_free(general_report_str, TRUE);
1059  gtk_misc_set_alignment(GTK_MISC(general_label), 0, 0);
1060  gtk_misc_set_padding(GTK_MISC(general_label), 7, 7);
1061  gtk_label_set_selectable(GTK_LABEL(general_label), TRUE);
1062 
1063  struct table* general_table;
1064 
1065  general_table =
1066  table_new_with_columns(3, _("Layer"), G_TYPE_UINT, _("Type"), G_TYPE_STRING, _("Description"), G_TYPE_STRING);
1067  table_set_column_align(general_table, 0, 1.0);
1068 
1069  for (i = 0; i <= mainProject->last_loaded; i++) {
1070  gerbv_fileinfo_t** files = mainProject->file;
1071 
1072  if (!files[i] || !files[i]->isVisible || files[i]->image->layertype != GERBV_LAYERTYPE_RS274X)
1073  continue;
1074 
1075  table_add_row(general_table, i + 1, _("RS-274X file"), files[i]->fullPathname);
1076 
1077  str = g_strdup_printf(
1078  _("%g x %g %s"), screen_units(fabs(files[i]->image->info->max_x - files[i]->image->info->min_x)),
1079  screen_units(fabs(files[i]->image->info->max_y - files[i]->image->info->min_y)), screen_units_str()
1080  );
1081  table_add_row(general_table, i + 1, _("Bounding size"), str);
1082  g_free(str);
1083 
1084  /* Check error report on layer */
1085  if (stat->layer_count > 0 && stat->error_list->error_text != NULL) {
1086  for (gerbv_error_list_t* err_list = stat->error_list; err_list != NULL; err_list = err_list->next) {
1087 
1088  if (i != err_list->layer - 1)
1089  continue;
1090 
1091  table_add_row(general_table, err_list->layer, error_type_string(err_list->type), err_list->error_text);
1092  }
1093  }
1094  }
1095 
1096  /* G codes on active layers */
1097  GtkWidget* G_report_window = gtk_scrolled_window_new(NULL, NULL);
1098  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(G_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1099 
1100  struct table* G_table = table_new_with_columns(
1101  3, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT, _("Note"), G_TYPE_STRING
1102  );
1103  table_set_column_align(G_table, 0, 1.0);
1104  table_set_column_align(G_table, 1, 1.0);
1105  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(G_table->widget), TRUE);
1106 
1107  table_add_row(G_table, "G00", stat->G0, _(gerber_g_code_name(0)));
1108  table_add_row(G_table, "G01", stat->G1, _(gerber_g_code_name(1)));
1109  table_add_row(G_table, "G02", stat->G2, _(gerber_g_code_name(2)));
1110  table_add_row(G_table, "G03", stat->G3, _(gerber_g_code_name(3)));
1111  table_add_row(G_table, "G04", stat->G4, _(gerber_g_code_name(4)));
1112  table_add_row(G_table, "G10", stat->G10, _(gerber_g_code_name(10)));
1113  table_add_row(G_table, "G11", stat->G11, _(gerber_g_code_name(11)));
1114  table_add_row(G_table, "G12", stat->G12, _(gerber_g_code_name(12)));
1115  table_add_row(G_table, "G36", stat->G36, _(gerber_g_code_name(36)));
1116  table_add_row(G_table, "G37", stat->G37, _(gerber_g_code_name(37)));
1117  table_add_row(G_table, "G54", stat->G54, _(gerber_g_code_name(54)));
1118  table_add_row(G_table, "G55", stat->G55, _(gerber_g_code_name(55)));
1119  table_add_row(G_table, "G70", stat->G70, _(gerber_g_code_name(70)));
1120  table_add_row(G_table, "G71", stat->G71, _(gerber_g_code_name(71)));
1121  table_add_row(G_table, "G74", stat->G74, _(gerber_g_code_name(74)));
1122  table_add_row(G_table, "G75", stat->G75, _(gerber_g_code_name(75)));
1123  table_add_row(G_table, "G90", stat->G90, _(gerber_g_code_name(90)));
1124  table_add_row(G_table, "G91", stat->G91, _(gerber_g_code_name(91)));
1125  table_add_row(G_table, "", stat->G_unknown, _("unknown G-codes"));
1126 
1127  table_set_sortable(G_table);
1128  gtk_container_add(GTK_CONTAINER(G_report_window), G_table->widget);
1129 
1130  /* D codes on active layers */
1131  GtkWidget* D_report_window = gtk_scrolled_window_new(NULL, NULL);
1132  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(D_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1133 
1134  struct table* D_table = table_new_with_columns(
1135  3, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT, _("Note"), G_TYPE_STRING
1136  );
1137  table_set_column_align(D_table, 0, 1.0);
1138  table_set_column_align(D_table, 1, 1.0);
1139  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(D_table->widget), TRUE);
1140  table_add_row(D_table, "D01", stat->D1, _(gerber_d_code_name(1)));
1141  table_add_row(D_table, "D02", stat->D2, _(gerber_d_code_name(2)));
1142  table_add_row(D_table, "D03", stat->D3, _(gerber_d_code_name(3)));
1143  table_add_row(D_table, "", stat->D_unknown, _("unknown D-codes"));
1144  table_add_row(D_table, "", stat->D_error, _("D-code errors"));
1145 
1146  table_set_sortable(D_table);
1147  gtk_container_add(GTK_CONTAINER(D_report_window), D_table->widget);
1148 
1149  /* M codes on active layers */
1150  GtkWidget* M_report_window = gtk_scrolled_window_new(NULL, NULL);
1151  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(M_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1152 
1153  struct table* M_table = table_new_with_columns(
1154  3, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT, _("Note"), G_TYPE_STRING
1155  );
1156  table_set_column_align(M_table, 0, 1.0);
1157  table_set_column_align(M_table, 1, 1.0);
1158  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(M_table->widget), TRUE);
1159  table_add_row(M_table, "M00", stat->M0, _(gerber_m_code_name(0)));
1160  table_add_row(M_table, "M01", stat->M1, _(gerber_m_code_name(1)));
1161  table_add_row(M_table, "M02", stat->M2, _(gerber_m_code_name(2)));
1162  table_add_row(M_table, "", stat->M_unknown, _("unknown M-codes"));
1163 
1164  table_set_sortable(M_table);
1165  gtk_container_add(GTK_CONTAINER(M_report_window), M_table->widget);
1166 
1167  /* Misc codes */
1168  GtkWidget* misc_report_window = gtk_scrolled_window_new(NULL, NULL);
1169  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(misc_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1170 
1171  struct table* misc_table =
1172  table_new_with_columns(2, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT);
1173  table_set_column_align(misc_table, 1, 1.0);
1174  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(misc_table->widget), TRUE);
1175  table_add_row(misc_table, "X", stat->X);
1176  table_add_row(misc_table, "Y", stat->Y);
1177  table_add_row(misc_table, "I", stat->I);
1178  table_add_row(misc_table, "J", stat->J);
1179  table_add_row(misc_table, "*", stat->star);
1180  table_add_row(misc_table, _("Unknown"), stat->unknown);
1181 
1182  table_set_sortable(misc_table);
1183  gtk_container_add(GTK_CONTAINER(misc_report_window), misc_table->widget);
1184 
1185  /* Apertures definition in input files */
1186  GtkWidget* aperture_def_report_window = gtk_scrolled_window_new(NULL, NULL);
1187  gtk_scrolled_window_set_policy(
1188  GTK_SCROLLED_WINDOW(aperture_def_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
1189  );
1190 
1191  if (stat->aperture_list->number == -1) {
1192  GtkWidget* aperture_def_label = gtk_label_new(_("No aperture definitions found in active Gerber file(s)!"));
1193  gtk_misc_set_alignment(GTK_MISC(aperture_def_label), 0, 0);
1194  gtk_misc_set_padding(GTK_MISC(aperture_def_label), 7, 7);
1195  gtk_label_set_selectable(GTK_LABEL(aperture_def_label), TRUE);
1196  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(aperture_def_report_window), aperture_def_label);
1197  } else {
1198  struct table* aperture_def_table = table_new_with_columns(
1199  6, _("Layer"), G_TYPE_UINT, _("D code"), G_TYPE_STRING, _("Aperture"), G_TYPE_STRING, _("Param[0]"),
1200  G_TYPE_DOUBLE, _("Param[1]"), G_TYPE_DOUBLE, _("Param[2]"), G_TYPE_DOUBLE
1201  );
1202  table_set_column_align(aperture_def_table, 0, 1.0);
1203  table_set_column_align(aperture_def_table, 1, 1.0);
1204  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(aperture_def_table->widget), TRUE);
1205  gtk_tree_view_set_search_column(GTK_TREE_VIEW(aperture_def_table->widget), 1);
1206 
1207  GString* gstr = g_string_new(NULL);
1208  for (aperture_list = stat->aperture_list; aperture_list != NULL; aperture_list = aperture_list->next) {
1209  g_string_printf(gstr, "D%d", aperture_list->number);
1210  table_add_row(
1211  aperture_def_table, aperture_list->layer, gstr->str, _(gerbv_aperture_type_name(aperture_list->type)),
1212  aperture_list->parameter[0], aperture_list->parameter[1], aperture_list->parameter[2]
1213  );
1214  }
1215  g_string_free(gstr, TRUE);
1216  table_set_sortable(aperture_def_table);
1217  gtk_container_add(GTK_CONTAINER(aperture_def_report_window), aperture_def_table->widget);
1218  }
1219 
1220  /* Gerber aperture usage on active layers */
1221  GtkWidget* aperture_usage_report_window = gtk_scrolled_window_new(NULL, NULL);
1222  gtk_scrolled_window_set_policy(
1223  GTK_SCROLLED_WINDOW(aperture_usage_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
1224  );
1225 
1226  unsigned int aperture_count = 0;
1227 
1228  if (stat->D_code_list->number == -1) {
1229  GtkWidget* aperture_usage_label = gtk_label_new(_("No apertures used in Gerber file(s)!"));
1230  gtk_misc_set_alignment(GTK_MISC(aperture_usage_label), 0, 0);
1231  gtk_misc_set_padding(GTK_MISC(aperture_usage_label), 7, 7);
1232  gtk_label_set_selectable(GTK_LABEL(aperture_usage_label), TRUE);
1233  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(aperture_usage_report_window), aperture_usage_label);
1234  } else {
1235  struct table* aperture_usage_table =
1236  table_new_with_columns(2, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT);
1237  table_set_column_align(aperture_usage_table, 0, 1.0);
1238  table_set_column_align(aperture_usage_table, 1, 1.0);
1239  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(aperture_usage_table->widget), TRUE);
1240 
1241  GString* gstr = g_string_new(NULL);
1242  for (aperture_list = stat->D_code_list; aperture_list != NULL; aperture_list = aperture_list->next) {
1243  g_string_printf(gstr, "D%d", aperture_list->number);
1244  table_add_row(aperture_usage_table, gstr->str, aperture_list->count);
1245  aperture_count += aperture_list->count;
1246  }
1247  g_string_free(gstr, TRUE);
1248  table_add_row(aperture_usage_table, _("Total"), aperture_count);
1249  table_set_sortable(aperture_usage_table);
1250  gtk_container_add(GTK_CONTAINER(aperture_usage_report_window), aperture_usage_table->widget);
1251  }
1252 
1253  /* Create top level dialog window for report */
1254  GtkWidget* analyze_active_gerbers;
1255  analyze_active_gerbers = gtk_dialog_new_with_buttons(
1256  _("Gerber codes report on visible layers"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
1257  GTK_RESPONSE_ACCEPT, NULL
1258  );
1259  gtk_container_set_border_width(GTK_CONTAINER(analyze_active_gerbers), 5);
1260 
1261  gtk_dialog_set_default_response(GTK_DIALOG(analyze_active_gerbers), GTK_RESPONSE_ACCEPT);
1262  g_signal_connect_after(
1263  G_OBJECT(analyze_active_gerbers), "response", G_CALLBACK(gtk_widget_destroy), GTK_WIDGET(analyze_active_gerbers)
1264  );
1265 
1266  /* Put general report text into scrolled window */
1267  GtkWidget* general_report_window = gtk_scrolled_window_new(NULL, NULL);
1268  gtk_scrolled_window_set_policy(
1269  GTK_SCROLLED_WINDOW(general_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
1270  );
1271 
1272  GtkWidget* vbox = gtk_vbox_new(0, 0);
1273  gtk_box_pack_start(GTK_BOX(vbox), general_label, 0, 0, 0);
1274  gtk_box_pack_start(GTK_BOX(vbox), general_table->widget, 0, 0, 0);
1275  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(general_report_window), vbox);
1276 
1277  /* Create tabbed notebook widget and add report widgets. */
1278  GtkWidget* notebook = gtk_notebook_new();
1279 
1280  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(general_report_window), gtk_label_new(_("General")));
1281 
1282  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(G_report_window), gtk_label_new(_("G codes")));
1283 
1284  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(D_report_window), gtk_label_new(_("D codes")));
1285 
1286  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(M_report_window), gtk_label_new(_("M codes")));
1287 
1288  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(misc_report_window), gtk_label_new(_("Misc. codes")));
1289 
1290  gtk_notebook_append_page(
1291  GTK_NOTEBOOK(notebook), GTK_WIDGET(aperture_def_report_window), gtk_label_new(_("Aperture definitions"))
1292  );
1293 
1294  gtk_notebook_append_page(
1295  GTK_NOTEBOOK(notebook), GTK_WIDGET(aperture_usage_report_window), gtk_label_new(_("Aperture usage"))
1296  );
1297 
1298  /* Now put notebook into dialog window and show the whole thing */
1299  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(analyze_active_gerbers)->vbox), GTK_WIDGET(notebook));
1300 
1301  if (screen.settings) {
1302  analyze_window_size_restore(analyze_active_gerbers);
1303  g_signal_connect(
1304  G_OBJECT(analyze_active_gerbers), "response", G_CALLBACK(analyze_window_size_store),
1305  GTK_WIDGET(analyze_active_gerbers)
1306  );
1307  } else {
1308  gtk_window_set_default_size(GTK_WINDOW(analyze_active_gerbers), 640, 320);
1309  }
1310 
1311  gtk_widget_show_all(analyze_active_gerbers);
1312 
1313  gerbv_stats_destroy(stat);
1314 }
1315 
1316 /* --------------------------------------------------------- */
1323 void
1324 callbacks_analyze_active_drill_activate(GtkMenuItem* menuitem, gpointer user_data) {
1325  gchar* str;
1326  int i;
1327 
1329 
1330  /* General info report */
1331  GString* general_report_str = g_string_new(NULL);
1332  if (stat->layer_count == 0) {
1333  g_string_printf(general_report_str, _("No drill layers visible!"));
1334  } else {
1335  if (stat->error_list->error_text == NULL) {
1336  g_string_printf(
1337  general_report_str,
1338  ngettext(
1339  "No errors found in %d visible "
1340  "drill layer.",
1341  "No errors found in %d visible "
1342  "drill layers.",
1343  stat->layer_count
1344  ),
1345  stat->layer_count
1346  );
1347  } else {
1348  g_string_printf(
1349  general_report_str,
1350  ngettext(
1351  "Found errors found in %d visible "
1352  "drill layer.",
1353  "Found errors found in %d visible "
1354  "drill layers.",
1355  stat->layer_count
1356  ),
1357  stat->layer_count
1358  );
1359  }
1360  }
1361 
1362  GtkWidget* general_label = gtk_label_new(general_report_str->str);
1363  g_string_free(general_report_str, TRUE);
1364  gtk_misc_set_alignment(GTK_MISC(general_label), 0, 0);
1365  gtk_misc_set_padding(GTK_MISC(general_label), 7, 7);
1366  gtk_label_set_selectable(GTK_LABEL(general_label), TRUE);
1367 
1368  struct table* general_table;
1369 
1370  general_table =
1371  table_new_with_columns(3, _("Layer"), G_TYPE_UINT, _("Type"), G_TYPE_STRING, _("Description"), G_TYPE_STRING);
1372  table_set_column_align(general_table, 0, 1.0);
1373 
1374  for (i = 0; i <= mainProject->last_loaded; i++) {
1375  gerbv_fileinfo_t** files = mainProject->file;
1376 
1377  if (!files[i] || !files[i]->isVisible || files[i]->image->layertype != GERBV_LAYERTYPE_DRILL)
1378  continue;
1379 
1380  table_add_row(general_table, i + 1, _("Excellon file"), files[i]->fullPathname);
1381 
1382  str = g_strdup_printf(
1383  _("%g x %g %s"), screen_units(fabs(files[i]->image->info->max_x - files[i]->image->info->min_x)),
1384  screen_units(fabs(files[i]->image->info->max_y - files[i]->image->info->min_y)), screen_units_str()
1385  );
1386  table_add_row(general_table, i + 1, _("Bounding size"), str);
1387  g_free(str);
1388 
1389  /* Check error report on layer */
1390  if (stat->layer_count > 0 && stat->error_list->error_text != NULL) {
1391  for (gerbv_error_list_t* err_list = stat->error_list; err_list != NULL; err_list = err_list->next) {
1392 
1393  if (i != err_list->layer - 1)
1394  continue;
1395 
1396  table_add_row(general_table, err_list->layer, error_type_string(err_list->type), err_list->error_text);
1397  }
1398  }
1399  }
1400 
1401  /* G codes on active layers */
1402  GtkWidget* G_report_window = gtk_scrolled_window_new(NULL, NULL);
1403  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(G_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1404 
1405  struct table* G_table = table_new_with_columns(
1406  3, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT, _("Note"), G_TYPE_STRING
1407  );
1408  table_set_column_align(G_table, 0, 1.0);
1409  table_set_column_align(G_table, 1, 1.0);
1410  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(G_table->widget), TRUE);
1411 
1412  table_add_row(G_table, "G00", stat->G00, _(drill_g_code_name(0)));
1413  table_add_row(G_table, "G01", stat->G01, _(drill_g_code_name(1)));
1414  table_add_row(G_table, "G02", stat->G02, _(drill_g_code_name(2)));
1415  table_add_row(G_table, "G03", stat->G03, _(drill_g_code_name(3)));
1416  table_add_row(G_table, "G04", stat->G04, _(drill_g_code_name(4)));
1417  table_add_row(G_table, "G05", stat->G05, _(drill_g_code_name(5)));
1418  table_add_row(G_table, "G85", stat->G85, _(drill_g_code_name(85)));
1419  table_add_row(G_table, "G90", stat->G90, _(drill_g_code_name(90)));
1420  table_add_row(G_table, "G91", stat->G91, _(drill_g_code_name(91)));
1421  table_add_row(G_table, "G93", stat->G93, _(drill_g_code_name(93)));
1422  table_add_row(G_table, "", stat->G_unknown, _("unknown G-codes"));
1423 
1424  table_set_sortable(G_table);
1425  gtk_container_add(GTK_CONTAINER(G_report_window), G_table->widget);
1426 
1427  /* M codes on active layers */
1428  GtkWidget* M_report_window = gtk_scrolled_window_new(NULL, NULL);
1429  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(M_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1430 
1431  struct table* M_table = table_new_with_columns(
1432  3, _("Code"), G_TYPE_STRING, pgettext("table", "Count"), G_TYPE_UINT, _("Note"), G_TYPE_STRING
1433  );
1434  table_set_column_align(M_table, 0, 1.0);
1435  table_set_column_align(M_table, 1, 1.0);
1436  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(M_table->widget), TRUE);
1437  table_add_row(M_table, "M00", stat->M00, _(drill_m_code_name(0)));
1438  table_add_row(M_table, "M01", stat->M01, _(drill_m_code_name(1)));
1439  table_add_row(M_table, "M18", stat->M18, _(drill_m_code_name(18)));
1440  table_add_row(M_table, "M25", stat->M25, _(drill_m_code_name(25)));
1441  table_add_row(M_table, "M30", stat->M30, _(drill_m_code_name(30)));
1442  table_add_row(M_table, "M45", stat->M45, _(drill_m_code_name(45)));
1443  table_add_row(M_table, "M47", stat->M47, _(drill_m_code_name(47)));
1444  table_add_row(M_table, "M48", stat->M48, _(drill_m_code_name(48)));
1445  table_add_row(M_table, "M71", stat->M71, _(drill_m_code_name(71)));
1446  table_add_row(M_table, "M72", stat->M72, _(drill_m_code_name(72)));
1447  table_add_row(M_table, "M95", stat->M95, _(drill_m_code_name(95)));
1448  table_add_row(M_table, "M97", stat->M97, _(drill_m_code_name(97)));
1449  table_add_row(M_table, "M98", stat->M98, _(drill_m_code_name(98)));
1450  table_add_row(M_table, "", stat->M_unknown, _("unknown M-codes"));
1451 
1452  table_set_sortable(M_table);
1453  gtk_container_add(GTK_CONTAINER(M_report_window), M_table->widget);
1454 
1455  /* Misc codes */
1456  GtkWidget* misc_report_window = gtk_scrolled_window_new(NULL, NULL);
1457  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(misc_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1458 
1459  struct table* misc_table = table_new_with_columns(
1460  2,
1461  /* Count is string for value hide. */
1462  pgettext("table", "Count"), G_TYPE_STRING, _("Code"), G_TYPE_STRING
1463  );
1464  table_set_column_align(misc_table, 0, 1.0);
1465  str = g_strdup_printf("%d", stat->comment);
1466  table_add_row(misc_table, str, _("Comments"));
1467  g_free(str);
1468  str = g_strdup_printf("%d", stat->unknown);
1469  table_add_row(misc_table, str, _("Unknown codes"));
1470  g_free(str);
1471  str = g_strdup_printf("%d", stat->R);
1472  table_add_row(misc_table, str, _("Repeat hole (R)"));
1473  g_free(str);
1474  if (stat->detect != NULL) {
1475  table_add_row(misc_table, "", stat->detect);
1476  }
1477 
1478  table_set_sortable(misc_table);
1479  gtk_container_add(GTK_CONTAINER(misc_report_window), misc_table->widget);
1480 
1481  /* Drill usage on active layers */
1482  GtkWidget* drill_usage_report_window = gtk_scrolled_window_new(NULL, NULL);
1483  gtk_scrolled_window_set_policy(
1484  GTK_SCROLLED_WINDOW(drill_usage_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
1485  );
1486 
1487  struct table* drill_usage_table = table_new_with_columns(
1488  4, _("Drill no."), G_TYPE_UINT, _("Dia."), G_TYPE_DOUBLE, _("Units"), G_TYPE_STRING, pgettext("table", "Count"),
1489  G_TYPE_UINT
1490  );
1491 
1492  table_set_column_align(drill_usage_table, 0, 1.0);
1493  table_set_column_align(drill_usage_table, 3, 1.0);
1494  gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(drill_usage_table->widget), TRUE);
1495 
1497  for (drill_list = stat->drill_list; drill_list != NULL; drill_list = drill_list->next) {
1498  if (drill_list->drill_num == -1)
1499  break; /* No drill list */
1500 
1501  table_add_row(
1502  drill_usage_table, drill_list->drill_num, drill_list->drill_size, drill_list->drill_unit,
1503  drill_list->drill_count
1504  );
1505  }
1506 
1507  table_set_sortable(drill_usage_table);
1508  gtk_container_add(GTK_CONTAINER(drill_usage_report_window), drill_usage_table->widget);
1509 
1510  /* Create top level dialog window for report */
1511  GtkWidget* analyze_active_drill;
1512  analyze_active_drill = gtk_dialog_new_with_buttons(
1513  _("Drill codes report on visible layers"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
1514  GTK_RESPONSE_ACCEPT, NULL
1515  );
1516  gtk_container_set_border_width(GTK_CONTAINER(analyze_active_drill), 5);
1517 
1518  gtk_dialog_set_default_response(GTK_DIALOG(analyze_active_drill), GTK_RESPONSE_ACCEPT);
1519  g_signal_connect_after(
1520  G_OBJECT(analyze_active_drill), "response", G_CALLBACK(gtk_widget_destroy), GTK_WIDGET(analyze_active_drill)
1521  );
1522 
1523  /* Put general report text into scrolled window */
1524  GtkWidget* general_report_window = gtk_scrolled_window_new(NULL, NULL);
1525  gtk_scrolled_window_set_policy(
1526  GTK_SCROLLED_WINDOW(general_report_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
1527  );
1528 
1529  GtkWidget* vbox = gtk_vbox_new(0, 0);
1530  gtk_box_pack_start(GTK_BOX(vbox), general_label, 0, 0, 0);
1531  gtk_box_pack_start(GTK_BOX(vbox), general_table->widget, 0, 0, 0);
1532  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(general_report_window), vbox);
1533 
1534  /* Create tabbed notebook widget and add report widgets. */
1535  GtkWidget* notebook = gtk_notebook_new();
1536 
1537  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(general_report_window), gtk_label_new(_("General")));
1538 
1539  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(G_report_window), gtk_label_new(_("G codes")));
1540 
1541  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(M_report_window), gtk_label_new(_("M codes")));
1542 
1543  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(misc_report_window), gtk_label_new(_("Misc. codes")));
1544 
1545  gtk_notebook_append_page(
1546  GTK_NOTEBOOK(notebook), GTK_WIDGET(drill_usage_report_window), gtk_label_new(_("Drill usage"))
1547  );
1548 
1549  /* Now put notebook into dialog window and show the whole thing */
1550  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(analyze_active_drill)->vbox), GTK_WIDGET(notebook));
1551 
1552  if (screen.settings) {
1553  analyze_window_size_restore(analyze_active_drill);
1554  g_signal_connect(
1555  G_OBJECT(analyze_active_drill), "response", G_CALLBACK(analyze_window_size_store),
1556  GTK_WIDGET(analyze_active_drill)
1557  );
1558  } else {
1559  gtk_window_set_default_size(GTK_WINDOW(analyze_active_drill), 640, 320);
1560  }
1561 
1562  gtk_widget_show_all(analyze_active_drill);
1563 
1565 }
1566 
1567 /* --------------------------------------------------------- */
1568 void
1569 callbacks_control_gerber_options_activate(GtkMenuItem* menuitem, gpointer user_data) {}
1570 
1571 /* --------------------------------------------------------- */
1572 void
1573 callbacks_online_manual_activate(GtkMenuItem* menuitem, gpointer user_data) {}
1574 
1575 /* --------------------------------------------------------- */
1582 gboolean
1583 callbacks_quit_activate(GtkMenuItem* menuitem, gpointer user_data) {
1584  gboolean layers_dirty = FALSE;
1585  gint idx;
1586 
1587  for (idx = 0; idx <= mainProject->last_loaded; idx++) {
1588  if (mainProject->file[idx] == NULL)
1589  break;
1590  layers_dirty = layers_dirty || mainProject->file[idx]->layer_dirty;
1591  }
1592 
1593  if (layers_dirty
1595  _("Do you want to close all open layers and quit the program?"),
1596  _("Quitting the program will cause any unsaved changes "
1597  "to be lost."),
1598  FALSE, NULL, GTK_STOCK_QUIT, GTK_STOCK_CANCEL
1599  )) {
1600  return TRUE; // stop propagation of the delete_event.
1601  // this would destroy the gui but not return from the gtk event loop.
1602  }
1603 
1604  /* Save background color */
1605  if (screen.settings && !screen.background_is_from_project) {
1606  guint clr;
1607  GdkColor* bg = &mainProject->background;
1608 
1609  clr = bg->red / 257 << 16 | bg->green / 257 << 8 | bg->blue / 257;
1610  g_settings_set_uint(screen.settings, "background-color", clr);
1611  }
1612 
1613  /* Save main window size and postion */
1614  if (screen.settings) {
1615  GtkWindow* win = GTK_WINDOW(screen.win.topLevelWindow);
1616  gint32 xy[2];
1617  GVariant* var;
1618  gboolean is_max;
1619 
1620  is_max = FALSE != (GDK_WINDOW_STATE_MAXIMIZED & gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(win))));
1621  g_settings_set_boolean(screen.settings, "window-maximized", is_max);
1622 
1623  if (!is_max) {
1624  gtk_window_get_size(win, (gint*)xy, (gint*)(xy + 1));
1625  var = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, xy, 2, sizeof(xy[0]));
1626  g_settings_set_value(screen.settings, "window-size", var);
1627 
1628  gtk_window_get_position(win, (gint*)xy, (gint*)(xy + 1));
1629  var = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, xy, 2, sizeof(xy[0]));
1630  g_settings_set_value(screen.settings, "window-position", var);
1631  }
1632  }
1633 
1634  gerbv_unload_all_layers(mainProject);
1635  gtk_main_quit();
1636 
1637  return FALSE;
1638 }
1639 
1640 /* --------------------------------------------------------- */
1646 void
1647 callbacks_about_activate(GtkMenuItem* menuitem, gpointer user_data) {
1648  GtkWidget* aboutdialog1;
1649  /* TRANSLATORS: Replace this string with your names, one name per line. */
1650  gchar* translators = _("translator-credits");
1651 
1652  gchar* string = g_strdup_printf(
1653  _("Gerbv — a Gerber (RS-274/X) viewer\n"
1654  "\n"
1655  "Version %s\n"
1656  "Compiled on %s at %s\n"
1657  "\n"
1658  "Gerbv was originally developed as part of the gEDA Project "
1659  "but is now separately maintained.\n"
1660  "\n"
1661  "For more information see:\n"
1662  " gerbv homepage: https://gerbv.github.io/\n"
1663  " gerbv repository: https://github.com/gerbv/gerbv"),
1664  VERSION, __DATE__, __TIME__
1665  );
1666 
1667 #if GTK_CHECK_VERSION(2, 6, 0)
1668  gchar* license =
1669  g_strdup_printf(_("Gerbv — a Gerber (RS-274/X) viewer\n"
1670  "\n"
1671  "Copyright (C) 2000—2007 Stefan Petersen\n"
1672  "\n"
1673  "This program is free software: you can redistribute it and/or modify\n"
1674  "it under the terms of the GNU General Public License as published by\n"
1675  "the Free Software Foundation, either version 2 of the License, or\n"
1676  "(at your option) any later version.\n"
1677  "\n"
1678  "This program is distributed in the hope that it will be useful,\n"
1679  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1680  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1681  "GNU General Public License for more details.\n\n"
1682  "You should have received a copy of the GNU General Public License\n"
1683  "along with this program. If not, see <http://www.gnu.org/licenses/>."));
1684 
1685 #include "authors.c"
1686 
1687  int a_size, i;
1688  gchar** a;
1689 
1690  aboutdialog1 = gtk_about_dialog_new();
1691  gtk_container_set_border_width(GTK_CONTAINER(aboutdialog1), 5);
1692  gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(aboutdialog1), VERSION);
1693  gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(aboutdialog1), _("Gerbv"));
1694 
1695  gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(aboutdialog1), translators);
1696  gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(aboutdialog1), string);
1697  gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(aboutdialog1), license);
1698 
1699  /* The authors.c file is autogenerated at build time */
1700  a_size = sizeof(authors_string_array) / sizeof(authors_string_array[0]);
1701  a = g_new(gchar*, a_size);
1702  for (i = 0; i < a_size; i++)
1703  a[i] = _(authors_string_array[i]);
1704 
1705  gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(aboutdialog1), (const gchar**)a);
1706  gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(aboutdialog1), "https://gerbv.github.io/");
1707  g_free(a);
1708 
1709  g_signal_connect(G_OBJECT(aboutdialog1), "response", G_CALLBACK(gtk_widget_destroy), GTK_WIDGET(aboutdialog1));
1710 
1711  g_free(string);
1712  g_free(license);
1713 #else
1714  aboutdialog1 = gtk_message_dialog_new(
1715  GTK_WINDOW(screen.win.topLevelWindow), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1716  string
1717  );
1718 
1719  gtk_window_set_title(GTK_WINDOW(aboutdialog1), _("About Gerbv"));
1720 
1721  /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
1722  g_signal_connect_swapped(aboutdialog1, "response", G_CALLBACK(gtk_widget_destroy), aboutdialog1);
1723  g_free(string);
1724 #endif
1725 
1726  gtk_widget_show_all(GTK_WIDGET(aboutdialog1));
1727 }
1728 
1729 /* --------------------------------------------------------- */
1735 void
1736 callbacks_bugs_activate(GtkMenuItem* menuitem, gpointer user_data) {
1737  int i;
1738 #include "bugs.c"
1739 
1740  /* Create the top level dialog widget with an OK button */
1741  GtkWidget* bugs_dialog = gtk_dialog_new_with_buttons(
1742  _("Known bugs in Gerbv"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL
1743  );
1744  gtk_container_set_border_width(GTK_CONTAINER(bugs_dialog), 5);
1745  gtk_dialog_set_default_response(GTK_DIALOG(bugs_dialog), GTK_RESPONSE_ACCEPT);
1746  g_signal_connect(G_OBJECT(bugs_dialog), "response", G_CALLBACK(gtk_widget_destroy), GTK_WIDGET(bugs_dialog));
1747 
1748  /* First create single bugs_string from bugs_string_array */
1749  GString* bugs_string = g_string_new(NULL);
1750  for (i = 0; bugs_string_array[i] != NULL; i++) {
1751  /* gettext("") will return info string */
1752  g_string_append_printf(bugs_string, "%s\n", (bugs_string_array[i][0] == '\0') ? "" : _(bugs_string_array[i]));
1753  }
1754 
1755  /* Create GtkLabel to hold text */
1756  GtkWidget* bugs_label = gtk_label_new(bugs_string->str);
1757  g_string_free(bugs_string, FALSE);
1758  gtk_misc_set_alignment(GTK_MISC(bugs_label), 0, 0);
1759  gtk_misc_set_padding(GTK_MISC(bugs_label), 7, 7);
1760 
1761  /* Put text into scrolled window */
1762  GtkWidget* bugs_window = gtk_scrolled_window_new(NULL, NULL);
1763  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(bugs_window), GTK_WIDGET(bugs_label));
1764  gtk_widget_set_size_request(bugs_window, 600, 300);
1765  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(bugs_dialog)->vbox), GTK_WIDGET(bugs_window));
1766 
1767  gtk_widget_show_all(GTK_WIDGET(bugs_dialog));
1768  gtk_dialog_run(GTK_DIALOG(bugs_dialog));
1769 }
1770 
1771 /* --------------------------------------------------------- */
1772 gdouble
1773 callbacks_calculate_actual_distance(gdouble inputDimension) {
1774  return screen_units(inputDimension);
1775 }
1776 
1777 /* --------------------------------------------------------- */
1778 void
1779 callbacks_update_ruler_pointers(void) {
1780  double xPosition, yPosition;
1781  xPosition = screenRenderInfo.lowerLeftX + (screen.last_x / screenRenderInfo.scaleFactorX);
1782  yPosition = screenRenderInfo.lowerLeftY
1783  + ((screenRenderInfo.displayHeight - screen.last_y) / screenRenderInfo.scaleFactorY);
1784 
1785  if (!((screen.unit == GERBV_MILS) && ((screenRenderInfo.scaleFactorX < 80) || (screenRenderInfo.scaleFactorY < 80))
1786  )) {
1787  xPosition = callbacks_calculate_actual_distance(xPosition);
1788  yPosition = callbacks_calculate_actual_distance(yPosition);
1789  }
1790  g_object_set(G_OBJECT(screen.win.hRuler), "position", xPosition, NULL);
1791  g_object_set(G_OBJECT(screen.win.vRuler), "position", yPosition, NULL);
1792 }
1793 
1794 /* --------------------------------------------------------- */
1795 static void
1796 callbacks_render_type_changed() {
1797  static gboolean isChanging = FALSE;
1798  if (isChanging)
1799  return;
1800 
1801  isChanging = TRUE;
1802  gerbv_render_types_t type = screenRenderInfo.renderType;
1803  GtkCheckMenuItem* check_item = screen.win.menu_view_render_group[type];
1804  dprintf("%s(): type = %d, check_item = %p\n", __FUNCTION__, type, check_item);
1805  gtk_check_menu_item_set_active(check_item, TRUE);
1806  gtk_combo_box_set_active(screen.win.sidepaneRenderComboBox, type);
1807 
1808  render_refresh_rendered_image_on_screen();
1809  isChanging = FALSE;
1810 }
1811 
1812 /* --------------------------------------------------------- */
1813 static void
1814 callbacks_units_changed(gerbv_gui_unit_t unit) {
1815  static gboolean isChanging = FALSE;
1816 
1817  if (isChanging)
1818  return;
1819 
1820  isChanging = TRUE;
1821  screen.unit = unit;
1822 
1823  if (unit == GERBV_MILS || unit == GERBV_MMS || unit == GERBV_INS) {
1824  gtk_combo_box_set_active(GTK_COMBO_BOX(screen.win.statusUnitComboBox), unit);
1825  gtk_check_menu_item_set_active(screen.win.menu_view_unit_group[unit], TRUE);
1826  }
1827 
1828  callbacks_update_ruler_scales();
1829  callbacks_update_statusbar_coordinates(screen.last_x, screen.last_y);
1830 
1831  if (screen.tool == MEASURE)
1832  callbacks_update_statusbar_measured_distance(screen.measure_last_x, screen.measure_last_y);
1833 
1834  isChanging = FALSE;
1835 }
1836 
1837 /* --------------------------------------------------------- */
1838 static void
1839 callbacks_update_ruler_scales(void) {
1840  double xStart, xEnd, yStart, yEnd;
1841 
1842  xStart = screenRenderInfo.lowerLeftX;
1843  yStart = screenRenderInfo.lowerLeftY;
1844  xEnd = screenRenderInfo.lowerLeftX + (screenRenderInfo.displayWidth / screenRenderInfo.scaleFactorX);
1845  yEnd = screenRenderInfo.lowerLeftY + (screenRenderInfo.displayHeight / screenRenderInfo.scaleFactorY);
1846  /* mils can get super crowded with large boards, but inches are too
1847  large for most boards. So, we leave mils in for now and just switch
1848  to inches if the scale factor gets too small */
1849  if (!((screen.unit == GERBV_MILS) && ((screenRenderInfo.scaleFactorX < 80) || (screenRenderInfo.scaleFactorY < 80))
1850  )) {
1851  xStart = callbacks_calculate_actual_distance(xStart);
1852  xEnd = callbacks_calculate_actual_distance(xEnd);
1853  yStart = callbacks_calculate_actual_distance(yStart);
1854  yEnd = callbacks_calculate_actual_distance(yEnd);
1855  }
1856  /* make sure the widgets actually exist before setting (in case this gets
1857  called before everything is realized */
1858  if (screen.win.hRuler)
1859  gtk_ruler_set_range(GTK_RULER(screen.win.hRuler), xStart, xEnd, 0, xEnd - xStart);
1860  /* reverse y min and max, since the ruler starts at the top */
1861  if (screen.win.vRuler)
1862  gtk_ruler_set_range(GTK_RULER(screen.win.vRuler), yEnd, yStart, 0, yEnd - yStart);
1863 }
1864 
1865 /* --------------------------------------------------------- */
1866 void
1867 callbacks_update_scrollbar_limits(void) {
1868  gerbv_render_info_t tempRenderInfo = {
1869  0, 0, 0, 0, GERBV_RENDER_TYPE_CAIRO_HIGH_QUALITY, screenRenderInfo.displayWidth, screenRenderInfo.displayHeight
1870  };
1871 
1872  GtkAdjustment* hAdjust = (GtkAdjustment*)screen.win.hAdjustment;
1873  GtkAdjustment* vAdjust = (GtkAdjustment*)screen.win.vAdjustment;
1875  hAdjust->lower = tempRenderInfo.lowerLeftX;
1876  hAdjust->page_increment = hAdjust->page_size;
1877  hAdjust->step_increment = hAdjust->page_size / 10.0;
1878  vAdjust->lower = tempRenderInfo.lowerLeftY;
1879  vAdjust->page_increment = vAdjust->page_size;
1880  vAdjust->step_increment = vAdjust->page_size / 10.0;
1881  hAdjust->upper = tempRenderInfo.lowerLeftX + (tempRenderInfo.displayWidth / tempRenderInfo.scaleFactorX);
1882  hAdjust->page_size = screenRenderInfo.displayWidth / screenRenderInfo.scaleFactorX;
1883  vAdjust->upper = tempRenderInfo.lowerLeftY + (tempRenderInfo.displayHeight / tempRenderInfo.scaleFactorY);
1884  vAdjust->page_size = screenRenderInfo.displayHeight / screenRenderInfo.scaleFactorY;
1885  callbacks_update_scrollbar_positions();
1886 }
1887 
1888 /* --------------------------------------------------------- */
1889 void
1890 callbacks_update_scrollbar_positions(void) {
1891  gdouble positionX, positionY;
1892 
1893  positionX = screenRenderInfo.lowerLeftX;
1894  if (positionX < ((GtkAdjustment*)screen.win.hAdjustment)->lower)
1895  positionX = ((GtkAdjustment*)screen.win.hAdjustment)->lower;
1896  if (positionX
1897  > (((GtkAdjustment*)screen.win.hAdjustment)->upper - ((GtkAdjustment*)screen.win.hAdjustment)->page_size))
1898  positionX =
1899  (((GtkAdjustment*)screen.win.hAdjustment)->upper - ((GtkAdjustment*)screen.win.hAdjustment)->page_size);
1900  gtk_adjustment_set_value((GtkAdjustment*)screen.win.hAdjustment, positionX);
1901 
1902  positionY = ((GtkAdjustment*)screen.win.vAdjustment)->upper - screenRenderInfo.lowerLeftY
1903  - ((GtkAdjustment*)screen.win.vAdjustment)->page_size + ((GtkAdjustment*)screen.win.vAdjustment)->lower;
1904  if (positionY < ((GtkAdjustment*)screen.win.vAdjustment)->lower)
1905  positionY = ((GtkAdjustment*)screen.win.vAdjustment)->lower;
1906  if (positionY
1907  > (((GtkAdjustment*)screen.win.vAdjustment)->upper - ((GtkAdjustment*)screen.win.vAdjustment)->page_size))
1908  positionY =
1909  (((GtkAdjustment*)screen.win.vAdjustment)->upper - ((GtkAdjustment*)screen.win.vAdjustment)->page_size);
1910  gtk_adjustment_set_value((GtkAdjustment*)screen.win.vAdjustment, positionY);
1911 }
1912 
1913 /* --------------------------------------------------------- */
1914 gboolean
1915 callbacks_scrollbar_button_released(GtkWidget* widget, GdkEventButton* event) {
1916  screen.off_x = 0;
1917  screen.off_y = 0;
1918  screen.state = NORMAL;
1919  render_refresh_rendered_image_on_screen();
1920  return FALSE;
1921 }
1922 
1923 /* --------------------------------------------------------- */
1924 gboolean
1925 callbacks_scrollbar_button_pressed(GtkWidget* widget, GdkEventButton* event) {
1926  // screen.last_x = ((GtkAdjustment *)screen.win.hAdjustment)->value;
1927  screen.state = SCROLLBAR;
1928  return FALSE;
1929 }
1930 
1931 /* --------------------------------------------------------- */
1932 void
1933 callbacks_hadjustment_value_changed(GtkAdjustment* adjustment, gpointer user_data) {
1934  /* make sure we're actually using the scrollbar to make sure we don't reset
1935  lowerLeftX during a scrollbar redraw during something else */
1936  if (screen.state == SCROLLBAR) {
1937  screenRenderInfo.lowerLeftX = gtk_adjustment_get_value(adjustment);
1938  }
1939 }
1940 
1941 /* --------------------------------------------------------- */
1942 void
1943 callbacks_vadjustment_value_changed(GtkAdjustment* adjustment, gpointer user_data) {
1944  /* make sure we're actually using the scrollbar to make sure we don't reset
1945  lowerLeftY during a scrollbar redraw during something else */
1946  if (screen.state == SCROLLBAR) {
1947  screenRenderInfo.lowerLeftY =
1948  adjustment->upper - (gtk_adjustment_get_value(adjustment) + adjustment->page_size) + adjustment->lower;
1949  }
1950 }
1951 
1952 /* --------------------------------------------------------- */
1953 static void
1954 callbacks_layer_tree_visibility_toggled(gint index) {
1955  mainProject->file[index]->isVisible = !mainProject->file[index]->isVisible;
1956 
1957  callbacks_update_layer_tree();
1958  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
1959  render_refresh_rendered_image_on_screen();
1960  } else {
1961  render_recreate_composite_surface(screen.drawing_area);
1962  callbacks_force_expose_event_for_screen();
1963  }
1964 }
1965 
1966 /* --------------------------------------------------------- */
1967 static gint
1968 callbacks_get_col_num_from_tree_view_col(GtkTreeViewColumn* col) {
1969  GList* cols;
1970  gint num;
1971 
1972  g_return_val_if_fail(col != NULL, -1);
1973  g_return_val_if_fail(col->tree_view != NULL, -1);
1974  cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(col->tree_view));
1975  num = g_list_index(cols, (gpointer)col);
1976  g_list_free(cols);
1977  return num;
1978 }
1979 
1980 /* --------------------------------------------------------- */
1981 void
1982 callbacks_add_layer_button_clicked(GtkButton* button, gpointer user_data) {
1983  callbacks_open_activate(NULL, NULL);
1984 }
1985 
1986 /* --------------------------------------------------------- */
1987 void
1988 callbacks_unselect_all_tool_buttons(void) {}
1989 
1990 void
1991 callbacks_switch_to_normal_tool_cursor(gint toolNumber) {
1992  GdkWindow* drawing_area_window = screen.drawing_area->window;
1993  GdkCursor* cursor;
1994 
1995  switch (toolNumber) {
1996  case POINTER: gdk_window_set_cursor(drawing_area_window, GERBV_DEF_CURSOR); break;
1997  case PAN:
1998  cursor = gdk_cursor_new(GDK_FLEUR);
1999  gdk_window_set_cursor(drawing_area_window, cursor);
2000  gdk_cursor_destroy(cursor);
2001  break;
2002  case ZOOM:
2003  cursor = gdk_cursor_new(GDK_SIZING);
2004  gdk_window_set_cursor(drawing_area_window, cursor);
2005  gdk_cursor_destroy(cursor);
2006  break;
2007  case MEASURE:
2008  cursor = gdk_cursor_new(GDK_CROSSHAIR);
2009  gdk_window_set_cursor(drawing_area_window, cursor);
2010  gdk_cursor_destroy(cursor);
2011  break;
2012  default: break;
2013  }
2014 }
2015 
2016 /* --------------------------------------------------------- */
2017 void
2018 callbacks_switch_to_correct_cursor(void) {
2019  GdkWindow* drawing_area_window = screen.drawing_area->window;
2020  GdkCursor* cursor;
2021 
2022  if (screen.state == IN_MOVE) {
2023  cursor = gdk_cursor_new(GDK_FLEUR);
2024  gdk_window_set_cursor(drawing_area_window, cursor);
2025  gdk_cursor_destroy(cursor);
2026  return;
2027  } else if (screen.state == IN_ZOOM_OUTLINE) {
2028  cursor = gdk_cursor_new(GDK_SIZING);
2029  gdk_window_set_cursor(drawing_area_window, cursor);
2030  gdk_cursor_destroy(cursor);
2031  return;
2032  }
2033  callbacks_switch_to_normal_tool_cursor(screen.tool);
2034 }
2035 
2036 /* --------------------------------------------------------- */
2037 void
2038 callbacks_change_tool(GtkButton* button, gpointer user_data) {
2039  gint toolNumber = GPOINTER_TO_INT(user_data);
2040 
2041  /* make sure se don't get caught in endless recursion here */
2042  if (screen.win.updatingTools)
2043  return;
2044  screen.win.updatingTools = TRUE;
2045  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonPointer), FALSE);
2046  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonPan), FALSE);
2047  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonZoom), FALSE);
2048  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonMeasure), FALSE);
2049  switch (toolNumber) {
2050  case POINTER:
2051  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonPointer), TRUE);
2052  screen.tool = POINTER;
2053  screen.state = NORMAL;
2054  utf8_strncpy(
2055  screen.statusbar.diststr,
2056  _("Click to select objects in the active layer. "
2057  "Middle click and drag to pan."),
2058  MAX_DISTLEN
2059  );
2060  break;
2061  case PAN:
2062  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonPan), TRUE);
2063  screen.tool = PAN;
2064  screen.state = NORMAL;
2065  utf8_strncpy(
2066  screen.statusbar.diststr, _("Click and drag to pan. Right click and drag to zoom."), MAX_DISTLEN
2067  );
2068  break;
2069  case ZOOM:
2070  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonZoom), TRUE);
2071  screen.tool = ZOOM;
2072  screen.state = NORMAL;
2073  utf8_strncpy(
2074  screen.statusbar.diststr, _("Click and drag to zoom in. Shift+click to zoom out."), MAX_DISTLEN
2075  );
2076  break;
2077 
2078  case MEASURE:
2079  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(screen.win.toolButtonMeasure), TRUE);
2080  screen.tool = MEASURE;
2081  screen.state = NORMAL;
2082  utf8_strncpy(
2083  screen.statusbar.diststr, _("Click and drag to measure a distance or select two apertures."),
2084  MAX_DISTLEN
2085  );
2086 
2087  /* To not show previous measure drag-line */
2088  screen.measure_start_x = 0;
2089  screen.measure_start_y = 0;
2090  screen.measure_stop_x = 0;
2091  screen.measure_stop_y = 0;
2092 
2093  /* If two items are selected, measure they distance */
2094  if (selection_length(&screen.selectionInfo) == 2) {
2095  gerbv_selection_item_t item[2];
2096  gerbv_net_t* net[2];
2097 
2098  item[0] = selection_get_item_by_index(&screen.selectionInfo, 0);
2099  item[1] = selection_get_item_by_index(&screen.selectionInfo, 1);
2100  net[0] = item[0].net;
2101  net[1] = item[1].net;
2102 
2103  if ((net[0]->aperture_state == net[1]->aperture_state)
2104  && (net[0]->aperture_state == GERBV_APERTURE_STATE_FLASH)) {
2105  screen.measure_start_x = net[0]->stop_x;
2106  screen.measure_start_y = net[0]->stop_y;
2108  &screen.measure_start_x, &screen.measure_start_y, item[0].image, mainProject
2109  );
2110 
2111  screen.measure_stop_x = net[1]->stop_x;
2112  screen.measure_stop_y = net[1]->stop_y;
2114  &screen.measure_stop_x, &screen.measure_stop_y, item[1].image, mainProject
2115  );
2116 
2118  }
2119  }
2120  break;
2121  default: break;
2122  }
2123 
2124  callbacks_switch_to_normal_tool_cursor(toolNumber);
2126  screen.win.updatingTools = FALSE;
2127  callbacks_force_expose_event_for_screen();
2128 }
2129 
2130 /* --------------------------------------------------------- */
2131 static void
2132 callbacks_select_layer_row(gint rowIndex) {
2133  GtkTreeSelection* selection;
2134  GtkTreeIter iter;
2135  GtkListStore* list_store = (GtkListStore*)gtk_tree_view_get_model((GtkTreeView*)screen.win.layerTree);
2136 
2137  selection = gtk_tree_view_get_selection((GtkTreeView*)screen.win.layerTree);
2138 
2139  if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store), &iter, NULL, rowIndex)) {
2140  gtk_tree_selection_select_iter(selection, &iter);
2141  }
2142 }
2143 
2144 /* --------------------------------------------------------- */
2150 static gint
2152  GtkTreeSelection* selection;
2153  GtkTreeIter iter;
2154  GtkListStore* list_store = (GtkListStore*)gtk_tree_view_get_model((GtkTreeView*)screen.win.layerTree);
2155  gint index = -1, i = 0;
2156 
2157  /* This will only work in single or browse selection mode! */
2158  selection = gtk_tree_view_get_selection((GtkTreeView*)screen.win.layerTree);
2159  if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
2160  while (gtk_tree_model_iter_nth_child((GtkTreeModel*)list_store, &iter, NULL, i)) {
2161  if (gtk_tree_selection_iter_is_selected(selection, &iter)) {
2162  return i;
2163  }
2164  i++;
2165  }
2166  }
2167  return index;
2168 }
2169 
2170 /* --------------------------------------------------------- */
2171 void
2172 callbacks_remove_layer_button_clicked(GtkButton* button, gpointer user_data) {
2173  gint index = callbacks_get_selected_row_index();
2174 
2175  if ((index >= 0) && (index <= mainProject->last_loaded)) {
2176  render_remove_selected_objects_belonging_to_layer(&screen.selectionInfo, mainProject->file[index]->image);
2177  update_selected_object_message(FALSE);
2178 
2179  gerbv_unload_layer(mainProject, index);
2180  callbacks_update_layer_tree();
2181 
2182  index = MAX(0, index - 1);
2183  callbacks_select_layer_row(index);
2184 
2185  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
2186  render_refresh_rendered_image_on_screen();
2187  } else {
2188  render_recreate_composite_surface(screen.drawing_area);
2189  callbacks_force_expose_event_for_screen();
2190  }
2191  }
2192 }
2193 
2194 /* --------------------------------------------------------- */
2195 void
2196 callbacks_move_layer_down_menu_activate(GtkMenuItem* menuitem, gpointer user_data) {
2197  callbacks_move_layer_down_button_clicked(NULL, NULL);
2198  gtk_widget_grab_focus(screen.win.layerTree);
2199 }
2200 
2201 /* --------------------------------------------------------- */
2202 void
2203 callbacks_move_layer_down_button_clicked(GtkButton* button, gpointer user_data) {
2204  gint index = callbacks_get_selected_row_index();
2205  if (index < 0) {
2206  show_no_layers_warning();
2207  return;
2208  }
2209 
2210  if (index < mainProject->last_loaded) {
2211  gerbv_change_layer_order(mainProject, index, index + 1);
2212  callbacks_update_layer_tree();
2213  callbacks_select_layer_row(index + 1);
2214 
2215  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
2216  render_refresh_rendered_image_on_screen();
2217  } else {
2218  render_recreate_composite_surface(screen.drawing_area);
2219  callbacks_force_expose_event_for_screen();
2220  }
2221  }
2222 }
2223 
2224 /* --------------------------------------------------------- */
2225 void
2226 callbacks_move_layer_up_menu_activate(GtkMenuItem* menuitem, gpointer user_data) {
2227  callbacks_move_layer_up_button_clicked(NULL, NULL);
2228  gtk_widget_grab_focus(screen.win.layerTree);
2229 }
2230 
2231 /* --------------------------------------------------------- */
2232 void
2233 callbacks_move_layer_up_button_clicked(GtkButton* button, gpointer user_data) {
2234  gint index = callbacks_get_selected_row_index();
2235  if (index < 0) {
2236  show_no_layers_warning();
2237  return;
2238  }
2239  if (index > 0) {
2240  gerbv_change_layer_order(mainProject, index, index - 1);
2241  callbacks_update_layer_tree();
2242  callbacks_select_layer_row(index - 1);
2243  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
2244  render_refresh_rendered_image_on_screen();
2245  } else {
2246  render_recreate_composite_surface(screen.drawing_area);
2247  callbacks_force_expose_event_for_screen();
2248  }
2249  }
2250 }
2251 
2252 /* --------------------------------------------------------- */
2253 void
2254 callbacks_layer_tree_row_inserted(GtkTreeModel* tree_model, GtkTreePath* path, GtkTreeIter* oIter, gpointer user_data) {
2255  gint *indices = NULL, oldPosition, newPosition;
2256 
2257  if ((!screen.win.treeIsUpdating) && (path != NULL)) {
2258  indices = gtk_tree_path_get_indices(path);
2259  if (indices) {
2260  newPosition = indices[0];
2261  oldPosition = callbacks_get_selected_row_index();
2262  /* compensate for the fact that the old row has already
2263  been removed */
2264  if (oldPosition < newPosition)
2265  newPosition--;
2266  else
2267  oldPosition--;
2268  gerbv_change_layer_order(mainProject, oldPosition, newPosition);
2269 
2270  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
2271  render_refresh_rendered_image_on_screen();
2272  } else {
2273  render_recreate_composite_surface(screen.drawing_area);
2274  callbacks_force_expose_event_for_screen();
2275  }
2276  /* select the new line */
2277  GtkTreeSelection* selection;
2278  GtkTreeIter iter;
2279  GtkListStore* list_store = (GtkListStore*)gtk_tree_view_get_model((GtkTreeView*)screen.win.layerTree);
2280 
2281  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(screen.win.layerTree));
2282  if (gtk_tree_model_get_iter((GtkTreeModel*)list_store, &iter, path))
2283  gtk_tree_selection_select_iter(selection, &iter);
2284  }
2285  }
2286 }
2287 
2288 /* --------------------------------------------------------- */
2289 void
2290 callbacks_show_color_picker_dialog(gint index) {
2291  screen.win.colorSelectionDialog = NULL;
2292  GtkColorSelectionDialog* cs = (GtkColorSelectionDialog*)gtk_color_selection_dialog_new(_("Select a color"));
2293  GtkColorSelection* colorsel = (GtkColorSelection*)cs->colorsel;
2294 
2295  screen.win.colorSelectionDialog = (GtkWidget*)cs;
2296  screen.win.colorSelectionIndex = index;
2297  if (index >= 0)
2298  gtk_color_selection_set_current_color(colorsel, &mainProject->file[index]->color);
2299  else
2300  gtk_color_selection_set_current_color(colorsel, &mainProject->background);
2301  if ((screenRenderInfo.renderType >= GERBV_RENDER_TYPE_CAIRO_NORMAL) && (index >= 0)) {
2302  gtk_color_selection_set_has_opacity_control(colorsel, TRUE);
2303  gtk_color_selection_set_current_alpha(colorsel, mainProject->file[index]->alpha);
2304  }
2305  gtk_widget_show_all((GtkWidget*)cs);
2306  if (gtk_dialog_run((GtkDialog*)cs) == GTK_RESPONSE_OK) {
2307  GtkColorSelection* colorsel = (GtkColorSelection*)cs->colorsel;
2308  gint rowIndex = screen.win.colorSelectionIndex;
2309 
2310  if (index >= 0) {
2311  gtk_color_selection_get_current_color(colorsel, &mainProject->file[rowIndex]->color);
2312  gdk_colormap_alloc_color(gdk_colormap_get_system(), &mainProject->file[rowIndex]->color, FALSE, TRUE);
2313  } else {
2314  gtk_color_selection_get_current_color(colorsel, &mainProject->background);
2315  gdk_colormap_alloc_color(gdk_colormap_get_system(), &mainProject->background, FALSE, TRUE);
2316  }
2317  if ((screenRenderInfo.renderType >= GERBV_RENDER_TYPE_CAIRO_NORMAL) && (index >= 0)) {
2318  mainProject->file[rowIndex]->alpha = gtk_color_selection_get_current_alpha(colorsel);
2319  }
2320 
2321  callbacks_update_layer_tree();
2322  render_refresh_rendered_image_on_screen();
2323  }
2324  gtk_widget_destroy((GtkWidget*)cs);
2325  screen.win.colorSelectionDialog = NULL;
2326 }
2327 
2328 /* --------------------------------------------------------- */
2329 void
2330 callbacks_invert_layer_clicked(GtkButton* button, gpointer user_data) {
2331  gint index = callbacks_get_selected_row_index();
2332  if (index < 0) {
2333  show_no_layers_warning();
2334  return;
2335  }
2337  render_refresh_rendered_image_on_screen();
2338  callbacks_update_layer_tree();
2339 }
2340 
2341 /* --------------------------------------------------------- */
2342 void
2343 callbacks_change_layer_color_clicked(GtkButton* button, gpointer user_data) {
2344  gint index = callbacks_get_selected_row_index();
2345  if (index < 0) {
2346  show_no_layers_warning();
2347  return;
2348  }
2349  callbacks_show_color_picker_dialog(index);
2350 }
2351 
2352 void
2353 callbacks_change_background_color_clicked(GtkButton* button, gpointer user_data) {
2354  callbacks_show_color_picker_dialog(-1);
2355 }
2356 
2357 /* --------------------------------------------------------------------------- */
2358 void
2359 callbacks_reload_layer_clicked(GtkButton* button, gpointer user_data) {
2360  gint index = callbacks_get_selected_row_index();
2361 
2362  if (index < 0) {
2363  show_no_layers_warning();
2364  return;
2365  }
2366 
2367  render_remove_selected_objects_belonging_to_layer(&screen.selectionInfo, mainProject->file[index]->image);
2368  update_selected_object_message(FALSE);
2369 
2370  gerbv_revert_file(mainProject, index);
2371  render_refresh_rendered_image_on_screen();
2372  callbacks_update_layer_tree();
2373 }
2374 
2375 void
2376 callbacks_change_layer_edit_clicked(GtkButton* button, gpointer userData) {
2377  gint index = callbacks_get_selected_row_index();
2378  gerbv_fileinfo_t** files = mainProject->file;
2379  gerbv_user_transformation_t** transforms;
2380  int i, j;
2381 
2382  if (index < 0) {
2383  show_no_layers_warning();
2384  return;
2385  }
2386 
2387  /* last_loaded == 0 if only one file is loaded */
2388  transforms = g_new(
2390  mainProject->last_loaded + 2 /* layer + NULL */ + 1 /* if selected layer is visible */
2391  );
2392 
2393  /* [0] is selected layer */
2394  transforms[0] = &mainProject->file[index]->transform;
2395 
2396  /* Get visible Gerber files transformations */
2397  j = 1; /* [0] is alerady used */
2398  for (i = 0; i <= mainProject->last_loaded; i++) {
2399  if (files[i] && files[i]->isVisible)
2400  transforms[j++] = &files[i]->transform;
2401  }
2402 
2403  /* Terminate array with NULL */
2404  transforms[j] = NULL;
2405 
2406  interface_show_layer_edit_dialog(transforms, screen.unit);
2407  g_free(transforms);
2408  render_refresh_rendered_image_on_screen();
2409  callbacks_update_layer_tree();
2410 }
2411 
2412 /* --------------------------------------------------------------------------- */
2413 void
2414 callbacks_change_layer_format_clicked(GtkButton* button, gpointer user_data) {
2415  gerbv_HID_Attribute* attr = NULL;
2416  int n = 0;
2417  int i;
2418  gerbv_HID_Attr_Val* results = NULL;
2419  gint index = callbacks_get_selected_row_index();
2420  gchar* type;
2421  gint rc;
2422  if (index < 0) {
2423  show_no_layers_warning();
2424  return;
2425  }
2426  dprintf("%s(): index = %d\n", __FUNCTION__, index);
2427  attr = mainProject->file[index]->image->info->attr_list;
2428  n = mainProject->file[index]->image->info->n_attr;
2429  type = mainProject->file[index]->image->info->type;
2430  if (type == NULL)
2431  type = N_("Unknown type");
2432 
2433  if (attr == NULL || n == 0) {
2435  _("This file type does not currently have any editable features"),
2436  _("Format editing is currently only supported for Excellon drill file formats."), FALSE, NULL
2437  );
2438  return;
2439  }
2440 
2441  dprintf("%s(): n = %d, attr = %p\n", __FUNCTION__, n, attr);
2442  if (n > 0) {
2443  if (mainProject->file[index]->layer_dirty) {
2445  _("This layer has changed!"),
2446  _("Editing the file type will reload the layer, "
2447  "destroying your changes. Click OK to edit "
2448  "the file type and destroy your changes, "
2449  "or Cancel to leave."),
2450  TRUE, NULL, GTK_STOCK_OK, GTK_STOCK_CANCEL
2451  );
2452  if (rc == 0)
2453  return; /* Return if user hit Cancel */
2454  }
2455 
2456  results = (gerbv_HID_Attr_Val*)malloc(n * sizeof(gerbv_HID_Attr_Val));
2457  if (results == NULL)
2458  GERB_FATAL_ERROR("%s(): malloc failed", __FUNCTION__);
2459 
2460  /* non-zero means cancel was picked */
2461  if (attribute_interface_dialog(attr, n, results, _("Edit file format"), _(type))) {
2462  return;
2463  }
2464  }
2465 
2466  dprintf("%s(): reloading layer\n", __func__);
2467  gerbv_revert_file(mainProject, index);
2468 
2469  for (i = 0; i < n; i++) {
2470  if (results[i].str_value)
2471  free(results[i].str_value);
2472  }
2473 
2474  if (results)
2475  free(results);
2476  render_refresh_rendered_image_on_screen();
2477  callbacks_update_layer_tree();
2478 }
2479 
2480 /* --------------------------------------------------------------------------- */
2481 gboolean
2482 callbacks_file_drop_event(
2483  GtkWidget* widget, GdkDragContext* dc, gint x, gint y, GtkSelectionData* data, guint info, guint time, gpointer p
2484 ) {
2485  gchar **uris, **uri;
2486  GSList* fns = NULL;
2487 
2488  uris = gtk_selection_data_get_uris(data);
2489  if (!uris)
2490  return FALSE;
2491 
2492  for (uri = uris; *uri; uri++) {
2493  const char* prefix_str =
2494 #ifdef WIN32
2495  "file:///";
2496 #else
2497  "file://";
2498 #endif
2499  if (g_strrstr(*uri, prefix_str) == *uri)
2500  fns = g_slist_append(fns, g_uri_unescape_string(*uri + strlen(prefix_str), NULL));
2501  }
2502 
2503  open_files(fns);
2504  g_slist_free_full(fns, g_free);
2505  g_strfreev(uris);
2506 
2507  return TRUE;
2508 }
2509 
2510 /* --------------------------------------------------------------------------- */
2511 gboolean
2512 callbacks_layer_tree_key_press(GtkWidget* widget, GdkEventKey* event, gpointer user_data) {
2513  /* if space is pressed while a color picker icon is in focus,
2514  show the color picker dialog. */
2515 
2516  if (event->keyval == GDK_space) {
2517  GtkTreeView* tree;
2518  GtkTreePath* path;
2519  GtkTreeViewColumn* col;
2520  gint* indices;
2521  gint idx;
2522 
2523  tree = (GtkTreeView*)screen.win.layerTree;
2524  gtk_tree_view_get_cursor(tree, &path, &col);
2525  if (path) {
2526  indices = gtk_tree_path_get_indices(path);
2527  if (indices) {
2528  idx = callbacks_get_col_num_from_tree_view_col(col);
2529  if ((idx == 1) && (indices[0] <= mainProject->last_loaded)) {
2530  callbacks_show_color_picker_dialog(indices[0]);
2531  }
2532  }
2533  gtk_tree_path_free(path);
2534  }
2535  }
2536  /* by default propagate the key press */
2537  return FALSE;
2538 }
2539 
2540 /* --------------------------------------------------------------------------- */
2541 gboolean
2542 callbacks_layer_tree_button_press(GtkWidget* widget, GdkEventButton* event, gpointer user_data) {
2543  GtkTreePath* path;
2544  GtkTreeIter iter;
2545  GtkTreeViewColumn* col;
2546  gint x, y;
2547  gint* indices;
2548 
2549  GtkListStore* list_store = (GtkListStore*)gtk_tree_view_get_model((GtkTreeView*)screen.win.layerTree);
2550 
2551  if (event->button == 1) {
2552  if (gtk_tree_view_get_path_at_pos((GtkTreeView*)widget, event->x, event->y, &path, &col, &x, &y)
2553  && gtk_tree_model_get_iter((GtkTreeModel*)list_store, &iter, path)) {
2554  indices = gtk_tree_path_get_indices(path);
2555  if (indices && (indices[0] <= mainProject->last_loaded)) {
2556  switch (callbacks_get_col_num_from_tree_view_col(col)) {
2557  case 0:
2558  callbacks_select_layer_row(indices[0]);
2559  callbacks_layer_tree_visibility_toggled(indices[0]);
2560  return TRUE;
2561  case 1:
2562  callbacks_show_color_picker_dialog(indices[0]);
2563  /* don't propagate the signal, since drag and drop can
2564  sometimes activated during color selection */
2565  return TRUE;
2566  }
2567  }
2568  }
2569  }
2570  /* don't pop up the menu if we don't have any loaded files */
2571  else if ((event->button == 3) && (mainProject->last_loaded >= 0)) {
2572  gtk_menu_popup(GTK_MENU(screen.win.layerTreePopupMenu), NULL, NULL, NULL, NULL, event->button, event->time);
2573  }
2574 
2575  /* always allow the click to propagate and make sure the line is activated */
2576  return FALSE;
2577 }
2578 
2579 /* --------------------------------------------------------------------------- */
2580 void
2581 callbacks_update_layer_tree(void) {
2582  GtkListStore* list_store = (GtkListStore*)gtk_tree_view_get_model((GtkTreeView*)screen.win.layerTree);
2583  gint idx;
2584  GtkTreeIter iter;
2585  GtkTreeSelection* selection;
2586  gint oldSelectedRow;
2587 
2588  if (screen.win.treeIsUpdating)
2589  return;
2590 
2591  screen.win.treeIsUpdating = TRUE;
2592 
2593  oldSelectedRow = callbacks_get_selected_row_index();
2594  if (oldSelectedRow < 0)
2595  oldSelectedRow = 0;
2596 
2597  gtk_list_store_clear(list_store);
2598 
2599  for (idx = 0; idx <= mainProject->last_loaded; idx++) {
2600  GdkPixbuf * pixbuf, *blackPixbuf;
2601  unsigned char red, green, blue, alpha;
2602  guint32 color;
2603  gchar* layerName;
2604  gerbv_fileinfo_t* file;
2605 
2606  file = mainProject->file[idx];
2607  if (!file)
2608  continue;
2609 
2610  red = (unsigned char)(file->color.red * 255 / G_MAXUINT16);
2611  green = (unsigned char)(file->color.green * 255 / G_MAXUINT16);
2612  blue = (unsigned char)(file->color.blue * 255 / G_MAXUINT16);
2613  alpha = (unsigned char)(file->alpha * 255 / G_MAXUINT16);
2614 
2615  color = red * (256 * 256 * 256) + green * (256 * 256) + blue * 256 + alpha;
2616  pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 20, 15);
2617  gdk_pixbuf_fill(pixbuf, color);
2618 
2619  blackPixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 20, 15);
2620  color = 100 * (256 * 256 * 256) + 100 * (256 * 256) + 100 * 256 + 150;
2621  gdk_pixbuf_fill(blackPixbuf, color);
2622 
2623  /* copy the color area into the black pixbuf */
2624  gdk_pixbuf_copy_area(pixbuf, 1, 1, 18, 13, blackPixbuf, 1, 1);
2625  g_object_unref(pixbuf);
2626 
2627  gtk_list_store_append(list_store, &iter);
2628 
2629  gchar startChar[2], *modifiedCode;
2630  /* terminate the letter string */
2631  startChar[1] = 0;
2632 
2633  gint numberOfModifications = 0;
2634  if (file->transform.inverted) {
2635  startChar[0] = 'I';
2636  numberOfModifications++;
2637  }
2638  if (file->transform.mirrorAroundX || file->transform.mirrorAroundY) {
2639  startChar[0] = 'M';
2640  numberOfModifications++;
2641  }
2642  if (fabs(file->transform.translateX) > GERBV_PRECISION_LINEAR_INCH
2643  || fabs(file->transform.translateY) > GERBV_PRECISION_LINEAR_INCH) {
2644  startChar[0] = 'T';
2645  numberOfModifications++;
2646  }
2647  if (fabs(file->transform.scaleX - 1) > GERBV_PRECISION_LINEAR_INCH
2648  || fabs(file->transform.scaleY - 1) > GERBV_PRECISION_LINEAR_INCH) {
2649  startChar[0] = 'S';
2650  numberOfModifications++;
2651  }
2652  if (fabs(file->transform.rotation) > GERBV_PRECISION_ANGLE_RAD) {
2653  startChar[0] = 'R';
2654  numberOfModifications++;
2655  }
2656  if (numberOfModifications > 1)
2657  startChar[0] = '*';
2658  if (numberOfModifications == 0)
2659  modifiedCode = g_strdup("");
2660  else
2661  modifiedCode = g_strdup(startChar);
2662 
2663  /* Display any unsaved layers differently to show the user they
2664  * are unsaved */
2665  if (file->layer_dirty == TRUE) {
2666  /* The layer has unsaved changes in it. Show layer name
2667  * in italics. */
2668  layerName = g_strconcat("*", "<i>", file->name, "</i>", NULL);
2669  } else {
2670  /* Layer is clean. Show layer name using normal font. */
2671  layerName = g_strdup(file->name);
2672  }
2673 
2674  gtk_list_store_set(list_store, &iter, 0, file->isVisible, 1, blackPixbuf, 2, layerName, 3, modifiedCode, -1);
2675  g_free(layerName);
2676  g_free(modifiedCode);
2677  /* pixbuf has a refcount of 2 now, as the list store has added its own reference */
2678  g_object_unref(blackPixbuf);
2679  }
2680 
2681  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(screen.win.layerTree));
2682 
2683  /* if no line is selected yet, select the first row (if it has data) */
2684  /* or, select the line that was previously selected */
2685 
2686  if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
2687  if (gtk_tree_model_iter_nth_child((GtkTreeModel*)list_store, &iter, NULL, oldSelectedRow)) {
2688  gtk_tree_selection_select_iter(selection, &iter);
2689  }
2690  }
2691  gboolean showItems = (mainProject->last_loaded >= 0);
2692  gtk_widget_set_sensitive(screen.win.curLayerMenuItem, showItems);
2693  gtk_widget_set_sensitive(screen.win.curAnalyzeMenuItem, showItems);
2694  gtk_widget_set_sensitive(screen.win.curEditMenuItem, showItems);
2695  for (unsigned int i = 0; i < G_N_ELEMENTS(screen.win.curFileMenuItem); i++) {
2696  gtk_widget_set_sensitive(screen.win.curFileMenuItem[i], showItems);
2697  }
2698  screen.win.treeIsUpdating = FALSE;
2699 }
2700 
2701 /* --------------------------------------------------------------------------- */
2702 void
2703 callbacks_display_object_properties_clicked(GtkButton* button, gpointer user_data) {
2704  gint index = callbacks_get_selected_row_index();
2705  guint i;
2706 
2707  if (index < 0 || selection_length(&screen.selectionInfo) == 0) {
2709  _("No object is currently selected"),
2710  _("Objects must be selected using the pointer tool "
2711  "before you can view the object properties."),
2712  FALSE, NULL
2713  );
2714  return;
2715  }
2716 
2717  for (i = 0; i < selection_length(&screen.selectionInfo); i++) {
2718  gerbv_selection_item_t sItem = selection_get_item_by_index(&screen.selectionInfo, i);
2719 
2720  gerbv_net_t* net = sItem.net;
2721  gerbv_image_t* image = sItem.image;
2722 
2724  /* Spacing for a pretty display */
2725  if (i != 0)
2726  g_message(" ");
2727 
2728  g_message(_("Object type: Polygon"));
2729  parea_report(net, image, mainProject);
2730  net_layer_file_report(net, image, mainProject);
2731  } else {
2732  switch (net->aperture_state) {
2733 
2736  /* Spacing for a pretty display */
2737  if (i != 0)
2738  g_message(" ");
2739  break;
2740 
2741  default: break;
2742  }
2743 
2744  aperture_state_report(net, image, mainProject);
2745  }
2746  }
2747  /* Use separator for different report requests */
2748  g_message("---------------------------------------");
2749 }
2750 
2751 void
2752 callbacks_support_benchmark(gerbv_render_info_t* renderInfo) {
2753  int i;
2754  time_t start, now;
2755  GdkPixmap* renderedPixmap = gdk_pixmap_new(NULL, renderInfo->displayWidth, renderInfo->displayHeight, 24);
2756 
2757  // start by running the GDK (fast) rendering test
2758  i = 0;
2759  start = time(NULL);
2760  now = start;
2761  while (now - 30 < start) {
2762  i++;
2763  dprintf("Benchmark(): Starting redraw #%d\n", i);
2764  gerbv_render_to_pixmap_using_gdk(mainProject, renderedPixmap, renderInfo, NULL, NULL);
2765  now = time(NULL);
2766  dprintf("Elapsed time = %ld seconds\n", (long int)(now - start));
2767  }
2768  g_message(
2769  _("FAST (=GDK) mode benchmark: %d redraws "
2770  "in %ld seconds (%g redraws/second)"),
2771  i, (long int)(now - start), (double)i / (double)(now - start)
2772  );
2773  gdk_pixmap_unref(renderedPixmap);
2774 
2775  // run the cairo (normal) render mode
2776  i = 0;
2777  start = time(NULL);
2778  now = start;
2780  while (now - 30 < start) {
2781  i++;
2782  dprintf("Benchmark(): Starting redraw #%d\n", i);
2783  cairo_surface_t* cSurface =
2784  cairo_image_surface_create(CAIRO_FORMAT_ARGB32, renderInfo->displayWidth, renderInfo->displayHeight);
2785  cairo_t* cairoTarget = cairo_create(cSurface);
2786  gerbv_render_all_layers_to_cairo_target(mainProject, cairoTarget, renderInfo);
2787  cairo_destroy(cairoTarget);
2788  cairo_surface_destroy(cSurface);
2789  now = time(NULL);
2790  dprintf("Elapsed time = %ld seconds\n", (long int)(now - start));
2791  }
2792  g_message(
2793  _("NORMAL (=Cairo) mode benchmark: %d redraws "
2794  "in %ld seconds (%g redraws/second)"),
2795  i, (long int)(now - start), (double)i / (double)(now - start)
2796  );
2797 }
2798 
2799 /* --------------------------------------------------------------------------- */
2800 void
2801 callbacks_benchmark_clicked(GtkButton* button, gpointer user_data) {
2802  // prepare render size and options (canvas size width and height are last 2 variables)
2803  gerbv_render_info_t renderInfo = { 1.0, 1.0, 0, 0, GERBV_RENDER_TYPE_GDK, 640, 480 };
2804 
2806  _("Performance benchmark"),
2807  _("Application will be unresponsive for 1 minute! "
2808  "Run performance benchmark?"),
2809  FALSE, NULL, GTK_STOCK_OK, GTK_STOCK_CANCEL
2810  ))
2811  return;
2812 
2813  GERB_COMPILE_WARNING(_("Running full zoom benchmark..."));
2814  while (g_main_context_iteration(NULL, FALSE)) {} // update log widget
2815 
2816  // autoscale the image for now...maybe we don't want to do this in order to
2817  // allow benchmarking of different zoom levels?
2819  callbacks_support_benchmark(&renderInfo);
2820 
2821  GERB_COMPILE_WARNING(_("Running x5 zoom benchmark..."));
2822  while (g_main_context_iteration(NULL, FALSE)) {} // update log widget
2823 
2824  renderInfo.lowerLeftX += (screenRenderInfo.displayWidth / screenRenderInfo.scaleFactorX) / 3.0;
2825  renderInfo.lowerLeftY += (screenRenderInfo.displayHeight / screenRenderInfo.scaleFactorY) / 3.0;
2826  renderInfo.scaleFactorX *= 5;
2827  renderInfo.scaleFactorY *= 5;
2828  callbacks_support_benchmark(&renderInfo);
2829 }
2830 
2831 /* --------------------------------------------------------------------------- */
2832 void
2833 callbacks_edit_object_properties_clicked(GtkButton* button, gpointer user_data) {}
2834 
2835 /* --------------------------------------------------------------------------- */
2836 void
2837 callbacks_live_edit(GtkWidget* button, gpointer user_data) {
2838  GtkDialog* toplevel = GTK_DIALOG(gtk_widget_get_toplevel(button));
2839  gtk_dialog_response(toplevel, GTK_RESPONSE_APPLY);
2840 }
2841 
2842 /* --------------------------------------------------------------------------- */
2843 void
2844 callbacks_move_objects_clicked(GtkButton* button, gpointer user_data) {
2845  /* for testing, just hard code in some translations here */
2846  gerbv_image_move_selected_objects(screen.selectionInfo.selectedNodeArray, -0.050, 0.050);
2847  callbacks_update_layer_tree();
2848  selection_clear(&screen.selectionInfo);
2849  update_selected_object_message(FALSE);
2850  render_refresh_rendered_image_on_screen();
2851 }
2852 
2853 /* --------------------------------------------------------------------------- */
2854 void
2855 callbacks_reduce_object_area_clicked(GtkButton* button, gpointer user_data) {
2856  /* for testing, just hard code in some parameters */
2857  gerbv_image_reduce_area_of_selected_objects(screen.selectionInfo.selectedNodeArray, 0.20, 3, 3, 0.01);
2858  selection_clear(&screen.selectionInfo);
2859  update_selected_object_message(FALSE);
2860  render_refresh_rendered_image_on_screen();
2861 }
2862 
2863 /* --------------------------------------------------------------------------- */
2864 void
2865 callbacks_delete_objects_clicked(GtkButton* button, gpointer user_data) {
2866  if (selection_length(&screen.selectionInfo) == 0) {
2868  _("No object is currently selected"),
2869  _("Objects must be selected using the pointer tool "
2870  "before they can be deleted."),
2871  FALSE, NULL
2872  );
2873  return;
2874  }
2875 
2876  gint index = callbacks_get_selected_row_index();
2877  if (index < 0)
2878  return;
2879 
2882  _("Do you want to permanently delete "
2883  "the selected objects from <i>visible</i> layers?"),
2884  _("Gerbv currently has no undo function, so "
2885  "this action cannot be undone. This action "
2886  "will not change the saved file unless you "
2887  "save the file afterwards."),
2888  TRUE, &(mainProject->check_before_delete), GTK_STOCK_DELETE, GTK_STOCK_CANCEL
2889  )) {
2890  return;
2891  }
2892  }
2893 
2894  guint i;
2895  for (i = 0; i < selection_length(&screen.selectionInfo);) {
2896  gerbv_selection_item_t sel_item = selection_get_item_by_index(&screen.selectionInfo, i);
2897  gerbv_fileinfo_t* file_info = gerbv_get_fileinfo_for_image(sel_item.image, mainProject);
2898 
2899  /* Preserve currently invisible selection from deletion */
2900  if (!file_info->isVisible) {
2901  i++;
2902  continue;
2903  }
2904 
2905  file_info->layer_dirty = TRUE;
2906  selection_clear_item_by_index(&screen.selectionInfo, i);
2907  gerbv_image_delete_net(sel_item.net);
2908  }
2909  update_selected_object_message(FALSE);
2910 
2911  render_refresh_rendered_image_on_screen();
2912  callbacks_update_layer_tree();
2913 }
2914 
2915 /* --------------------------------------------------------------------------- */
2916 gboolean
2917 callbacks_drawingarea_configure_event(GtkWidget* widget, GdkEventConfigure* event) {
2918  GdkDrawable* drawable = widget->window;
2919 
2920  gdk_drawable_get_size(drawable, &screenRenderInfo.displayWidth, &screenRenderInfo.displayHeight);
2921 
2922  /* set this up if cairo is compiled, since we may need to switch over to
2923  using the surface at any time */
2924  int x_off = 0, y_off = 0;
2925 
2926  if (GDK_IS_WINDOW(widget->window)) {
2927  /* query the window's backbuffer if it has one */
2928  GdkWindow* window = GDK_WINDOW(widget->window);
2929  gdk_window_get_internal_paint_info(window, &drawable, &x_off, &y_off);
2930  }
2931  if (screen.windowSurface)
2932  cairo_surface_destroy((cairo_surface_t*)screen.windowSurface);
2933 
2934 #if defined(WIN32) || defined(QUARTZ)
2935  cairo_t* cairoTarget = gdk_cairo_create(GDK_WINDOW(widget->window));
2936 
2937  screen.windowSurface = cairo_get_target(cairoTarget);
2938  /* increase surface reference by one so it isn't freed when the cairo_t
2939  is destroyed next */
2940  screen.windowSurface = cairo_surface_reference(screen.windowSurface);
2941  cairo_destroy(cairoTarget);
2942 #else
2943  GdkVisual* visual = gdk_drawable_get_visual(drawable);
2944  screen.windowSurface = (gpointer)cairo_xlib_surface_create(
2945  GDK_DRAWABLE_XDISPLAY(drawable), GDK_DRAWABLE_XID(drawable), GDK_VISUAL_XVISUAL(visual),
2946  screenRenderInfo.displayWidth, screenRenderInfo.displayHeight
2947  );
2948 #endif
2949  /* if this is the first time, go ahead and call autoscale even if we don't
2950  have a model loaded */
2951  if ((screenRenderInfo.scaleFactorX < 0.001) || (screenRenderInfo.scaleFactorY < 0.001)) {
2952  gerbv_render_zoom_to_fit_display(mainProject, &screenRenderInfo);
2953  }
2954  render_refresh_rendered_image_on_screen();
2955  return TRUE;
2956 }
2957 
2958 /* --------------------------------------------------------- */
2959 gboolean
2960 callbacks_drawingarea_expose_event(GtkWidget* widget, GdkEventExpose* event) {
2961  if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
2962  GdkPixmap* new_pixmap;
2963  GdkGC* gc = gdk_gc_new(widget->window);
2964 
2965  /*
2966  * Create a pixmap with default background
2967  */
2968  new_pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1);
2969 
2970  gdk_gc_set_foreground(gc, &mainProject->background);
2971 
2972  gdk_draw_rectangle(new_pixmap, gc, TRUE, event->area.x, event->area.y, event->area.width, event->area.height);
2973 
2974  /*
2975  * Copy gerber pixmap onto background if we have one to copy.
2976  * Do translation at the same time.
2977  */
2978  if (screen.pixmap != NULL) {
2979  gdk_draw_pixmap(
2980  new_pixmap, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], screen.pixmap, event->area.x - screen.off_x,
2981  event->area.y - screen.off_y, event->area.x, event->area.y, event->area.width, event->area.height
2982  );
2983  }
2984 
2985  /*
2986  * Draw the whole thing onto screen
2987  */
2988  gdk_draw_pixmap(
2989  widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], new_pixmap, event->area.x, event->area.y,
2990  event->area.x, event->area.y, event->area.width, event->area.height
2991  );
2992 
2993  gdk_pixmap_unref(new_pixmap);
2994  gdk_gc_unref(gc);
2995 
2996  /*
2997  * Draw Zooming outline if we are in that mode
2998  */
2999  if (screen.state == IN_ZOOM_OUTLINE) {
3000  render_draw_zoom_outline(screen.centered_outline_zoom);
3001  } else if (screen.state == IN_MEASURE) {
3003  }
3004  if (screen.tool == MEASURE && screen.state != IN_MEASURE) {
3006  }
3007 
3008  return FALSE;
3009  }
3010 
3011  cairo_t* cr;
3012  int width, height;
3013  int x_off = 0, y_off = 0;
3014  GdkDrawable* drawable = widget->window;
3015 
3016  if (GDK_IS_WINDOW(widget->window)) {
3017  /* query the window's backbuffer if it has one */
3018  GdkWindow* window = GDK_WINDOW(widget->window);
3019  gdk_window_get_internal_paint_info(window, &drawable, &x_off, &y_off);
3020  }
3021  gdk_drawable_get_size(drawable, &width, &height);
3022 
3023 #if defined(WIN32) || defined(QUARTZ)
3024  /* FIXME */
3025  cr = gdk_cairo_create(GDK_WINDOW(widget->window));
3026 #else
3027  cairo_surface_t* buffert;
3028 
3029  GdkVisual* visual = gdk_drawable_get_visual(drawable);
3030  buffert = (gpointer)cairo_xlib_surface_create(
3031  GDK_DRAWABLE_XDISPLAY(drawable), GDK_DRAWABLE_XID(drawable), GDK_VISUAL_XVISUAL(visual), event->area.width,
3032  event->area.height
3033  );
3034  cr = cairo_create(buffert);
3035 #endif
3036  cairo_translate(cr, -event->area.x + screen.off_x, -event->area.y + screen.off_y);
3037  render_project_to_cairo_target(cr);
3038  cairo_destroy(cr);
3039 #if !defined(WIN32) && !defined(QUARTZ)
3040  cairo_surface_destroy(buffert);
3041 #endif
3042 
3043  if (screen.tool == MEASURE)
3045  return FALSE;
3046 }
3047 
3048 /* Transforms screen coordinates to board ones */
3049 static void
3050 callbacks_screen2board(gdouble* X, gdouble* Y, gint x, gint y) {
3051 
3052  /* make sure we don't divide by zero (which is possible if the gui
3053  isn't displayed yet */
3054  if ((screenRenderInfo.scaleFactorX > 0.001) || (screenRenderInfo.scaleFactorY > 0.001)) {
3055  *X = screenRenderInfo.lowerLeftX + (x / screenRenderInfo.scaleFactorX);
3056  *Y = screenRenderInfo.lowerLeftY + ((screenRenderInfo.displayHeight - y) / screenRenderInfo.scaleFactorY);
3057  } else {
3058  *X = *Y = 0.0;
3059  }
3060 }
3061 
3062 const char* gerbv_coords_pattern_mils_str = N_("%8.2f %8.2f");
3063 
3064 /* --------------------------------------------------------- */
3065 static void
3066 callbacks_update_statusbar_coordinates(gint x, gint y) {
3067  gdouble X, Y;
3068 
3069  callbacks_screen2board(&X, &Y, x, y);
3070 
3071  switch (screen.unit) {
3072  case GERBV_MILS:
3073  utf8_snprintf(
3074  screen.statusbar.coordstr, MAX_COORDLEN, _(gerbv_coords_pattern_mils_str), COORD2MILS(X), COORD2MILS(Y)
3075  );
3076  break;
3077  case GERBV_MMS:
3078  utf8_snprintf(screen.statusbar.coordstr, MAX_COORDLEN, _("%8.3f %8.3f"), COORD2MMS(X), COORD2MMS(Y));
3079  break;
3080  default: utf8_snprintf(screen.statusbar.coordstr, MAX_COORDLEN, _("%4.5f %4.5f"), COORD2INS(X), COORD2INS(Y));
3081  }
3082 
3084 }
3085 
3086 static void
3087 update_selected_object_message(gboolean userTriedToSelect) {
3088  if (screen.tool != POINTER)
3089  return;
3090 
3091  gint selectionLength = selection_length(&screen.selectionInfo);
3092 
3093  if (selectionLength == 0) {
3094  if (userTriedToSelect) {
3095  /* Update status bar message to make sure the user
3096  * knows about needing to select the layer */
3097  gchar* str = g_new(gchar, MAX_DISTLEN);
3098  utf8_strncpy(
3099  str,
3100  _("No object selected. Objects can "
3101  "only be selected in the active "
3102  "layer."),
3103  MAX_DISTLEN - 7
3104  );
3105  utf8_snprintf(screen.statusbar.diststr, MAX_DISTLEN, "<b>%s</b>", str);
3106  g_free(str);
3107  } else {
3108  utf8_strncpy(
3109  screen.statusbar.diststr,
3110  _("Click to select objects in the "
3111  "active layer. Middle click and drag "
3112  "to pan."),
3113  MAX_DISTLEN
3114  );
3115  }
3116  } else {
3117  utf8_snprintf(
3118  screen.statusbar.diststr, MAX_DISTLEN,
3119  ngettext("%d object are currently selected", "%d objects are currently selected", selectionLength),
3120  selectionLength
3121  );
3122  }
3123 
3125 }
3126 
3127 /* --------------------------------------------------------- */
3128 gboolean
3129 callbacks_drawingarea_motion_notify_event(GtkWidget* widget, GdkEventMotion* event) {
3130  int x, y;
3131  GdkModifierType state;
3132 
3133  if (event->is_hint)
3134  gdk_window_get_pointer(event->window, &x, &y, &state);
3135  else {
3136  x = event->x;
3137  y = event->y;
3138  state = event->state;
3139  }
3140 
3141  switch (screen.state) {
3142  case IN_MOVE:
3143  {
3144  if (screen.last_x != 0 || screen.last_y != 0) {
3145  /* Move pixmap to get a snappier feel of movement */
3146  screen.off_x += x - screen.last_x;
3147  screen.off_y += y - screen.last_y;
3148  }
3149  screenRenderInfo.lowerLeftX -= ((x - screen.last_x) / screenRenderInfo.scaleFactorX);
3150  screenRenderInfo.lowerLeftY += ((y - screen.last_y) / screenRenderInfo.scaleFactorY);
3151  callbacks_force_expose_event_for_screen();
3152  callbacks_update_scrollbar_positions();
3153  screen.last_x = x;
3154  screen.last_y = y;
3155  break;
3156  }
3157  case IN_ZOOM_OUTLINE:
3158  {
3159  if (screen.last_x || screen.last_y)
3160  render_draw_zoom_outline(screen.centered_outline_zoom);
3161  screen.last_x = x;
3162  screen.last_y = y;
3163  render_draw_zoom_outline(screen.centered_outline_zoom);
3164  break;
3165  }
3166  case IN_MEASURE:
3167  {
3168  /* clear the previous drawn line by drawing over it */
3170  callbacks_screen2board(&(screen.measure_stop_x), &(screen.measure_stop_y), x, y);
3171  /* screen.last_[xy] are updated to move the ruler pointers */
3172  screen.last_x = x;
3173  screen.last_y = y;
3174  /* draw the new line and write the new distance */
3176  break;
3177  }
3178  case IN_SELECTION_DRAG:
3179  {
3180  if (screen.last_x || screen.last_y)
3181  render_draw_selection_box_outline();
3182  screen.last_x = x;
3183  screen.last_y = y;
3184  render_draw_selection_box_outline();
3185  break;
3186  }
3187  default:
3188  screen.last_x = x;
3189  screen.last_y = y;
3190  break;
3191  }
3192  callbacks_update_statusbar_coordinates(x, y);
3193  callbacks_update_ruler_pointers();
3194  return TRUE;
3195 } /* motion_notify_event */
3196 
3197 /* --------------------------------------------------------- */
3198 gboolean
3199 callbacks_drawingarea_button_press_event(GtkWidget* widget, GdkEventButton* event) {
3200  GdkWindow* drawing_area_window = screen.drawing_area->window;
3201  GdkCursor* cursor;
3202 
3203  switch (event->button) {
3204  case 1:
3205  if (screen.tool == POINTER) {
3206  /* select */
3207  /* selection will only work with cairo, so do nothing if it's
3208  not compiled */
3209  screen.state = IN_SELECTION_DRAG;
3210  screen.start_x = event->x;
3211  screen.start_y = event->y;
3212  } else if (screen.tool == PAN) {
3213  /* Plain panning */
3214  screen.state = IN_MOVE;
3215  screen.last_x = event->x;
3216  screen.last_y = event->y;
3217  } else if (screen.tool == ZOOM) {
3218  screen.state = IN_ZOOM_OUTLINE;
3219  /* Zoom outline mode initiated */
3220  screen.start_x = event->x;
3221  screen.start_y = event->y;
3222  screen.centered_outline_zoom = event->state;
3223  } else if (screen.tool == MEASURE) {
3224  screen.state = IN_MEASURE;
3225  callbacks_screen2board(&(screen.measure_start_x), &(screen.measure_start_y), event->x, event->y);
3226  screen.measure_stop_x = screen.measure_start_x;
3227  screen.measure_stop_y = screen.measure_start_y;
3228  /* force an expose event to clear any previous measure lines */
3229  callbacks_force_expose_event_for_screen();
3230  }
3231  break;
3232  case 2:
3233  screen.state = IN_MOVE;
3234  screen.last_x = event->x;
3235  screen.last_y = event->y;
3236  cursor = gdk_cursor_new(GDK_FLEUR);
3237  gdk_window_set_cursor(drawing_area_window, cursor);
3238  gdk_cursor_destroy(cursor);
3239  break;
3240  case 3:
3241  if (screen.tool == POINTER) {
3242  /* if no items are selected, try and find the item the user
3243  is pointing at */
3244  if (selection_length(&screen.selectionInfo) == 0) {
3245  gint index = callbacks_get_selected_row_index();
3246  if ((index >= 0) && (index <= mainProject->last_loaded) && (mainProject->file[index]->isVisible)) {
3247  render_fill_selection_buffer_from_mouse_click(event->x, event->y, index, SELECTION_REPLACE);
3248  } else {
3249  selection_clear(&screen.selectionInfo);
3250  update_selected_object_message(FALSE);
3251  render_refresh_rendered_image_on_screen();
3252  }
3253  }
3254 
3255  /* only show the popup if we actually have something selected now */
3256  if (selection_length(&screen.selectionInfo) != 0) {
3257  update_selected_object_message(TRUE);
3258  gtk_menu_popup(
3259  GTK_MENU(screen.win.drawWindowPopupMenu), NULL, NULL, NULL, NULL, event->button, event->time
3260  );
3261  }
3262 
3263  } else {
3264  /* Zoom outline mode initiated */
3265  screen.state = IN_ZOOM_OUTLINE;
3266  screen.start_x = event->x;
3267  screen.start_y = event->y;
3268  screen.centered_outline_zoom = event->state & GDK_SHIFT_MASK;
3269  cursor = gdk_cursor_new(GDK_SIZING);
3270  gdk_window_set_cursor(drawing_area_window, cursor);
3271  gdk_cursor_destroy(cursor);
3272  }
3273  break;
3274  case 4: /* Scroll wheel */ render_zoom_display(ZOOM_IN_CMOUSE, 0, event->x, event->y); break;
3275  case 5: /* Scroll wheel */ render_zoom_display(ZOOM_OUT_CMOUSE, 0, event->x, event->y); break;
3276  default: break;
3277  }
3278  callbacks_switch_to_correct_cursor();
3279  return TRUE;
3280 }
3281 
3282 static gboolean
3283 check_align_files_possibility(gerbv_selection_info_t* sel_info) {
3285  GtkMenuItem** menu_items = (GtkMenuItem**)screen.win.curEditAlingItem;
3286  gerbv_selection_item_t si[2];
3287  int id[2] = { -1, -1 };
3288  int i;
3289 
3290  /* If has two objects, then can do files aligning */
3291  if (selection_length(sel_info) == 2) {
3292  si[0] = selection_get_item_by_index(sel_info, 0);
3293  si[1] = selection_get_item_by_index(sel_info, 1);
3294 
3295  for (i = 0; i <= mainProject->last_loaded; i++) {
3296  if (f[i]->image == si[0].image)
3297  id[0] = i;
3298 
3299  if (f[i]->image == si[1].image)
3300  id[1] = i;
3301  }
3302 
3303  /* Can align if on different files */
3304  if (id[0] * id[1] >= 0 && id[0] != id[1]) {
3305  gchar* str;
3306 
3307  /* TODO: add color boxes for layers as hint */
3308 
3309  /* Update align menu items */
3310  str = g_strdup_printf(_("#_%i %s > #%i %s"), id[0] + 1, f[id[0]]->name, id[1] + 1, f[id[1]]->name);
3311  gtk_menu_item_set_label(menu_items[0], str);
3312  g_free(str);
3313 
3314  str = g_strdup_printf(_("#_%i %s > #%i %s"), id[1] + 1, f[id[1]]->name, id[0] + 1, f[id[0]]->name);
3315  gtk_menu_item_set_label(menu_items[1], str);
3316  g_free(str);
3317 
3318  gtk_widget_set_sensitive(screen.win.curEditAlingMenuItem, TRUE);
3319 
3320  return TRUE;
3321  }
3322  }
3323 
3324  /* Can't align, disable align menu */
3325  gtk_widget_set_sensitive(screen.win.curEditAlingMenuItem, FALSE);
3326  gtk_menu_item_set_label(menu_items[0], "");
3327  gtk_menu_item_set_label(menu_items[1], "");
3328 
3329  return FALSE;
3330 }
3331 
3334 void
3335 callbacks_align_files_from_sel_clicked(GtkMenuItem* menu_item, gpointer user_data) {
3336  gerbv_fileinfo_t* fi[2];
3337  gerbv_selection_item_t item[2];
3338  gerbv_net_t* net;
3339  gerbv_selection_info_t* sel_info = &screen.selectionInfo;
3340  int align_second_to_first = GPOINTER_TO_INT(user_data);
3341  gdouble x[2], y[2];
3342  int i;
3343 
3344  if (selection_length(sel_info) != 2)
3345  return;
3346 
3347  item[0] = selection_get_item_by_index(sel_info, 0);
3348  item[1] = selection_get_item_by_index(sel_info, 1);
3349 
3350  fi[0] = gerbv_get_fileinfo_for_image(item[0].image, mainProject);
3351  fi[1] = gerbv_get_fileinfo_for_image(item[1].image, mainProject);
3352 
3353  if (fi[0] == NULL || fi[1] == NULL || fi[0] == fi[1])
3354  return;
3355 
3356  /* Calculate aligning coords */
3357  for (i = 0; i < 2; i++) {
3358  net = item[i].net;
3359 
3360  switch (net->aperture_state) {
3362  x[i] = net->stop_x;
3363  y[i] = net->stop_y;
3364  break;
3366  switch (net->interpolation) {
3371  x[i] = (net->stop_x + net->start_x) / 2;
3372  y[i] = (net->stop_y + net->start_y) / 2;
3373  break;
3376  x[i] = net->cirseg->cp_x;
3377  y[i] = net->cirseg->cp_y;
3378  break;
3379  default:
3380  GERB_COMPILE_ERROR(
3381  _("Can't align by this "
3382  "type of object")
3383  );
3384  return;
3385  }
3386  break;
3387  default:
3388  GERB_COMPILE_ERROR(
3389  _("Can't align by this "
3390  "type of object")
3391  );
3392  return;
3393  }
3394 
3395  gerbv_transform_coord_for_image(x + i, y + i, item[i].image, mainProject);
3396  }
3397 
3398  if (align_second_to_first) {
3399  fi[1]->transform.translateX += x[0] - x[1];
3400  fi[1]->transform.translateY += y[0] - y[1];
3401  } else {
3402  fi[0]->transform.translateX += x[1] - x[0];
3403  fi[0]->transform.translateY += y[1] - y[0];
3404  }
3405 
3406  render_refresh_rendered_image_on_screen();
3407  callbacks_update_layer_tree();
3408 }
3409 
3410 /* --------------------------------------------------------- */
3411 gboolean
3412 callbacks_drawingarea_button_release_event(GtkWidget* widget, GdkEventButton* event) {
3413  gint index;
3414 
3415  if (event->type != GDK_BUTTON_RELEASE)
3416  return TRUE;
3417 
3418  switch (screen.state) {
3419  case IN_MOVE:
3420  screen.off_x = 0;
3421  screen.off_y = 0;
3422  render_refresh_rendered_image_on_screen();
3423  callbacks_switch_to_normal_tool_cursor(screen.tool);
3424  break;
3425 
3426  case IN_ZOOM_OUTLINE:
3427  if ((event->state & GDK_SHIFT_MASK) != 0) {
3428  render_zoom_display(ZOOM_OUT_CMOUSE, 0, event->x, event->y);
3429  }
3430  /* if the user just clicks without dragging, then simply
3431  zoom in a preset amount */
3432  else if ((abs(screen.start_x - event->x) < 4) && (abs(screen.start_y - event->y) < 4)) {
3433  render_zoom_display(ZOOM_IN_CMOUSE, 0, event->x, event->y);
3434  } else {
3435  render_calculate_zoom_from_outline(widget, event);
3436  }
3437  callbacks_switch_to_normal_tool_cursor(screen.tool);
3438  break;
3439 
3440  case IN_SELECTION_DRAG:
3441  /* selection will only work with cairo, so do nothing if it's
3442  not compiled */
3444 
3445  if ((index >= 0) && mainProject->file[index]->isVisible) {
3446  enum selection_action sel_action = SELECTION_REPLACE;
3447 
3448  if (event->state & GDK_SHIFT_MASK)
3449  sel_action = SELECTION_ADD;
3450  else if (event->state & GDK_CONTROL_MASK)
3451  sel_action = SELECTION_TOGGLE;
3452 
3453  /* determine if this was just a click or a box drag */
3454  if ((fabs((double)(screen.last_x - screen.start_x)) < 5)
3455  && (fabs((double)(screen.last_y - screen.start_y)) < 5)) {
3456  render_fill_selection_buffer_from_mouse_click(event->x, event->y, index, sel_action);
3457  } else {
3458  render_fill_selection_buffer_from_mouse_drag(
3459  event->x, event->y, screen.start_x, screen.start_y, index, sel_action
3460  );
3461  }
3462 
3463  /* Check if anything was selected */
3464  update_selected_object_message(TRUE);
3465 
3466  check_align_files_possibility(&screen.selectionInfo);
3467  } else {
3468  render_refresh_rendered_image_on_screen();
3469  }
3470  break;
3471  default: break;
3472  }
3473 
3474  screen.state = NORMAL;
3475 
3476  return TRUE;
3477 } /* button_release_event */
3478 
3479 /* --------------------------------------------------------- */
3480 gboolean
3481 callbacks_window_key_press_event(GtkWidget* widget, GdkEventKey* event) {
3482  switch (event->keyval) {
3483  case GDK_Escape:
3484  if (screen.tool == POINTER) {
3485  selection_clear(&screen.selectionInfo);
3486  update_selected_object_message(FALSE);
3487  }
3488 
3489  /* Escape may be used to abort outline zoom and just plain
3490  * repaint */
3491  screen.state = NORMAL;
3492  render_refresh_rendered_image_on_screen();
3493 
3494  break;
3495  default: break;
3496  }
3497 
3498  return TRUE;
3499 } /* key_press_event */
3500 
3501 /* --------------------------------------------------------- */
3502 gboolean
3503 callbacks_window_key_release_event(GtkWidget* widget, GdkEventKey* event) {
3504  return TRUE;
3505 } /* key_release_event */
3506 
3507 /* --------------------------------------------------------- */
3508 /* Scroll wheel */
3509 gboolean
3510 callbacks_window_scroll_event(GtkWidget* widget, GdkEventScroll* event) {
3511  switch (event->direction) {
3512  case GDK_SCROLL_UP: render_zoom_display(ZOOM_IN_CMOUSE, 0, event->x, event->y); break;
3513  case GDK_SCROLL_DOWN: render_zoom_display(ZOOM_OUT_CMOUSE, 0, event->x, event->y); break;
3514  case GDK_SCROLL_LEFT:
3515  /* Ignore */
3516  case GDK_SCROLL_RIGHT:
3517  /* Ignore */
3518  default: return TRUE;
3519  }
3520  return TRUE;
3521 } /* scroll_event */
3522 
3523 /* ------------------------------------------------------------------ */
3530 void
3532  if ((screen.statusbar.coordstr != NULL) && (GTK_IS_LABEL(screen.win.statusMessageLeft))) {
3533  gtk_label_set_text(GTK_LABEL(screen.win.statusMessageLeft), screen.statusbar.coordstr);
3534  }
3535  if ((screen.statusbar.diststr != NULL) && (GTK_IS_LABEL(screen.win.statusMessageRight))) {
3536  gtk_label_set_markup(GTK_LABEL(screen.win.statusMessageRight), screen.statusbar.diststr);
3537  }
3538 }
3539 
3540 /* --------------------------------------------------------- */
3541 void
3542 callbacks_update_statusbar_measured_distance(gdouble dx, gdouble dy) {
3543  gdouble delta = hypot(dx, dy);
3544 
3545  if (screen.unit == GERBV_MILS) {
3546  utf8_snprintf(
3547  screen.statusbar.diststr, MAX_DISTLEN, _("Measured distance: %8.2f mils (%8.2f x, %8.2f y)"),
3548  COORD2MILS(delta), COORD2MILS(dx), COORD2MILS(dy)
3549  );
3550  } else if (screen.unit == GERBV_MMS) {
3551  utf8_snprintf(
3552  screen.statusbar.diststr, MAX_DISTLEN, _("Measured distance: %8.3f mm (%8.3f x, %8.3f y)"),
3553  COORD2MMS(delta), COORD2MMS(dx), COORD2MMS(dy)
3554  );
3555  } else {
3556  utf8_snprintf(
3557  screen.statusbar.diststr, MAX_DISTLEN, _("Measured distance: %4.5f inches (%4.5f x, %4.5f y)"),
3558  COORD2INS(delta), COORD2INS(dx), COORD2INS(dy)
3559  );
3560  }
3562 }
3563 
3564 /* --------------------------------------------------------- */
3565 void
3566 callbacks_sidepane_render_type_combo_box_changed(GtkComboBox* widget, gpointer user_data) {
3567  gerbv_render_types_t type = gtk_combo_box_get_active(widget);
3568 
3569  dprintf("%s(): type = %d\n", __FUNCTION__, type);
3570 
3571  if (type < 0 || type == screenRenderInfo.renderType)
3572  return;
3573 
3574  screenRenderInfo.renderType = type;
3575  callbacks_render_type_changed();
3576 }
3577 
3578 /* --------------------------------------------------------- */
3579 void
3580 callbacks_viewmenu_rendertype_changed(GtkCheckMenuItem* widget, gpointer user_data) {
3581  gerbv_render_types_t type = GPOINTER_TO_INT(user_data);
3582 
3583  if (type == screenRenderInfo.renderType)
3584  return;
3585 
3586  dprintf("%s(): type = %d\n", __FUNCTION__, type);
3587 
3588  screenRenderInfo.renderType = type;
3589  callbacks_render_type_changed();
3590 }
3591 
3592 /* --------------------------------------------------------- */
3593 void
3594 callbacks_viewmenu_units_changed(GtkCheckMenuItem* widget, gpointer user_data) {
3595  gerbv_gui_unit_t unit = GPOINTER_TO_INT(user_data);
3596 
3597  if (unit < 0 || unit == screen.unit)
3598  return;
3599 
3600  dprintf("%s(): unit = %d, screen.unit = %d\n", __FUNCTION__, unit, screen.unit);
3601 
3602  callbacks_units_changed(unit);
3603 }
3604 
3605 /* --------------------------------------------------------- */
3606 void
3607 callbacks_statusbar_unit_combo_box_changed(GtkComboBox* widget, gpointer user_data) {
3608  gerbv_gui_unit_t unit = gtk_combo_box_get_active(widget);
3609  int force_change = GPOINTER_TO_INT(user_data);
3610 
3611  if (!force_change && (unit < 0 || unit == screen.unit))
3612  return;
3613 
3614  callbacks_units_changed(unit);
3615 }
3616 
3617 /* --------------------------------------------------------- */
3618 void
3619 callbacks_clear_messages_button_clicked(GtkButton* button, gpointer user_data) {
3620  GtkTextBuffer* textbuffer;
3621  GtkTextIter start, end;
3622 
3623  screen.length_sum = 0;
3624 
3625  textbuffer = gtk_text_view_get_buffer((GtkTextView*)screen.win.messageTextView);
3626  gtk_text_buffer_get_start_iter(textbuffer, &start);
3627  gtk_text_buffer_get_end_iter(textbuffer, &end);
3628  gtk_text_buffer_delete(textbuffer, &start, &end);
3629 }
3630 
3631 /* --------------------------------------------------------- */
3632 void
3633 callbacks_handle_log_messages(
3634  const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data
3635 ) {
3636  GtkTextBuffer* textbuffer = NULL;
3637  GtkTextIter iter;
3638  GtkTextTag* tag;
3639  GtkTextMark * StartMark = NULL, *StopMark = NULL;
3640  GtkTextIter StartIter, StopIter;
3641  GtkWidget * dialog, *label;
3642 
3643  if (!screen.win.messageTextView)
3644  return;
3645 
3646  textbuffer = gtk_text_view_get_buffer((GtkTextView*)screen.win.messageTextView);
3647 
3648  /* create a mark for the end of the text. */
3649  gtk_text_buffer_get_end_iter(textbuffer, &iter);
3650 
3651  /* get the current end position of the text (it will be the
3652  start of the new text. */
3653  StartMark = gtk_text_buffer_create_mark(textbuffer, "NewTextStart", &iter, TRUE);
3654 
3655  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "blue_foreground");
3656  /* the tag does not exist: create it and let them exist in the tag table.*/
3657  if (tag == NULL) {
3658  tag = gtk_text_buffer_create_tag(textbuffer, "black_foreground", "foreground", "black", NULL);
3659  tag = gtk_text_buffer_create_tag(textbuffer, "blue_foreground", "foreground", "blue", NULL);
3660  tag = gtk_text_buffer_create_tag(textbuffer, "red_foreground", "foreground", "red", NULL);
3661  tag = gtk_text_buffer_create_tag(textbuffer, "darkred_foreground", "foreground", "darkred", NULL);
3662  tag = gtk_text_buffer_create_tag(textbuffer, "darkblue_foreground", "foreground", "darkblue", NULL);
3663  tag = gtk_text_buffer_create_tag(textbuffer, "darkgreen_foreground", "foreground", "darkgreen", NULL);
3664  tag = gtk_text_buffer_create_tag(textbuffer, "saddlebrown_foreground", "foreground", "saddlebrown", NULL);
3665  }
3666 
3667  /*
3668  * See rgb.txt for the color names definition
3669  * (on my PC it is on /usr/X11R6/lib/X11/rgb.txt)
3670  */
3671  switch (log_level & G_LOG_LEVEL_MASK) {
3672  case G_LOG_LEVEL_ERROR:
3673  /* a message of this kind aborts the application calling abort() */
3674  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "red_foreground");
3675  gtk_notebook_set_current_page(GTK_NOTEBOOK(screen.win.sidepane_notebook), 1);
3676  gtk_widget_show(screen.win.sidepane_notebook);
3677  break;
3678  case G_LOG_LEVEL_CRITICAL:
3679  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "red_foreground");
3680  gtk_notebook_set_current_page(GTK_NOTEBOOK(screen.win.sidepane_notebook), 1);
3681  gtk_widget_show(screen.win.sidepane_notebook);
3682  break;
3683  case G_LOG_LEVEL_WARNING:
3684  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "darkred_foreground");
3685  gtk_notebook_set_current_page(GTK_NOTEBOOK(screen.win.sidepane_notebook), 1);
3686  gtk_widget_show(screen.win.sidepane_notebook);
3687  break;
3688  case G_LOG_LEVEL_MESSAGE:
3689  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "darkblue_foreground");
3690  gtk_notebook_set_current_page(GTK_NOTEBOOK(screen.win.sidepane_notebook), 1);
3691  gtk_widget_show(screen.win.sidepane_notebook);
3692  break;
3693  case G_LOG_LEVEL_INFO:
3694  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "darkgreen_foreground");
3695  break;
3696  case G_LOG_LEVEL_DEBUG:
3697  tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "saddlebrown_foreground");
3698  break;
3699  default: tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer), "black_foreground"); break;
3700  }
3701 
3702  /*
3703  * Fatal aborts application. We will try to get the message out anyhow.
3704  */
3705  if (log_level & G_LOG_FLAG_FATAL) {
3706  fprintf(stderr, _("Fatal error: %s\n"), message);
3707 
3708  /* Try to show dialog box with error message */
3709  dialog = gtk_dialog_new_with_buttons(
3710  _("Fatal Error"), NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
3711  GTK_RESPONSE_ACCEPT, NULL
3712  );
3713 
3714  label = gtk_label_new(g_strdup_printf(_("Fatal error: %s"), message));
3715  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
3716  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
3717  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), gtk_label_new(_("\nGerbv will be closed now!")));
3718 
3719  gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
3720 
3721  gtk_widget_show_all(dialog);
3722  gtk_dialog_run(GTK_DIALOG(dialog));
3723  }
3724 
3725  gtk_text_buffer_insert(textbuffer, &iter, message, -1);
3726  gtk_text_buffer_insert(textbuffer, &iter, "\n", -1);
3727 
3728  /* Scroll view to inserted text */
3729  g_signal_emit_by_name(textbuffer, "paste-done", NULL);
3730 
3731  gtk_text_buffer_get_end_iter(textbuffer, &iter);
3732 
3733  StopMark = gtk_text_buffer_create_mark(textbuffer, "NewTextStop", &iter, TRUE);
3734 
3735  gtk_text_buffer_get_iter_at_mark(textbuffer, &StartIter, StartMark);
3736  gtk_text_buffer_get_iter_at_mark(textbuffer, &StopIter, StopMark);
3737 
3738  gtk_text_buffer_apply_tag(textbuffer, tag, &StartIter, &StopIter);
3739 }
3740 
3741 /* --------------------------------------------------------- */
3742 void
3743 callbacks_force_expose_event_for_screen(void) {
3744  GdkRectangle update_rect;
3745 
3746  update_rect.x = 0;
3747  update_rect.y = 0;
3748  update_rect.width = screenRenderInfo.displayWidth;
3749  update_rect.height = screenRenderInfo.displayHeight;
3750 
3751  /* Calls expose_event */
3752  gdk_window_invalidate_rect(screen.drawing_area->window, &update_rect, FALSE);
3753 
3754  /* update other gui things that could have changed */
3755  callbacks_update_ruler_scales();
3756  callbacks_update_scrollbar_limits();
3757  callbacks_update_scrollbar_positions();
3758 }
3759 
3760 static double
3761 screen_units(double d) {
3762  switch (screen.unit) {
3763  case GERBV_INS: return COORD2INS(d); break;
3764  case GERBV_MILS: return COORD2MILS(d); break;
3765  case GERBV_MMS: return COORD2MMS(d); break;
3766  }
3767 
3768  return d;
3769 }
3770 
3771 static const char*
3772 screen_units_str(void) {
3773  /* NOTE: in order of gerbv_gui_unit_t */
3774  const char* units_str[] = { N_("mil"), N_("mm"), N_("in") };
3775 
3776  return _(units_str[screen.unit]);
3777 }
3778 
3779 static double
3780 line_length(double x0, double y0, double x1, double y1) {
3781  double dx = x0 - x1;
3782  double dy = y0 - y1;
3783 
3784  return hypot(dx, dy);
3785 }
3786 
3787 static double
3788 arc_length(double dia, double angle) {
3789  return M_PI * dia * (angle / 360.0);
3790 }
3791 
3792 static void
3793 aperture_state_report(gerbv_net_t* net, gerbv_image_t* img, gerbv_project_t* prj) {
3794  gerbv_layertype_t layer_type = img->layertype;
3795 
3796  gboolean show_length = FALSE;
3797  gboolean aperture_is_valid = FALSE;
3798  double x, y, len = 0;
3799 
3800  if (net->aperture > 0)
3801  aperture_is_valid = TRUE;
3802 
3803  switch (net->aperture_state) {
3804 
3805  case GERBV_APERTURE_STATE_OFF: break;
3806 
3808  switch (net->interpolation) {
3809 
3814  if (layer_type != GERBV_LAYERTYPE_DRILL)
3815  g_message(_("Object type: Line"));
3816  else
3817  g_message(_("Object type: Slot (drilled)"));
3818 
3819  len = line_length(net->start_x, net->start_y, net->stop_x, net->stop_y);
3820  show_length = 1;
3821 
3822  break;
3823 
3826  g_message(_("Object type: Arc"));
3827  len = arc_length(net->cirseg->width, fabs(net->cirseg->angle1 - net->cirseg->angle2));
3828  show_length = 1;
3829 
3830  break;
3831  default: g_message(_("Object type: Unknown")); break;
3832  }
3833 
3834  if (layer_type != GERBV_LAYERTYPE_DRILL)
3835  g_message(_(" Exposure: On"));
3836 
3837  if (aperture_is_valid) {
3838  if (layer_type != GERBV_LAYERTYPE_DRILL)
3839  aperture_report(img->aperture, net->aperture, net->start_x, net->start_y, img, prj);
3840  else
3841  drill_report(img->aperture, net->aperture);
3842  }
3843 
3844  x = net->start_x;
3845  y = net->start_y;
3846  gerbv_transform_coord_for_image(&x, &y, img, prj);
3847  g_message(_(" Start: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3848 
3849  x = net->stop_x;
3850  y = net->stop_y;
3851  gerbv_transform_coord_for_image(&x, &y, img, prj);
3852  g_message(_(" Stop: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3853 
3854  switch (net->interpolation) {
3855 
3858  x = net->cirseg->cp_x;
3859  y = net->cirseg->cp_y;
3860  gerbv_transform_coord_for_image(&x, &y, img, prj);
3861  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3862 
3863  x = net->cirseg->width / 2;
3864  y = x;
3865  gerbv_transform_coord_for_image(&x, &y, img, prj);
3866  g_message(_(" Radius: %g %s"), screen_units(x), screen_units_str());
3867 
3868  g_message(_(" Angle: %g deg"), fabs(net->cirseg->angle1 - net->cirseg->angle2));
3869  g_message(_(" Angles: (%g, %g) deg"), net->cirseg->angle1, net->cirseg->angle2);
3870  g_message(
3871  _(" Direction: %s"),
3872  (net->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ? _("CW") : _("CCW")
3873  );
3874  break;
3875 
3876  default: break;
3877  }
3878 
3879  if (show_length) {
3880  gerbv_aperture_t* aper = img->aperture[net->aperture];
3881 
3882  if (layer_type == GERBV_LAYERTYPE_DRILL && aperture_is_valid && aper->type == GERBV_APTYPE_CIRCLE) {
3883  double dia = aper->parameter[0];
3884  g_message(_(" Slot length: %g %s"), screen_units(len + dia), screen_units_str());
3885  }
3886 
3887  screen.length_sum += len;
3888  g_message(
3889  _(" Length: %g (sum: %g) %s"), screen_units(len), screen_units(screen.length_sum),
3890  screen_units_str()
3891  );
3892  }
3893 
3894  net_layer_file_report(net, img, prj);
3895 
3896  break;
3897 
3899  if (layer_type != GERBV_LAYERTYPE_DRILL)
3900  g_message(_("Object type: Flashed aperture"));
3901  else
3902  g_message(_("Object type: Drill"));
3903 
3904  if (aperture_is_valid) {
3905  if (layer_type != GERBV_LAYERTYPE_DRILL)
3906  aperture_report(img->aperture, net->aperture, net->stop_x, net->stop_y, img, prj);
3907  else
3908  drill_report(img->aperture, net->aperture);
3909  }
3910 
3911  x = net->stop_x;
3912  y = net->stop_y;
3913  gerbv_transform_coord_for_image(&x, &y, img, prj);
3914  g_message(_(" Location: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3915 
3916  net_layer_file_report(net, img, prj);
3917 
3918  break;
3919  }
3920 }
3921 
3922 static void
3923 aperture_report(
3924  gerbv_aperture_t* apertures[], int aperture_num, double x, double y, gerbv_image_t* img, gerbv_project_t* prj
3925 ) {
3926  gerbv_aperture_type_t type = apertures[aperture_num]->type;
3927  double* params = apertures[aperture_num]->parameter;
3928  gerbv_simplified_amacro_t* sam = apertures[aperture_num]->simplified;
3929 
3930  g_message(_(" Aperture used: D%d"), aperture_num);
3931  g_message(
3932  _(" Aperture type: %s"),
3933  (type == GERBV_APTYPE_MACRO) ? _(gerbv_aperture_type_name(sam->type)) : _(gerbv_aperture_type_name(type))
3934  );
3935 
3936  switch (type) {
3937  case GERBV_APTYPE_CIRCLE:
3938  g_message(_(" Diameter: %g %s"), screen_units(params[0]), screen_units_str());
3939  break;
3941  case GERBV_APTYPE_OVAL:
3942  g_message(
3943  _(" Dimensions: %gx%g %s"), screen_units(params[0]), screen_units(params[1]), screen_units_str()
3944  );
3945  break;
3946  case GERBV_APTYPE_MACRO:
3947  {
3948  switch (sam->type) {
3950  g_message(
3951  _(" Diameter: %g %s"), screen_units(sam->parameter[CIRCLE_DIAMETER]), screen_units_str()
3952  );
3953  x += sam->parameter[CIRCLE_CENTER_X];
3954  y += sam->parameter[CIRCLE_CENTER_Y];
3955  gerbv_transform_coord_for_image(&x, &y, img, prj);
3956  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3957  break;
3958 
3960  g_message(_(" Number of points: %g"), sam->parameter[OUTLINE_NUMBER_OF_POINTS]);
3961  x += sam->parameter[OUTLINE_FIRST_X];
3962  y += sam->parameter[OUTLINE_FIRST_Y];
3963  gerbv_transform_coord_for_image(&x, &y, img, prj);
3964  g_message(_(" Start: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3965  g_message(_(" Rotation: %g deg"), sam->parameter[OUTLINE_ROTATION_IDX(sam->parameter)]);
3966  break;
3967 
3969  g_message(_(" Number of points: %g"), sam->parameter[POLYGON_NUMBER_OF_POINTS]);
3970  g_message(
3971  _(" Diameter: %g %s"), screen_units(sam->parameter[POLYGON_DIAMETER]), screen_units_str()
3972  );
3973  x += sam->parameter[POLYGON_CENTER_X];
3974  y += sam->parameter[POLYGON_CENTER_Y];
3975  gerbv_transform_coord_for_image(&x, &y, img, prj);
3976  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
3977  g_message(_(" Rotation: %g deg"), sam->parameter[POLYGON_ROTATION]);
3978  break;
3979 
3981  g_message(
3982  _(" Outside diameter: %g %s"), screen_units(sam->parameter[MOIRE_OUTSIDE_DIAMETER]),
3983  screen_units_str()
3984  );
3985  g_message(
3986  _(" Ring thickness: %g %s"), screen_units(sam->parameter[MOIRE_CIRCLE_THICKNESS]),
3987  screen_units_str()
3988  );
3989  g_message(
3990  _(" Gap width: %g %s"), screen_units(sam->parameter[MOIRE_GAP_WIDTH]), screen_units_str()
3991  );
3992  g_message(_(" Number of rings: %g"), sam->parameter[MOIRE_NUMBER_OF_CIRCLES]);
3993  g_message(
3994  _(" Crosshair thickness: %g %s"),
3995  screen_units(sam->parameter[MOIRE_CROSSHAIR_THICKNESS]), screen_units_str()
3996  );
3997  g_message(
3998  _(" Crosshair length: %g %s"), screen_units(sam->parameter[MOIRE_CROSSHAIR_LENGTH]),
3999  screen_units_str()
4000  );
4001  x += sam->parameter[MOIRE_CENTER_X];
4002  y += sam->parameter[MOIRE_CENTER_Y];
4003  gerbv_transform_coord_for_image(&x, &y, img, prj);
4004  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4005  g_message(_(" Rotation: %g deg"), sam->parameter[MOIRE_ROTATION]);
4006  break;
4007 
4009  g_message(
4010  _(" Outside diameter: %g %s"), screen_units(sam->parameter[THERMAL_OUTSIDE_DIAMETER]),
4011  screen_units_str()
4012  );
4013  g_message(
4014  _(" Inside diameter: %g %s"), screen_units(sam->parameter[THERMAL_INSIDE_DIAMETER]),
4015  screen_units_str()
4016  );
4017  g_message(
4018  _(" Crosshair thickness: %g %s"),
4019  screen_units(sam->parameter[THERMAL_CROSSHAIR_THICKNESS]), screen_units_str()
4020  );
4021  x += sam->parameter[THERMAL_CENTER_X];
4022  y += sam->parameter[THERMAL_CENTER_Y];
4023  gerbv_transform_coord_for_image(&x, &y, img, prj);
4024  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4025  g_message(_(" Rotation: %g deg"), sam->parameter[THERMAL_ROTATION]);
4026  break;
4027 
4029  g_message(
4030  _(" Width: %g %s"), screen_units(sam->parameter[LINE20_WIDTH]), screen_units_str()
4031  );
4032  x += sam->parameter[LINE20_START_X];
4033  y += sam->parameter[LINE20_START_Y];
4034  gerbv_transform_coord_for_image(&x, &y, img, prj);
4035  g_message(_(" Start: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4036  x += sam->parameter[LINE20_END_X];
4037  y += sam->parameter[LINE20_END_Y];
4038  gerbv_transform_coord_for_image(&x, &y, img, prj);
4039  g_message(_(" Stop: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4040  g_message(_(" Rotation: %g deg"), sam->parameter[LINE20_ROTATION]);
4041  break;
4042 
4044  g_message(
4045  _(" Width: %g %s"), screen_units(sam->parameter[LINE21_WIDTH]), screen_units_str()
4046  );
4047  g_message(
4048  _(" Height: %g %s"), screen_units(sam->parameter[LINE21_HEIGHT]), screen_units_str()
4049  );
4050  x += sam->parameter[LINE21_CENTER_X];
4051  y += sam->parameter[LINE21_CENTER_Y];
4052  gerbv_transform_coord_for_image(&x, &y, img, prj);
4053  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4054  g_message(_(" Rotation: %g deg"), sam->parameter[LINE21_ROTATION]);
4055  break;
4056 
4058  g_message(
4059  _(" Width: %g %s"), screen_units(sam->parameter[LINE22_WIDTH]), screen_units_str()
4060  );
4061  g_message(
4062  _(" Height: %g %s"), screen_units(sam->parameter[LINE22_HEIGHT]), screen_units_str()
4063  );
4064  x += sam->parameter[LINE22_LOWER_LEFT_X];
4065  y += sam->parameter[LINE22_LOWER_LEFT_Y];
4066  gerbv_transform_coord_for_image(&x, &y, img, prj);
4067  g_message(
4068  _(" Lower left: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str()
4069  );
4070  g_message(_(" Rotation: %g deg"), sam->parameter[LINE22_ROTATION]);
4071  break;
4072 
4073  default: break;
4074  }
4075  break;
4076  }
4077  default: break;
4078  }
4079 }
4080 
4081 static void
4082 drill_report(gerbv_aperture_t* apertures[], int aperture_num) {
4083  gerbv_aperture_type_t type = apertures[aperture_num]->type;
4084  double* params = apertures[aperture_num]->parameter;
4085 
4086  g_message(_(" Tool used: T%d"), aperture_num);
4087  if (type == GERBV_APTYPE_CIRCLE)
4088  g_message(_(" Diameter: %g %s"), screen_units(params[0]), screen_units_str());
4089 }
4090 
4091 static void
4092 parea_report(gerbv_net_t* net, gerbv_image_t* img, gerbv_project_t* prj) {
4093  gerbv_net_t* n;
4094  unsigned int c = 0;
4095  gerbv_interpolation_t inter_prev;
4096  double x, y;
4097 
4099  return;
4100 
4101  /* Count vertices */
4102  for (gerbv_net_t* n = net->next; n != NULL; n = n->next) {
4104  break;
4105  c++;
4106  }
4107 
4108  g_message(_(" Number of vertices: %u"), c - 1);
4109 
4110  for (n = net->next, inter_prev = net->interpolation; n != NULL && n->interpolation != GERBV_INTERPOLATION_PAREA_END;
4111  n = n->next) {
4112 
4113  switch (n->interpolation) {
4114 
4116 
4117  if (inter_prev != n->interpolation) {
4118  x = n->start_x;
4119  y = n->start_y;
4120  gerbv_transform_coord_for_image(&x, &y, img, prj);
4121  g_message(_(" Line from: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4122  }
4123 
4124  x = n->stop_x;
4125  y = n->stop_y;
4126  gerbv_transform_coord_for_image(&x, &y, img, prj);
4127  g_message(_(" Line to: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4128  break;
4129 
4132 
4133  x = n->start_x;
4134  y = n->start_y;
4135  gerbv_transform_coord_for_image(&x, &y, img, prj);
4136  g_message(_(" Arc from: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4137 
4138  x = n->stop_x;
4139  y = n->stop_y;
4140  gerbv_transform_coord_for_image(&x, &y, img, prj);
4141  g_message(_(" Arc to: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4142 
4143  x = n->cirseg->cp_x;
4144  y = n->cirseg->cp_y;
4145  gerbv_transform_coord_for_image(&x, &y, img, prj);
4146  g_message(_(" Center: (%g, %g) %s"), screen_units(x), screen_units(y), screen_units_str());
4147 
4148  x = n->cirseg->width;
4149  y = n->cirseg->height;
4150  gerbv_transform_coord_for_image(&x, &y, img, prj);
4151  g_message(_(" Radius: %g %s"), screen_units(x) / 2, screen_units_str());
4152 
4153  g_message(_(" Angle: %g deg"), fabs(n->cirseg->angle1 - n->cirseg->angle2));
4154  g_message(_(" Angles: (%g, %g) deg"), n->cirseg->angle1, n->cirseg->angle2);
4155  g_message(
4156  _(" Direction: %s"),
4157  (n->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ? _("CW") : _("CCW")
4158  );
4159  break;
4160 
4161  default: g_message(" Skipping interpolation: %s", _(gerbv_interpolation_name(n->interpolation)));
4162  }
4163 
4164  inter_prev = n->interpolation;
4165  }
4166 }
4167 
4168 static void
4169 net_layer_file_report(gerbv_net_t* net, gerbv_image_t* img, gerbv_project_t* prj) {
4170  /* Don't report "no net" to keep log short */
4171  if (net->label != NULL)
4172  g_message(_(" Net label: %s"), net->label->str);
4173 
4174  /* Don't report "no layer name" to keep log short */
4175  if (net->layer->name != NULL)
4176  g_message(_(" Layer name: %s"), net->layer->name);
4177 
4178  /* Search file name in project files array */
4179  for (int i = 0; i <= prj->last_loaded; i++) {
4180  if (img == prj->file[i]->image)
4181  g_message(_(" In file: %s"), prj->file[i]->name);
4182  }
4183 }
4184 
4185 /* Restore report window size and postion */
4186 static void
4187 analyze_window_size_restore(GtkWidget* win) {
4188  GVariant* var;
4189  const gint32* xy;
4190  gsize num;
4191 
4192  if (!screen.settings)
4193  return;
4194 
4195  var = g_settings_get_value(screen.settings, "analyze-window-size");
4196  xy = g_variant_get_fixed_array(var, &num, sizeof(*xy));
4197  if (num == 2)
4198  gtk_window_set_default_size(GTK_WINDOW(win), xy[0], xy[1]);
4199  g_variant_unref(var);
4200 
4201  var = g_settings_get_value(screen.settings, "analyze-window-position");
4202  xy = g_variant_get_fixed_array(var, &num, sizeof(*xy));
4203  if (num == 2)
4204  gtk_window_move(GTK_WINDOW(win), xy[0], xy[1]);
4205  g_variant_unref(var);
4206 }
4207 
4208 /* Store report window size and postion */
4209 static void
4210 analyze_window_size_store(GtkWidget* win, gpointer user_data) {
4211  gint32 xy[2];
4212  GVariant* var;
4213  gboolean is_max;
4214 
4215  if (!screen.settings)
4216  return;
4217 
4218  is_max = FALSE != (GDK_WINDOW_STATE_MAXIMIZED & gdk_window_get_state(gtk_widget_get_window(win)));
4219  if (is_max)
4220  return;
4221 
4222  gtk_window_get_size(GTK_WINDOW(win), (gint*)xy, (gint*)(xy + 1));
4223  var = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, xy, 2, sizeof(xy[0]));
4224  g_settings_set_value(screen.settings, "analyze-window-size", var);
4225 
4226  gtk_window_get_position(GTK_WINDOW(win), (gint*)xy, (gint*)(xy + 1));
4227  var = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, xy, 2, sizeof(xy[0]));
4228  g_settings_set_value(screen.settings, "analyze-window-position", var);
4229 }
Dynamic GUI window creation header info.
Generated at build time authors list.
Generated at build time bugs list.
void open_files(GSList *filenames)
File -> open action requested or file drop event happened.
Definition: callbacks.c:214
void callbacks_bugs_activate(GtkMenuItem *menuitem, gpointer user_data)
The help -> bugs menu item was selected.
Definition: callbacks.c:1736
gboolean callbacks_quit_activate(GtkMenuItem *menuitem, gpointer user_data)
The file -> quit menu item was selected or the user requested the main window to be closed by other m...
Definition: callbacks.c:1583
void callbacks_update_statusbar(void)
Displays additional information in the statusbar.
Definition: callbacks.c:3531
void callbacks_open_activate(GtkMenuItem *menuitem, gpointer user_data)
The file -> open action was selected.
Definition: callbacks.c:327
void callbacks_align_files_from_sel_clicked(GtkMenuItem *menu_item, gpointer user_data)
The edit -> align layers menu item was selected.
Definition: callbacks.c:3335
void callbacks_analyze_active_drill_activate(GtkMenuItem *menuitem, gpointer user_data)
The analyze -> analyze drill file menu item was selected.
Definition: callbacks.c:1324
static gint callbacks_get_selected_row_index(void)
This fcn returns the index of selected layer (selected in the layer window on left).
Definition: callbacks.c:2151
gerbv_image_t * merge_images(int type)
Go through each file and look at visibility, then type.
Definition: callbacks.c:401
void open_project(char *project_filename)
The file -> open menu item was selected.
Definition: callbacks.c:180
void callbacks_analyze_active_gerbers_activate(GtkMenuItem *menuitem, gpointer user_data)
The analyze -> analyze Gerbers menu item was selected.
Definition: callbacks.c:1017
void callbacks_about_activate(GtkMenuItem *menuitem, gpointer user_data)
The help -> about menu item was selected.
Definition: callbacks.c:1647
void callbacks_new_project_activate(GtkMenuItem *menuitem, gpointer user_data)
The file -> new menu item was selected.
Definition: callbacks.c:147
void callbacks_toggle_layer_visibility_activate(GtkMenuItem *menuitem, gpointer user_data)
View/"Toggle visibility layer X" or Current layer/"Toggle visibility" menu item was activated.
Definition: callbacks.c:940
Header info for the GUI callback functions.
Contains basic defines.
Header info for the GDK rendering functions.
Header info for the cairo rendering functions and the related selection calculating functions.
const char * drill_g_code_name(drill_g_code_t g_code)
Return drill G-code name by code number.
Definition: drill.c:2020
const char * drill_m_code_name(drill_m_code_t m_code)
Return drill M-code name by code number.
Definition: drill.c:2062
Header info for the Excellon drill parsing functions.
void gerbv_drill_stats_destroy(gerbv_drill_stats_t *stats)
Definition: drill_stats.c:101
gboolean gerbv_export_drill_file_from_image(const gchar *filename, gerbv_image_t *inputImage, gerbv_user_transformation_t *transform)
Export an image to a new file in Excellon drill format.
Definition: export-drill.c:43
gboolean gerbv_export_dxf_file_from_image(const gchar *file_name, gerbv_image_t *input_img, gerbv_user_transformation_t *trans)
Export an image to a new file in DXF format.
Definition: export-dxf.cpp:77
gboolean gerbv_export_geda_pcb_file_from_image(const gchar *file_name, gerbv_image_t *input_img, gerbv_user_transformation_t *trans)
Export an image to a new file in gEDA PCB format.
void gerbv_export_png_file_from_project(gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo, const gchar *filename)
Render a project to a PNG file using user-specified render info.
Definition: export-image.c:89
void gerbv_export_svg_file_from_project_autoscaled(gerbv_project_t *gerbvProject, const gchar *filename)
Render a project to a SVG file, autoscaling the layers to fit inside the specified image dimensions.
Definition: export-image.c:133
void gerbv_export_postscript_file_from_project_autoscaled(gerbv_project_t *gerbvProject, const gchar *filename)
Render a project to a Postscript file, autoscaling the layers to fit inside the specified image dimen...
Definition: export-image.c:119
void gerbv_export_pdf_file_from_project_autoscaled(gerbv_project_t *gerbvProject, const gchar *filename)
Render a project to a PDF file, autoscaling the layers to fit inside the specified image dimensions.
Definition: export-image.c:104
void gerbv_export_png_file_from_project_autoscaled(gerbv_project_t *gerbvProject, int widthInPixels, int heightInPixels, const gchar *filename)
Render a project to a PNG file, autoscaling the layers to fit inside the specified image dimensions.
Definition: export-image.c:77
gboolean gerbv_export_isel_drill_file_from_image(const gchar *filename, gerbv_image_t *inputImage, gerbv_user_transformation_t *transform)
Export an image to a new file in ISEL NCP drill format.
gboolean gerbv_export_rs274x_file_from_image(const gchar *filename, gerbv_image_t *inputImage, gerbv_user_transformation_t *transform)
Export an image to a new file in RS274X format.
void gerbv_destroy_image(gerbv_image_t *image)
Free an image structure.
Definition: gerb_image.c:104
void gerbv_image_copy_image(gerbv_image_t *sourceImage, gerbv_user_transformation_t *transform, gerbv_image_t *destinationImage)
Copy an image into an existing image, effectively merging the two together.
Definition: gerb_image.c:854
void gerbv_image_delete_net(gerbv_net_t *currentNet)
Delete a net in an existing image.
Definition: gerb_image.c:904
gerbv_image_t * gerbv_image_duplicate_image(gerbv_image_t *sourceImage, gerbv_user_transformation_t *transform)
Duplicate an existing image and return the new copy.
Definition: gerb_image.c:815
void gerbv_stats_destroy(gerbv_stats_t *stats)
Definition: gerb_stats.c:104
const char * gerber_d_code_name(int d_code)
Return Gerber D-code name by code number.
Definition: gerber.c:2578
const char * gerber_g_code_name(int g_code)
Return Gerber G-code name by code number.
Definition: gerber.c:2589
const char * gerber_m_code_name(int m_code)
Return Gerber M-code name by code number.
Definition: gerber.c:2615
void gerbv_open_layer_from_filename(gerbv_project_t *gerbvProject, const gchar *filename)
Open a file, parse the contents, and add a new layer to an existing project.
Definition: gerbv.c:214
const char * gerbv_interpolation_name(gerbv_interpolation_t interp)
Return string name of gerbv_interpolation_t interpolation.
Definition: gerbv.c:97
gerbv_fileinfo_t * gerbv_get_fileinfo_for_image(const gerbv_image_t *image, const gerbv_project_t *project)
Definition: gerbv.c:1018
const char * gerbv_aperture_type_name(gerbv_aperture_type_t type)
Return string name of gerbv_aperture_type_t aperture type.
Definition: gerbv.c:72
void gerbv_render_zoom_to_fit_display(gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo)
Calculate the zoom and translations to fit the rendered scene inside the given scene size.
Definition: gerbv.c:683
int gerbv_transform_coord_for_image(double *x, double *y, const gerbv_image_t *image, const gerbv_project_t *project)
Definition: gerbv.c:1056
The main header file for the libgerbv library.
@ GERBV_APERTURE_STATE_OFF
Definition: gerbv.h:171
@ GERBV_APERTURE_STATE_ON
Definition: gerbv.h:172
@ GERBV_APERTURE_STATE_FLASH
Definition: gerbv.h:173
gerbv_render_types_t
Definition: gerbv.h:367
@ GERBV_RENDER_TYPE_CAIRO_HIGH_QUALITY
Definition: gerbv.h:371
@ GERBV_RENDER_TYPE_GDK_XOR
Definition: gerbv.h:369
@ GERBV_RENDER_TYPE_GDK
Definition: gerbv.h:368
@ GERBV_RENDER_TYPE_CAIRO_NORMAL
Definition: gerbv.h:370
gerbv_message_type_t
Definition: gerbv.h:140
@ GERBV_MESSAGE_ERROR
Definition: gerbv.h:142
@ GERBV_MESSAGE_NOTE
Definition: gerbv.h:144
@ GERBV_MESSAGE_FATAL
Definition: gerbv.h:141
@ GERBV_MESSAGE_WARNING
Definition: gerbv.h:143
gerbv_aperture_type_t
Definition: gerbv.h:150
@ GERBV_APTYPE_MACRO_LINE20
Definition: gerbv.h:162
@ GERBV_APTYPE_MACRO_LINE21
Definition: gerbv.h:163
@ GERBV_APTYPE_OVAL
Definition: gerbv.h:154
@ GERBV_APTYPE_MACRO_OUTLINE
Definition: gerbv.h:158
@ GERBV_APTYPE_MACRO_CIRCLE
Definition: gerbv.h:157
@ GERBV_APTYPE_MACRO
Definition: gerbv.h:156
@ GERBV_APTYPE_CIRCLE
Definition: gerbv.h:152
@ GERBV_APTYPE_MACRO_POLYGON
Definition: gerbv.h:159
@ GERBV_APTYPE_RECTANGLE
Definition: gerbv.h:153
@ GERBV_APTYPE_MACRO_THERMAL
Definition: gerbv.h:161
@ GERBV_APTYPE_MACRO_LINE22
Definition: gerbv.h:164
@ GERBV_APTYPE_MACRO_MOIRE
Definition: gerbv.h:160
gerbv_interpolation_t
Definition: gerbv.h:293
@ GERBV_INTERPOLATION_LINEARx01
Definition: gerbv.h:296
@ GERBV_INTERPOLATION_PAREA_START
Definition: gerbv.h:300
@ GERBV_INTERPOLATION_LINEARx001
Definition: gerbv.h:297
@ GERBV_INTERPOLATION_CW_CIRCULAR
Definition: gerbv.h:298
@ GERBV_INTERPOLATION_PAREA_END
Definition: gerbv.h:301
@ GERBV_INTERPOLATION_LINEARx10
Definition: gerbv.h:295
@ GERBV_INTERPOLATION_CCW_CIRCULAR
Definition: gerbv.h:299
@ GERBV_INTERPOLATION_LINEARx1
Definition: gerbv.h:294
gerbv_layertype_t
Definition: gerbv.h:322
@ GERBV_LAYERTYPE_RS274X
Definition: gerbv.h:323
@ GERBV_LAYERTYPE_DRILL
Definition: gerbv.h:324
int interface_reopen_question(GSList *fns, GSList *fns_is_mod, GSList *fns_cnt, GSList *fns_lay_num)
This dialog box shows a text message with three buttons in the case if the file to be open was alread...
Definition: interface.c:1976
void interface_show_alert_dialog(gchar *primaryText, gchar *secondaryText, gboolean show_checkbox, gboolean *ask_to_show_again)
This dialog box shows a textmessage and one button: "OK".
Definition: interface.c:2093
gboolean interface_get_alert_dialog_response(const gchar *primaryText, const gchar *secondaryText, gboolean show_checkbox, gboolean *ask_to_show_again, const gchar *true_button_label, const gchar *false_button_label)
This dialog box shows a message and two buttons: "True" and "False".
Definition: interface.c:1772
Header info for the GUI building functions for Gerber Viewer.
gerbv_project_t * mainProject
Global state variable to keep track of what's happening on the screen.
Definition: main.c:169
Header info for common structs and functions used for the GUI application.
int project_is_gerbv_project(const char *filename, gboolean *ret)
Checks whether the supplied file look like a gerbv project by reading the first line and checking if ...
Definition: project.c:855
Header info for loading and saving project files.
void render_calculate_zoom_from_outline(GtkWidget *widget, GdkEventButton *event)
Will determine the outline of the zoomed regions.
Definition: render.c:139
gerbv_stats_t * generate_gerber_analysis(void)
Definition: render.c:615
void render_draw_measure_distance(void)
Displays a measured distance graphically on screen and in statusbar.
Definition: render.c:336
void render_toggle_measure_line(void)
Draws/erases measure line.
Definition: render.c:311
gerbv_drill_stats_t * generate_drill_analysis(void)
Definition: render.c:640
Header info for the rendering support functions for gerbv.
Header info for the selection support functions for libgerbv.
guint16 alpha
Definition: gerbv.h:748
gchar * fullPathname
Definition: gerbv.h:751
gerbv_image_t * image
Definition: gerbv.h:746
gerbv_user_transformation_t transform
Definition: gerbv.h:754
gchar * name
Definition: gerbv.h:752
GdkColor color
Definition: gerbv.h:747
gboolean layer_dirty
Definition: gerbv.h:755
gboolean isVisible
Definition: gerbv.h:749
gerbv_layertype_t layertype
Definition: gerbv.h:732
gerbv_aperture_t * aperture[APERTURE_MAX]
Definition: gerbv.h:733
gerbv_image_info_t * info
Definition: gerbv.h:738
gchar * name
Definition: gerbv.h:650
gerbv_layer_t * layer
Definition: gerbv.h:679
double stop_y
Definition: gerbv.h:671
GString * label
Definition: gerbv.h:678
gerbv_aperture_state_t aperture_state
Definition: gerbv.h:674
double stop_x
Definition: gerbv.h:670
double start_x
Definition: gerbv.h:668
struct gerbv_net * next
Definition: gerbv.h:677
double start_y
Definition: gerbv.h:669
gerbv_interpolation_t interpolation
Definition: gerbv.h:675
gerbv_cirseg_t * cirseg
Definition: gerbv.h:676
int aperture
Definition: gerbv.h:673
gerbv_fileinfo_t ** file
Definition: gerbv.h:763
gchar * project
Definition: gerbv.h:772
gboolean show_invisible_selection
Definition: gerbv.h:768
GdkColor background
Definition: gerbv.h:761
gboolean check_before_delete
Definition: gerbv.h:767
gchar * path
Definition: gerbv.h:769
int max_files
Definition: gerbv.h:762
int last_loaded
Definition: gerbv.h:765
gdouble lowerLeftY
Definition: gerbv.h:788
gboolean show_cross_on_drill_holes
Definition: gerbv.h:792
gdouble scaleFactorX
Definition: gerbv.h:785
gint displayHeight
Definition: gerbv.h:791
gdouble lowerLeftX
Definition: gerbv.h:787
gerbv_render_types_t renderType
Definition: gerbv.h:789
gdouble scaleFactorY
Definition: gerbv.h:786
Header info for GTK widgets table functions.