2 * gEDA - GNU Electronic Design Automation
3 * This file is a part of gerbv.
5 * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
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.
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.
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
25 \brief Callback functions for the GUI widgets
39 #include <gdk/gdkkeysyms.h>
61 #include "callbacks.h"
62 #include "interface.h"
63 #include "attribute.h"
70 #include <cairo-win32.h>
72 #include <cairo-xlib.h>
76 #define dprintf if(DEBUG) printf
78 /* This default extension should really not be changed, but if it absolutely
79 * must change, the ../win32/gerbv.nsi.in *must* be changed to reflect that.
80 * Just grep for the extension (gvp) and change it in two places in that file.
82 #define GERBV_PROJECT_FILE_NAME "Gerbv Project"
83 #define GERBV_PROJECT_FILE_EXT ".gvp"
84 #define GERBV_PROJECT_FILE_PAT "*.gvp"
86 #define SAVE_PROJECT 0
87 #define SAVE_AS_PROJECT 1
88 #define OPEN_PROJECT 2
89 # define _(String) (String)
91 /**Global variable to keep track of what's happening on the screen.
92 Declared extern in gerbv_screen.h
94 extern gerbv_screen_t screen
;
95 extern gerbv_render_info_t screenRenderInfo
;
98 /* These are the names of the valid apertures. These
99 * values are used in several places in this file.
100 * Please keep this in sync with the gerbv_aperture_type_t
101 * enum defined in gerbv.h */
102 char *ap_names
[] = {"NONE",
105 "OVAL", /* an ovular (obround) aperture */
106 "POLYGON", /* a polygon aperture */
107 "MACRO", /* a RS274X macro */
108 "MACRO_CIRCLE", /* a RS274X circle macro */
109 "MACRO_OUTLINE", /* a RS274X outline macro */
110 "MACRO_POLYGON", /* a RS274X polygon macro */
111 "MACRO_MOIRE", /* a RS274X moire macro */
112 "MACRO_THERMAL", /* a RS274X thermal macro */
113 "MACRO_LINE20", /* a RS274X line (code 20) macro */
114 "MACRO_LINE21", /* a RS274X line (code 21) macro */
115 "MACRO_LINE22" /* a RS274X line (code 22) macro */
118 gint
callbacks_get_selected_row_index (void);
120 /* --------------------------------------------------------- */
122 callbacks_generate_alert_dialog (gchar
*primaryText
, gchar
*secondaryText
){
123 GtkWidget
*dialog
, *label
;
125 dialog
= gtk_dialog_new_with_buttons (primaryText
,
126 (GtkWindow
*)screen
.win
.topLevelWindow
,
127 GTK_DIALOG_DESTROY_WITH_PARENT
,
128 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
129 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
131 label
= gtk_label_new (secondaryText
);
132 /* Add the label, and show everything we've added to the dialog. */
133 gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog
)->vbox
),
135 gtk_widget_show_all (dialog
);
139 /* --------------------------------------------------------- */
141 * The file -> new menu item was selected. Create new
146 callbacks_new_activate (GtkMenuItem
*menuitem
,
149 if (mainProject
->last_loaded
>= 0) {
150 if (!interface_get_alert_dialog_response (
151 "Do you want to close any open layers and start a new project?",
152 "Starting a new project will cause all currently open layers to be closed. Any unsaved changes will be lost.",
157 /* Unload all layers and then clear layer window */
158 gerbv_unload_all_layers (mainProject
);
159 callbacks_update_layer_tree ();
160 render_clear_selection_buffer ();
162 /* Destroy project info */
163 if (mainProject
->project
) {
164 g_free(mainProject
->project
);
165 mainProject
->project
= NULL
;
167 render_refresh_rendered_image_on_screen();
171 /* --------------------------------------------------------- */
173 * The file -> open menu item was selected. Open a
178 callbacks_open_project_activate (GtkMenuItem
*menuitem
,
181 gchar
*filename
=NULL
;
182 GtkFileFilter
* filter
;
184 if (mainProject
->last_loaded
>= 0) {
185 if (!interface_get_alert_dialog_response (
186 "Do you want to close any open layers and load an existing project?",
187 "Loading a project will cause all currently open layers to be closed. Any unsaved changes will be lost.",
194 gtk_file_chooser_dialog_new ("Open project file...",
196 GTK_FILE_CHOOSER_ACTION_OPEN
,
197 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
198 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
200 gtk_file_chooser_set_current_folder ((GtkFileChooser
*) screen
.win
.gerber
,
203 filter
= gtk_file_filter_new();
204 gtk_file_filter_set_name(filter
, GERBV_PROJECT_FILE_NAME
);
205 gtk_file_filter_add_pattern(filter
, GERBV_PROJECT_FILE_PAT
);
206 gtk_file_chooser_add_filter ((GtkFileChooser
*) screen
.win
.gerber
,
209 filter
= gtk_file_filter_new();
210 gtk_file_filter_set_name(filter
, "All");
211 gtk_file_filter_add_pattern(filter
, "*");
212 gtk_file_chooser_add_filter ((GtkFileChooser
*) screen
.win
.gerber
,
215 gtk_widget_show (screen
.win
.gerber
);
216 if (gtk_dialog_run ((GtkDialog
*)screen
.win
.gerber
) == GTK_RESPONSE_ACCEPT
) {
218 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (screen
.win
.gerber
));
219 /* update the last folder */
220 g_free (mainProject
->path
);
221 mainProject
->path
= gtk_file_chooser_get_current_folder ((GtkFileChooser
*) screen
.win
.gerber
);
223 gtk_widget_destroy (screen
.win
.gerber
);
226 gerbv_unload_all_layers (mainProject
);
227 main_open_project_from_filename (mainProject
, filename
);
229 gerbv_render_zoom_to_fit_display (mainProject
, &screenRenderInfo
);
230 render_refresh_rendered_image_on_screen();
231 callbacks_update_layer_tree();
237 /* --------------------------------------------------------- */
239 * The file -> open layer menu item was selected. Open a
240 * layer (or layers) from a file.
244 callbacks_open_layer_activate (GtkMenuItem
*menuitem
,
247 GSList
*filenames
=NULL
;
248 GSList
*filename
=NULL
;
251 gtk_file_chooser_dialog_new ("Open Gerber, drill, or pick & place file(s)...",
253 GTK_FILE_CHOOSER_ACTION_OPEN
,
254 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
255 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
258 gtk_file_chooser_set_select_multiple((GtkFileChooser
*) screen
.win
.gerber
, TRUE
);
259 gtk_file_chooser_set_current_folder ((GtkFileChooser
*) screen
.win
.gerber
,
261 gtk_widget_show (screen
.win
.gerber
);
262 if (gtk_dialog_run ((GtkDialog
*)screen
.win
.gerber
) == GTK_RESPONSE_ACCEPT
) {
264 gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (screen
.win
.gerber
));
265 /* update the last folder */
266 g_free (mainProject
->path
);
267 mainProject
->path
= gtk_file_chooser_get_current_folder ((GtkFileChooser
*) screen
.win
.gerber
);
269 gtk_widget_destroy (screen
.win
.gerber
);
271 /* Now try to open all gerbers specified */
272 for (filename
=filenames
; filename
; filename
=filename
->next
) {
273 gerbv_open_layer_from_filename (mainProject
, filename
->data
);
275 g_slist_free(filenames
);
277 gerbv_render_zoom_to_fit_display (mainProject
, &screenRenderInfo
);
278 render_refresh_rendered_image_on_screen();
279 callbacks_update_layer_tree();
284 /* --------------------------------------------------------- */
286 callbacks_revert_activate (GtkMenuItem
*menuitem
,
289 gerbv_revert_all_files (mainProject
);
290 render_clear_selection_buffer();
291 callbacks_update_selected_object_message(FALSE
);
292 render_refresh_rendered_image_on_screen();
293 callbacks_update_layer_tree();
296 /* --------------------------------------------------------- */
298 callbacks_save_project_activate (GtkMenuItem
*menuitem
,
301 if (mainProject
->project
)
302 main_save_project_from_filename (mainProject
, mainProject
->project
);
304 callbacks_generic_save_activate (menuitem
, (gpointer
) CALLBACKS_SAVE_PROJECT_AS
);
305 callbacks_update_layer_tree();
309 /* --------------------------------------------------------- */
311 callbacks_save_layer_activate (GtkMenuItem
*menuitem
,
314 /* first figure out which layer in the layer side menu is selected */
315 gint index
=callbacks_get_selected_row_index();
317 /* Now save that layer */
319 if (!gerbv_save_layer_from_index (mainProject
, index
, mainProject
->file
[index
]->fullPathname
)) {
320 interface_show_alert_dialog("Gerbv cannot export this file type",
324 mainProject
->file
[index
]->layer_dirty
= FALSE
;
325 callbacks_update_layer_tree();
329 callbacks_update_layer_tree();
333 /* --------------------------------------------------------- */
335 callbacks_generic_save_activate (GtkMenuItem
*menuitem
,
338 gchar
*filename
=NULL
;
339 gint processType
= GPOINTER_TO_INT (user_data
);
340 gchar
*windowTitle
=NULL
;
341 GtkFileFilter
* filter
;
343 if (processType
== CALLBACKS_SAVE_PROJECT_AS
)
344 windowTitle
= g_strdup ("Save project as...");
345 else if (processType
== CALLBACKS_SAVE_FILE_PS
)
346 windowTitle
= g_strdup ("Export PS file as...");
347 else if (processType
== CALLBACKS_SAVE_FILE_PDF
)
348 windowTitle
= g_strdup ("Export PDF file as...");
349 else if (processType
== CALLBACKS_SAVE_FILE_SVG
)
350 windowTitle
= g_strdup ("Export SVG file as...");
351 else if (processType
== CALLBACKS_SAVE_FILE_PNG
)
352 windowTitle
= g_strdup ("Export PNG file as...");
353 else if (processType
== CALLBACKS_SAVE_FILE_RS274X
)
354 windowTitle
= g_strdup ("Export RS-274X file as...");
355 else if (processType
== CALLBACKS_SAVE_FILE_DRILL
)
356 windowTitle
= g_strdup ("Export Excellon drill file as...");
357 else if (processType
== CALLBACKS_SAVE_LAYER_AS
)
358 windowTitle
= g_strdup ("Save layer as...");
361 gtk_file_chooser_dialog_new (windowTitle
, NULL
,
362 GTK_FILE_CHOOSER_ACTION_SAVE
,
363 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
364 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
366 g_free (windowTitle
);
368 /* if we're saving or exporting a layer, start off in the location of the
370 if (processType
!= CALLBACKS_SAVE_PROJECT_AS
) {
371 gint index
=callbacks_get_selected_row_index();
373 gchar
*dirName
= g_path_get_dirname (mainProject
->file
[index
]->fullPathname
);
374 gtk_file_chooser_set_current_folder ((GtkFileChooser
*) screen
.win
.gerber
,
380 if (processType
== CALLBACKS_SAVE_PROJECT_AS
) {
381 filter
= gtk_file_filter_new();
382 gtk_file_filter_set_name(filter
, GERBV_PROJECT_FILE_NAME
);
383 gtk_file_filter_add_pattern(filter
, GERBV_PROJECT_FILE_PAT
);
384 gtk_file_chooser_add_filter ((GtkFileChooser
*) screen
.win
.gerber
,
387 filter
= gtk_file_filter_new();
388 gtk_file_filter_set_name(filter
, "All");
389 gtk_file_filter_add_pattern(filter
, "*");
390 gtk_file_chooser_add_filter ((GtkFileChooser
*) screen
.win
.gerber
,
393 gtk_file_chooser_set_current_name ((GtkFileChooser
*) screen
.win
.gerber
,
394 "untitled" GERBV_PROJECT_FILE_EXT
);
397 gtk_widget_show (screen
.win
.gerber
);
398 if (gtk_dialog_run ((GtkDialog
*)screen
.win
.gerber
) == GTK_RESPONSE_ACCEPT
) {
399 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (screen
.win
.gerber
));
401 gtk_widget_destroy (screen
.win
.gerber
);
404 if (processType
== CALLBACKS_SAVE_PROJECT_AS
) {
405 main_save_as_project_from_filename (mainProject
, filename
);
406 rename_main_window(filename
, NULL
);
408 else if (processType
== CALLBACKS_SAVE_FILE_PS
)
409 gerbv_export_postscript_file_from_project_autoscaled (mainProject
, filename
);
410 else if (processType
== CALLBACKS_SAVE_FILE_PDF
)
411 gerbv_export_pdf_file_from_project_autoscaled (mainProject
, filename
);
412 else if (processType
== CALLBACKS_SAVE_FILE_SVG
)
413 gerbv_export_svg_file_from_project_autoscaled (mainProject
, filename
);
414 else if (processType
== CALLBACKS_SAVE_FILE_PNG
)
415 gerbv_export_png_file_from_project_autoscaled (mainProject
,
416 screenRenderInfo
.displayWidth
, screenRenderInfo
.displayHeight
,
418 else if (processType
== CALLBACKS_SAVE_LAYER_AS
) {
419 gint index
=callbacks_get_selected_row_index();
421 gerbv_save_layer_from_index (mainProject
, index
, filename
);
422 /* rename the file path in the index, so future saves will reference the new file path */
423 g_free (mainProject
->file
[index
]->fullPathname
);
424 mainProject
->file
[index
]->fullPathname
= g_strdup (filename
);
425 g_free (mainProject
->file
[index
]->name
);
426 mainProject
->file
[index
]->name
= g_path_get_basename (filename
);
428 else if (processType
== CALLBACKS_SAVE_FILE_RS274X
) {
429 gint index
=callbacks_get_selected_row_index();
431 gerbv_export_rs274x_file_from_image (filename
, mainProject
->file
[index
]->image
,
432 &mainProject
->file
[index
]->transform
);
434 else if (processType
== CALLBACKS_SAVE_FILE_DRILL
) {
435 gint index
=callbacks_get_selected_row_index();
437 gerbv_export_drill_file_from_image (filename
, mainProject
->file
[index
]->image
,
438 &mainProject
->file
[index
]->transform
);
442 callbacks_update_layer_tree();
446 /* --------------------------------------------------------- */
447 #if GTK_CHECK_VERSION(2,10,0)
450 callbacks_begin_print (GtkPrintOperation
*operation
, GtkPrintContext
*context
,
451 gpointer user_data
) {
452 gtk_print_operation_set_n_pages (operation
, 1);
456 /* --------------------------------------------------------- */
458 callbacks_print_render_page (GtkPrintOperation
*operation
,
459 GtkPrintContext
*context
,
463 GtkPrintSettings
*pSettings
= gtk_print_operation_get_print_settings (operation
);
464 gerbv_render_info_t renderInfo
= {1.0, 1.0, 0, 0, 3,
465 (gint
) gtk_print_context_get_width (context
),
466 (gint
) gtk_print_context_get_height (context
)};
470 /* have to assume x and y resolutions are the same for now, since we
471 don't support differing scales in the gerb_render_info_t struct yet */
472 gdouble xres
= gtk_print_context_get_dpi_x (context
);
473 gdouble yres
= gtk_print_context_get_dpi_y (context
);
474 gdouble scalePercentage
= gtk_print_settings_get_scale (pSettings
);
475 renderInfo
.scaleFactorX
= scalePercentage
/ 100 * xres
;
476 renderInfo
.scaleFactorY
= scalePercentage
/ 100 * yres
;
478 gerbv_render_translate_to_fit_display (mainProject
, &renderInfo
);
479 cr
= gtk_print_context_get_cairo_context (context
);
480 for(i
= 0; i
<= mainProject
->last_loaded
; i
++) {
481 if (mainProject
->file
[i
] && mainProject
->file
[i
]->isVisible
) {
482 //cairo_push_group (cr);
483 gerbv_render_layer_to_cairo_target (cr
, mainProject
->file
[i
], &renderInfo
);
484 //cairo_pop_group_to_source (cr);
485 //cairo_paint_with_alpha (cr, screen.file[i]->alpha);
490 /* --------------------------------------------------------- */
492 callbacks_print_activate (GtkMenuItem
*menuitem
, gpointer user_data
)
494 GtkPrintOperation
*print
;
495 GtkPrintOperationResult res
;
497 print
= gtk_print_operation_new ();
499 g_signal_connect (print
, "begin_print", G_CALLBACK (callbacks_begin_print
), NULL
);
500 g_signal_connect (print
, "draw_page", G_CALLBACK (callbacks_print_render_page
), NULL
);
502 //GtkPrintSettings *pSettings = gtk_print_operation_get_print_settings (print);
504 res
= gtk_print_operation_run (print
, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
,
505 (GtkWindow
*) screen
.win
.topLevelWindow
, NULL
);
507 g_object_unref (print
);
509 #endif /* GTK_CHECK_VERSION(2,10,0) */
511 /* --------------------------------------------------------- */
513 callbacks_zoom_in_activate (GtkMenuItem
*menuitem
,
516 render_zoom_display (ZOOM_IN
, 0, 0, 0);
519 /* --------------------------------------------------------- */
521 callbacks_zoom_out_activate (GtkMenuItem
*menuitem
,
524 render_zoom_display (ZOOM_OUT
, 0, 0, 0);
527 /* --------------------------------------------------------- */
529 callbacks_fit_to_window_activate (GtkMenuItem
*menuitem
,
532 gerbv_render_zoom_to_fit_display (mainProject
, &screenRenderInfo
);
533 render_refresh_rendered_image_on_screen();
537 /* --------------------------------------------------------- */
539 * The analyze -> analyze Gerbers menu item was selected.
540 * Compile statistics on all open Gerber layers and then display
545 callbacks_analyze_active_gerbers_activate(GtkMenuItem
*menuitem
,
548 gerbv_stats_t
*stats_report
;
549 GString
*G_report_string
= g_string_new(NULL
);
550 GString
*D_report_string
= g_string_new(NULL
);
551 GString
*M_report_string
= g_string_new(NULL
);
552 GString
*misc_report_string
= g_string_new(NULL
);
553 GString
*general_report_string
= g_string_new(NULL
);
554 GString
*error_report_string
= g_string_new(NULL
);
555 gerbv_error_list_t
*my_error_list
;
556 gchar
*error_level
= NULL
;
557 GString
*aperture_def_report_string
= g_string_new(NULL
);
558 GString
*aperture_use_report_string
= g_string_new(NULL
);
559 gerbv_aperture_list_t
*my_aperture_list
;
562 /* First get a report of stats & errors accumulated from all layers */
563 stats_report
= generate_gerber_analysis();
565 /* General info report */
566 g_string_printf(general_report_string
,
567 "General information\n");
568 g_string_append_printf(general_report_string
,
569 " Active layer count = %d\n",
570 stats_report
->layer_count
);
571 g_string_append_printf(general_report_string
,
575 for (idx
= 0; idx
<= mainProject
->last_loaded
; idx
++) {
576 if (mainProject
->file
[idx
] &&
577 mainProject
->file
[idx
]->isVisible
&&
578 (mainProject
->file
[idx
]->image
->layertype
== GERBV_LAYERTYPE_RS274X
) ) {
579 g_string_append_printf(general_report_string
,
580 " %-45s %-10d\n", mainProject
->file
[idx
]->name
, idx
+1);
584 /* Error report (goes into general report tab) */
585 if (stats_report
->layer_count
== 0) {
586 g_string_printf(error_report_string
,
587 "\n\nNo Gerber files active (visible)!\n");
588 } else if (stats_report
->error_list
->error_text
== NULL
) {
589 g_string_printf(error_report_string
,
590 "\n\nNo errors found in active Gerber file(s)!\n");
592 g_string_printf(error_report_string
,
593 "\n\nErrors found in active Gerber file(s):\n");
594 for(my_error_list
= stats_report
->error_list
;
595 my_error_list
!= NULL
;
596 my_error_list
= my_error_list
->next
) {
597 switch(my_error_list
->type
) {
598 case GERBV_MESSAGE_FATAL
: /* We should never get this one since the
599 * program should terminate first.... */
600 error_level
= g_strdup_printf("FATAL: ");
602 case GERBV_MESSAGE_ERROR
:
603 error_level
= g_strdup_printf("ERROR: ");
605 case GERBV_MESSAGE_WARNING
:
606 error_level
= g_strdup_printf("WARNING: ");
608 case GERBV_MESSAGE_NOTE
:
609 error_level
= g_strdup_printf("NOTE: ");
612 g_string_append_printf(error_report_string
,
614 my_error_list
->layer
,
616 my_error_list
->error_text
);
622 g_string_append_printf(general_report_string
,
624 error_report_string
->str
);
625 g_string_free(error_report_string
, TRUE
);
627 /* Now compile stats related to reading G codes */
628 g_string_printf(G_report_string
,
629 "G code statistics (all active layers)\n");
630 g_string_append_printf(G_report_string
,
631 "<code> = <number of incidences>\n");
632 g_string_append_printf(G_report_string
,
636 g_string_append_printf(G_report_string
,
639 "1X linear interpolation");
640 g_string_append_printf(G_report_string
,
644 g_string_append_printf(G_report_string
,
647 "CCW interpolation");
648 g_string_append_printf(G_report_string
,
651 "Comment/ignore block");
652 g_string_append_printf(G_report_string
,
655 "10X linear interpolation");
656 g_string_append_printf(G_report_string
,
659 "0.1X linear interpolation");
660 g_string_append_printf(G_report_string
,
663 "0.01X linear interpolation");
664 g_string_append_printf(G_report_string
,
668 g_string_append_printf(G_report_string
,
672 g_string_append_printf(G_report_string
,
676 g_string_append_printf(G_report_string
,
680 g_string_append_printf(G_report_string
,
684 g_string_append_printf(G_report_string
,
688 g_string_append_printf(G_report_string
,
691 "Disable 360 circ. interpolation");
692 g_string_append_printf(G_report_string
,
695 "Enable 360 circ. interpolation");
696 g_string_append_printf(G_report_string
,
700 g_string_append_printf(G_report_string
,
703 "Incremental units");
704 g_string_append_printf(G_report_string
,
705 "Unknown G codes = %d\n",
706 stats_report
->G_unknown
);
709 g_string_printf(D_report_string
, "D code statistics (all active layers)\n");
710 g_string_append_printf(D_report_string
,
711 "<code> = <number of incidences>\n");
712 g_string_append_printf(D_report_string
,
716 g_string_append_printf(D_report_string
,
720 g_string_append_printf(D_report_string
,
724 g_string_append_printf(D_report_string
,
725 "Undefined D codes = %d\n",
726 stats_report
->D_unknown
);
727 g_string_append_printf(D_report_string
,
728 "D code Errors = %d\n",
729 stats_report
->D_error
);
732 g_string_printf(M_report_string
, "M code statistics (all active layers)\n");
733 g_string_append_printf(M_report_string
,
734 "<code> = <number of incidences>\n");
735 g_string_append_printf(M_report_string
,
739 g_string_append_printf(M_report_string
,
743 g_string_append_printf(M_report_string
,
747 g_string_append_printf(M_report_string
,
748 "Unknown M codes = %d\n",
749 stats_report
->M_unknown
);
752 g_string_printf(misc_report_string
, "Misc code statistics (all active layers)\n");
753 g_string_append_printf(misc_report_string
,
754 "<code> = <number of incidences>\n");
755 g_string_append_printf(misc_report_string
,
756 "X = %d\n", stats_report
->X
);
757 g_string_append_printf(misc_report_string
,
758 "Y = %d\n", stats_report
->Y
);
759 g_string_append_printf(misc_report_string
,
760 "I = %d\n", stats_report
->I
);
761 g_string_append_printf(misc_report_string
,
762 "J = %d\n", stats_report
->J
);
763 g_string_append_printf(misc_report_string
,
764 "* = %d\n", stats_report
->star
);
765 g_string_append_printf(misc_report_string
,
766 "Unknown codes = %d\n",
767 stats_report
->unknown
);
769 /* Report apertures defined in input files. */
771 if (stats_report
->aperture_list
->number
== -1) {
772 g_string_printf(aperture_def_report_string
,
773 "No aperture definitions found in Gerber file(s)!\n");
775 g_string_printf(aperture_def_report_string
,
776 "Apertures defined in Gerber file(s) (by layer)\n");
777 g_string_append_printf(aperture_def_report_string
,
778 " %-6s %-8s %12s %8s %8s %8s\n",
786 for(my_aperture_list
= stats_report
->aperture_list
;
787 my_aperture_list
!= NULL
;
788 my_aperture_list
= my_aperture_list
->next
) {
790 g_string_append_printf(aperture_def_report_string
,
791 " %-6d D%-4d%13s %8.3f %8.3f %8.3f\n",
792 my_aperture_list
->layer
,
793 my_aperture_list
->number
,
794 ap_names
[my_aperture_list
->type
],
795 my_aperture_list
->parameter
[0],
796 my_aperture_list
->parameter
[1],
797 my_aperture_list
->parameter
[2]
802 /* Report apertures usage count in input files. */
803 if (stats_report
->D_code_list
->number
== -1) {
804 g_string_printf(aperture_use_report_string
,
805 "No apertures used in Gerber file(s)!\n");
808 /* Now add list of user-defined D codes (apertures) */
810 g_string_printf(aperture_use_report_string
,
811 "Apertures used in Gerber file(s) (all active layers)\n");
812 g_string_append_printf(aperture_use_report_string
,
813 "<aperture code> = <number of uses>\n");
814 for (my_aperture_list
= stats_report
->D_code_list
;
815 my_aperture_list
!= NULL
;
816 my_aperture_list
= my_aperture_list
->next
) {
818 g_string_append_printf(aperture_use_report_string
,
820 my_aperture_list
->number
,
821 my_aperture_list
->count
827 /* Create top level dialog window for report */
828 GtkWidget
*analyze_active_gerbers
;
829 analyze_active_gerbers
= gtk_dialog_new_with_buttons("Gerber codes report",
831 GTK_DIALOG_DESTROY_WITH_PARENT
,
835 gtk_container_set_border_width (GTK_CONTAINER (analyze_active_gerbers
), 5);
837 gtk_dialog_set_default_response (GTK_DIALOG(analyze_active_gerbers
),
838 GTK_RESPONSE_ACCEPT
);
839 g_signal_connect (G_OBJECT(analyze_active_gerbers
),
841 G_CALLBACK (gtk_widget_destroy
),
842 GTK_WIDGET(analyze_active_gerbers
));
844 /* Use fixed width font for all reports */
845 PangoFontDescription
*font
=
846 pango_font_description_from_string ("monospace");
848 /* Create GtkLabel to hold general report text */
849 GtkWidget
*general_report_label
= gtk_label_new (general_report_string
->str
);
850 g_string_free (general_report_string
, TRUE
);
851 gtk_misc_set_alignment(GTK_MISC(general_report_label
), 0, 0);
852 gtk_misc_set_padding(GTK_MISC(general_report_label
), 13, 13);
853 gtk_label_set_selectable(GTK_LABEL(general_report_label
), TRUE
);
854 gtk_widget_modify_font (GTK_WIDGET(general_report_label
),
856 /* Put general report text into scrolled window */
857 GtkWidget
*general_code_report_window
= gtk_scrolled_window_new (NULL
, NULL
);
858 /* This throws a warning. Must find different approach.... */
859 gtk_widget_set_size_request(GTK_WIDGET(general_code_report_window
),
862 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(general_code_report_window
),
863 GTK_WIDGET(general_report_label
));
865 /* Create GtkLabel to hold G code text */
866 GtkWidget
*G_report_label
= gtk_label_new (G_report_string
->str
);
867 g_string_free (G_report_string
, TRUE
);
868 gtk_misc_set_alignment(GTK_MISC(G_report_label
), 0, 0);
869 gtk_misc_set_padding(GTK_MISC(G_report_label
), 13, 13);
870 gtk_label_set_selectable(GTK_LABEL(G_report_label
), TRUE
);
871 gtk_widget_modify_font (GTK_WIDGET(G_report_label
),
874 /* Create GtkLabel to hold D code text */
875 GtkWidget
*D_report_label
= gtk_label_new (D_report_string
->str
);
876 g_string_free (D_report_string
, TRUE
);
877 gtk_misc_set_alignment(GTK_MISC(D_report_label
), 0, 0);
878 gtk_misc_set_padding(GTK_MISC(D_report_label
), 13, 13);
879 gtk_label_set_selectable(GTK_LABEL(D_report_label
), TRUE
);
880 gtk_widget_modify_font (GTK_WIDGET(D_report_label
),
883 /* Create GtkLabel to hold M code text */
884 GtkWidget
*M_report_label
= gtk_label_new (M_report_string
->str
);
885 g_string_free (M_report_string
, TRUE
);
886 gtk_misc_set_alignment(GTK_MISC(M_report_label
), 0, 0);
887 gtk_misc_set_padding(GTK_MISC(M_report_label
), 13, 13);
888 gtk_label_set_selectable(GTK_LABEL(M_report_label
), TRUE
);
889 gtk_widget_modify_font (GTK_WIDGET(M_report_label
),
892 /* Create GtkLabel to hold misc code text */
893 GtkWidget
*misc_report_label
= gtk_label_new (misc_report_string
->str
);
894 g_string_free (misc_report_string
, TRUE
);
895 gtk_misc_set_alignment(GTK_MISC(misc_report_label
), 0, 0);
896 gtk_misc_set_padding(GTK_MISC(misc_report_label
), 13, 13);
897 gtk_label_set_selectable(GTK_LABEL(misc_report_label
), TRUE
);
898 gtk_widget_modify_font (GTK_WIDGET(misc_report_label
),
901 /* Create GtkLabel to hold aperture defintion text */
902 GtkWidget
*aperture_def_report_label
= gtk_label_new (aperture_def_report_string
->str
);
903 g_string_free (aperture_def_report_string
, TRUE
);
904 gtk_misc_set_alignment(GTK_MISC(aperture_def_report_label
), 0, 0);
905 gtk_misc_set_padding(GTK_MISC(aperture_def_report_label
), 13, 13);
906 gtk_label_set_selectable(GTK_LABEL(aperture_def_report_label
), TRUE
);
907 gtk_widget_modify_font (GTK_WIDGET(aperture_def_report_label
),
909 /* Put aperture definintion text into scrolled window */
910 GtkWidget
*aperture_def_report_window
= gtk_scrolled_window_new (NULL
, NULL
);
911 /* This throws a warning. Must find different approach.... */
912 gtk_widget_set_size_request(GTK_WIDGET(aperture_def_report_window
),
915 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(aperture_def_report_window
),
916 GTK_WIDGET(aperture_def_report_label
));
918 /* Create GtkLabel to hold aperture use text */
919 GtkWidget
*aperture_use_report_label
= gtk_label_new (aperture_use_report_string
->str
);
920 g_string_free (aperture_use_report_string
, TRUE
);
921 gtk_misc_set_alignment(GTK_MISC(aperture_use_report_label
), 0, 0);
922 gtk_misc_set_padding(GTK_MISC(aperture_use_report_label
), 13, 13);
923 gtk_label_set_selectable(GTK_LABEL(aperture_use_report_label
), TRUE
);
924 gtk_widget_modify_font (GTK_WIDGET(aperture_use_report_label
),
926 /* Put aperture definintion text into scrolled window */
927 GtkWidget
*aperture_use_report_window
= gtk_scrolled_window_new (NULL
, NULL
);
928 /* This throws a warning. Must find different approach.... */
929 gtk_widget_set_size_request(GTK_WIDGET(aperture_use_report_window
),
932 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(aperture_use_report_window
),
933 GTK_WIDGET(aperture_use_report_label
));
935 /* Create tabbed notebook widget and add report label widgets. */
936 GtkWidget
*notebook
= gtk_notebook_new();
938 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
939 GTK_WIDGET(general_code_report_window
),
940 gtk_label_new("General"));
942 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
943 GTK_WIDGET(G_report_label
),
944 gtk_label_new("G codes"));
946 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
947 GTK_WIDGET(D_report_label
),
948 gtk_label_new("D codes"));
950 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
951 GTK_WIDGET(M_report_label
),
952 gtk_label_new("M codes"));
954 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
955 GTK_WIDGET(misc_report_label
),
956 gtk_label_new("Misc. codes"));
958 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
959 GTK_WIDGET(aperture_def_report_window
),
960 gtk_label_new("Aperture definitions"));
962 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
963 GTK_WIDGET(aperture_use_report_window
),
964 gtk_label_new("Aperture usage"));
967 /* Now put notebook into dialog window and show the whole thing */
968 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(analyze_active_gerbers
)->vbox
),
969 GTK_WIDGET(notebook
));
971 gtk_widget_show_all(analyze_active_gerbers
);
973 /* free the stats report */
974 gerbv_stats_destroy (stats_report
);
979 /* --------------------------------------------------------- */
981 * The analyze -> analyze drill file menu item was selected.
982 * Complie statistics on all open drill layers and then display
987 callbacks_analyze_active_drill_activate(GtkMenuItem
*menuitem
,
990 gerbv_drill_stats_t
*stats_report
;
991 GString
*G_report_string
= g_string_new(NULL
);
992 GString
*M_report_string
= g_string_new(NULL
);
993 GString
*misc_report_string
= g_string_new(NULL
);
994 gerbv_drill_list_t
*my_drill_list
;
995 GString
*drill_report_string
= g_string_new(NULL
);
996 GString
*general_report_string
= g_string_new(NULL
);
997 GString
*error_report_string
= g_string_new(NULL
);
998 gerbv_error_list_t
*my_error_list
;
999 gchar
*error_level
= NULL
;
1002 stats_report
= (gerbv_drill_stats_t
*) generate_drill_analysis();
1004 /* General and error window strings */
1005 g_string_printf(general_report_string
, "General information\n");
1006 g_string_append_printf(general_report_string
,
1007 " Active layer count = %d\n",
1008 stats_report
->layer_count
);
1010 g_string_append_printf(general_report_string
,
1011 "\n\nFiles processed:\n");
1012 for (idx
= mainProject
->last_loaded
; idx
>= 0; idx
--) {
1013 if (mainProject
->file
[idx
] &&
1014 mainProject
->file
[idx
]->isVisible
&&
1015 (mainProject
->file
[idx
]->image
->layertype
== GERBV_LAYERTYPE_DRILL
) ) {
1016 g_string_append_printf(general_report_string
,
1018 mainProject
->file
[idx
]->name
);
1023 if (stats_report
->layer_count
== 0) {
1024 g_string_printf(error_report_string
, "\n\nNo drill files active (visible)!\n");
1025 } else if (stats_report
->error_list
->error_text
== NULL
) {
1026 g_string_printf(error_report_string
,
1027 "\n\nNo errors found in active drill file(s)!\n");
1029 g_string_printf(error_report_string
,
1030 "\n\nErrors found in active drill file(s):\n");
1031 for(my_error_list
= stats_report
->error_list
;
1032 my_error_list
!= NULL
;
1033 my_error_list
= my_error_list
->next
) {
1034 switch(my_error_list
->type
) {
1035 case GERBV_MESSAGE_FATAL
: /* We should never get this one since the
1036 * program should terminate first.... */
1037 error_level
= g_strdup_printf("FATAL: ");
1039 case GERBV_MESSAGE_ERROR
:
1040 error_level
= g_strdup_printf("ERROR: ");
1042 case GERBV_MESSAGE_WARNING
:
1043 error_level
= g_strdup_printf("WARNING: ");
1045 case GERBV_MESSAGE_NOTE
:
1046 error_level
= g_strdup_printf("NOTE: ");
1049 g_string_append_printf(error_report_string
,
1051 my_error_list
->layer
,
1053 my_error_list
->error_text
);
1057 g_string_append_printf(general_report_string
,
1058 "%s", error_report_string
->str
);
1059 g_string_free(error_report_string
, TRUE
);
1062 /* G code window strings */
1063 g_string_printf(G_report_string
, "G code statistics (all active layers)\n");
1064 g_string_append_printf(G_report_string
,
1065 "<code> = <number of incidences>\n");
1066 g_string_append_printf(G_report_string
,
1067 "G00 = %-6d (%s)\n",
1070 g_string_append_printf(G_report_string
,
1071 "G01 = %-6d (%s)\n",
1073 "1X linear interpolation");
1074 g_string_append_printf(G_report_string
,
1075 "G02 = %-6d (%s)\n",
1077 "CW interpolation");
1078 g_string_append_printf(G_report_string
,
1079 "G03 = %-6d (%s)\n",
1081 "CCW interpolation");
1082 g_string_append_printf(G_report_string
,
1083 "G04 = %-6d (%s)\n",
1086 g_string_append_printf(G_report_string
,
1087 "G05 = %-6d (%s)\n",
1090 g_string_append_printf(G_report_string
,
1091 "G90 = %-6d (%s)\n",
1094 g_string_append_printf(G_report_string
,
1095 "G91 = %-6d (%s)\n",
1097 "Incremental units");
1098 g_string_append_printf(G_report_string
,
1099 "G93 = %-6d (%s)\n",
1102 g_string_append_printf(G_report_string
,
1103 "Unknown G codes = %d\n",
1104 stats_report
->G_unknown
);
1106 /* M code window strings */
1107 g_string_printf(M_report_string
, "M code statistics (all active layers)\n");
1108 g_string_append_printf(M_report_string
,
1109 "<code> = <number of incidences>\n");
1110 g_string_append_printf(M_report_string
,
1111 "M00 = %-6d (%s)\n",
1114 g_string_append_printf(M_report_string
,
1115 "M01 = %-6d (%s)\n",
1118 g_string_append_printf(M_report_string
,
1119 "M18 = %-6d (%s)\n",
1122 g_string_append_printf(M_report_string
,
1123 "M25 = %-6d (%s)\n",
1126 g_string_append_printf(M_report_string
,
1127 "M30 = %-6d (%s)\n",
1129 "End program rewind");
1130 g_string_append_printf(M_report_string
,
1131 "M31 = %-6d (%s)\n",
1134 g_string_append_printf(M_report_string
,
1135 "M45 = %-6d (%s)\n",
1138 g_string_append_printf(M_report_string
,
1139 "M47 = %-6d (%s)\n",
1141 "Operator message");
1142 g_string_append_printf(M_report_string
,
1143 "M48 = %-6d (%s)\n",
1145 "Begin program header");
1146 g_string_append_printf(M_report_string
,
1147 "M71 = %-6d (%s)\n",
1150 g_string_append_printf(M_report_string
,
1151 "M72 = %-6d (%s)\n",
1154 g_string_append_printf(M_report_string
,
1155 "M95 = %-6d (%s)\n",
1157 "End program header");
1158 g_string_append_printf(M_report_string
,
1159 "M97 = %-6d (%s)\n",
1162 g_string_append_printf(M_report_string
,
1163 "M98 = %-6d (%s)\n",
1166 g_string_append_printf(M_report_string
,
1167 "Unknown M codes = %d\n",
1168 stats_report
->M_unknown
);
1171 /* misc report strings */
1172 g_string_printf(misc_report_string
, "Misc code statistics (all active layers)\n");
1173 g_string_append_printf(misc_report_string
,
1174 "<code> = <number of incidences>\n");
1175 g_string_append_printf(misc_report_string
,
1177 stats_report
->comment
);
1178 g_string_append_printf(misc_report_string
,
1179 "Unknown codes = %d\n",
1180 stats_report
->unknown
);
1182 g_string_append_printf(misc_report_string
,
1187 if (stats_report
->detect
!= NULL
) {
1188 g_string_append_printf(misc_report_string
,
1190 stats_report
->detect
);
1192 /* drill report window strings */
1193 g_string_printf(drill_report_string
, "Drills used (all active layers)\n");
1194 g_string_append_printf(drill_report_string
, "%10s %8s %8s %8s\n",
1195 "Drill no.", "Dia.", "Units", "Count");
1196 for(my_drill_list
= stats_report
->drill_list
;
1197 my_drill_list
!= NULL
;
1198 my_drill_list
= my_drill_list
->next
) {
1199 if (my_drill_list
->drill_num
== -1) break; /* No drill list */
1200 g_string_append_printf(drill_report_string
,
1201 "%10d %8.3f %8s %8d\n",
1202 my_drill_list
->drill_num
,
1203 my_drill_list
->drill_size
,
1204 my_drill_list
->drill_unit
,
1205 my_drill_list
->drill_count
);
1208 g_string_append_printf(drill_report_string
, "Total drill count %d\n",
1209 stats_report
->total_count
);
1211 /* Use fixed width font for all reports */
1212 PangoFontDescription
*font
=
1213 pango_font_description_from_string ("monospace");
1215 /* Create top level dialog window for report */
1216 GtkWidget
*analyze_active_drill
;
1217 analyze_active_drill
= gtk_dialog_new_with_buttons("Drill file codes report",
1219 GTK_DIALOG_DESTROY_WITH_PARENT
,
1221 GTK_RESPONSE_ACCEPT
,
1223 gtk_container_set_border_width (GTK_CONTAINER (analyze_active_drill
), 5);
1224 gtk_dialog_set_default_response (GTK_DIALOG(analyze_active_drill
),
1225 GTK_RESPONSE_ACCEPT
);
1226 g_signal_connect (G_OBJECT(analyze_active_drill
),
1228 G_CALLBACK (gtk_widget_destroy
),
1229 GTK_WIDGET(analyze_active_drill
));
1231 /* Create GtkLabel to hold general report text */
1232 GtkWidget
*general_report_label
= gtk_label_new (general_report_string
->str
);
1233 g_string_free(general_report_string
, TRUE
);
1234 gtk_misc_set_alignment(GTK_MISC(general_report_label
), 0, 0);
1235 gtk_misc_set_padding(GTK_MISC(general_report_label
), 13, 13);
1236 gtk_label_set_selectable(GTK_LABEL(general_report_label
), TRUE
);
1237 gtk_widget_modify_font (GTK_WIDGET(general_report_label
),
1240 /* Create GtkLabel to hold G code text */
1241 GtkWidget
*G_report_label
= gtk_label_new (G_report_string
->str
);
1242 g_string_free(G_report_string
, TRUE
);
1243 gtk_misc_set_alignment(GTK_MISC(G_report_label
), 0, 0);
1244 gtk_misc_set_padding(GTK_MISC(G_report_label
), 13, 13);
1245 gtk_label_set_selectable(GTK_LABEL(G_report_label
), TRUE
);
1246 gtk_widget_modify_font (GTK_WIDGET(G_report_label
),
1249 /* Create GtkLabel to hold M code text */
1250 GtkWidget
*M_report_label
= gtk_label_new (M_report_string
->str
);
1251 g_string_free(M_report_string
, TRUE
);
1252 gtk_misc_set_alignment(GTK_MISC(M_report_label
), 0, 0);
1253 gtk_misc_set_padding(GTK_MISC(M_report_label
), 13, 13);
1254 gtk_label_set_selectable(GTK_LABEL(M_report_label
), TRUE
);
1255 gtk_widget_modify_font (GTK_WIDGET(M_report_label
),
1258 /* Create GtkLabel to hold misc code text */
1259 GtkWidget
*misc_report_label
= gtk_label_new (misc_report_string
->str
);
1260 g_string_free(misc_report_string
, TRUE
);
1261 gtk_misc_set_alignment(GTK_MISC(misc_report_label
), 0, 0);
1262 gtk_misc_set_padding(GTK_MISC(misc_report_label
), 13, 13);
1263 gtk_label_set_selectable(GTK_LABEL(misc_report_label
), TRUE
);
1264 gtk_widget_modify_font (GTK_WIDGET(misc_report_label
),
1267 /* Create GtkLabel to hold drills used text */
1268 GtkWidget
*drill_report_label
= gtk_label_new (drill_report_string
->str
);
1269 g_string_free(drill_report_string
, TRUE
);
1270 gtk_misc_set_alignment(GTK_MISC(drill_report_label
), 0, 0);
1271 gtk_misc_set_padding(GTK_MISC(drill_report_label
), 13, 13);
1272 gtk_label_set_selectable(GTK_LABEL(drill_report_label
), TRUE
);
1273 gtk_widget_modify_font (GTK_WIDGET(drill_report_label
),
1276 /* Create tabbed notebook widget and add report label widgets. */
1277 GtkWidget
*notebook
= gtk_notebook_new();
1279 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
1280 GTK_WIDGET(general_report_label
),
1281 gtk_label_new("General"));
1283 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
1284 GTK_WIDGET(G_report_label
),
1285 gtk_label_new("G codes"));
1287 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
1288 GTK_WIDGET(M_report_label
),
1289 gtk_label_new("M codes"));
1291 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
1292 GTK_WIDGET(misc_report_label
),
1293 gtk_label_new("Misc. codes"));
1295 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
1296 GTK_WIDGET(drill_report_label
),
1297 gtk_label_new("Drills used"));
1299 /* Now put notebook into dialog window and show the whole thing */
1300 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(analyze_active_drill
)->vbox
),
1301 GTK_WIDGET(notebook
));
1302 gtk_widget_show_all(analyze_active_drill
);
1303 gerbv_drill_stats_destroy (stats_report
);
1307 /* --------------------------------------------------------- */
1309 callbacks_control_gerber_options_activate (GtkMenuItem
*menuitem
,
1315 /* --------------------------------------------------------- */
1317 callbacks_online_manual_activate (GtkMenuItem
*menuitem
,
1323 /* --------------------------------------------------------- */
1325 * The file -> quit menu item was selected.
1326 * Check that all changes have been saved, and then quit.
1330 callbacks_quit_activate (GtkMenuItem
*menuitem
,
1333 gboolean layers_dirty
= FALSE
;
1336 for (idx
= 0; idx
<=mainProject
->last_loaded
; idx
++) {
1337 if (mainProject
->file
[idx
] == NULL
) break;
1338 layers_dirty
= layers_dirty
|| mainProject
->file
[idx
]->layer_dirty
;
1342 !interface_get_alert_dialog_response(
1343 "Do you want to close all open layers and quit the program?",
1344 "Quitting the program will cause any unsaved changes to be lost.",
1349 gerbv_unload_all_layers (mainProject
);
1353 /* --------------------------------------------------------- */
1355 * The help -> about menu item was selected.
1356 * Show the about dialog.
1360 callbacks_about_activate (GtkMenuItem
*menuitem
,
1363 GtkWidget
*aboutdialog1
;
1364 /* TRANSLATORS: Replace this string with your names, one name per line. */
1365 /* gchar *translators = _("translator-credits"); */
1367 gchar
*string
= g_strdup_printf ( "gerbv -- a Gerber (RS-274/X) viewer.\n\n"
1368 "This is gerbv version %s\n"
1369 "Compiled on %s at %s\n"
1371 "gerbv is part of the gEDA Project.\n"
1373 "For more information see:\n"
1374 " gerbv homepage: http://gerbv.gpleda.org/\n"
1375 " gEDA homepage: http://gpleda.org/\n"
1376 " gEDA Wiki: http://geda.seul.org/wiki/",
1377 VERSION
, __DATE__
, __TIME__
);
1378 #if GTK_CHECK_VERSION(2,6,0)
1379 gchar
*license
= g_strdup_printf("gerbv -- a Gerber (RS-274/X) viewer.\n\n"
1380 "Copyright (C) 2000-2007 Stefan Petersen\n\n"
1381 "This program is free software: you can redistribute it and/or modify\n"
1382 "it under the terms of the GNU General Public License as published by\n"
1383 "the Free Software Foundation, either version 2 of the License, or\n"
1384 "(at your option) any later version.\n\n"
1385 "This program is distributed in the hope that it will be useful,\n"
1386 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1387 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1388 "GNU General Public License for more details.\n\n"
1389 "You should have received a copy of the GNU General Public License\n"
1390 "along with this program. If not, see <http://www.gnu.org/licenses/>.");
1391 #include "authors.c"
1393 aboutdialog1
= gtk_about_dialog_new ();
1394 gtk_container_set_border_width (GTK_CONTAINER (aboutdialog1
), 5);
1395 gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (aboutdialog1
), VERSION
);
1396 gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (aboutdialog1
), _("Gerbv"));
1398 /* gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG (aboutdialog1), translators); */
1399 gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (aboutdialog1
), string
);
1400 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG (aboutdialog1
), license
);
1402 /* The authors.c file is autogenerated at build time */
1403 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG (aboutdialog1
), authors_string_array
);
1404 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG (aboutdialog1
), "http://gerbv.gpleda.org/");
1406 g_signal_connect (G_OBJECT(aboutdialog1
),"response",
1407 G_CALLBACK (gtk_widget_destroy
), GTK_WIDGET(aboutdialog1
));
1412 aboutdialog1
= gtk_message_dialog_new ( GTK_WINDOW (screen
.win
.topLevelWindow
),
1413 GTK_DIALOG_DESTROY_WITH_PARENT
,
1419 gtk_window_set_title ( GTK_WINDOW (aboutdialog1
), _("About Gerbv"));
1421 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
1422 g_signal_connect_swapped (aboutdialog1
, "response",
1423 G_CALLBACK (gtk_widget_destroy
),
1428 gtk_widget_show_all(GTK_WIDGET(aboutdialog1
));
1432 /* --------------------------------------------------------- */
1434 * The help -> bugs menu item was selected.
1435 * Show the known bugs window
1439 callbacks_bugs_activate (GtkMenuItem
*menuitem
,
1445 /* Create the top level dialog widget with an OK button */
1446 GtkWidget
*bugs_dialog
= gtk_dialog_new_with_buttons("Known bugs in gerbv",
1448 GTK_DIALOG_DESTROY_WITH_PARENT
,
1449 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
1451 gtk_container_set_border_width (GTK_CONTAINER (bugs_dialog
), 5);
1452 gtk_dialog_set_default_response (GTK_DIALOG(bugs_dialog
),
1453 GTK_RESPONSE_ACCEPT
);
1454 g_signal_connect (G_OBJECT(bugs_dialog
), "response",
1455 G_CALLBACK (gtk_widget_destroy
), GTK_WIDGET(bugs_dialog
));
1457 /* First create single bugs_string from bugs_string_array */
1458 GString
*bugs_string
= g_string_new(NULL
);
1459 for (i
=0; bugs_string_array
[i
] != NULL
; i
++) {
1460 g_string_append_printf(bugs_string
,
1462 bugs_string_array
[i
]);
1465 /* Create GtkLabel to hold text */
1466 GtkWidget
*bugs_label
= gtk_label_new (bugs_string
->str
);
1467 g_string_free(bugs_string
, TRUE
);
1468 gtk_misc_set_alignment(GTK_MISC(bugs_label
), 0, 0);
1469 gtk_misc_set_padding(GTK_MISC(bugs_label
), 13, 13);
1471 /* Put text into scrolled window */
1472 GtkWidget
*bugs_window
= gtk_scrolled_window_new (NULL
, NULL
);
1473 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(bugs_window
),
1474 GTK_WIDGET(bugs_label
));
1475 gtk_widget_set_size_request(GTK_WIDGET(bugs_window
), 600, 300);
1476 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(bugs_dialog
)->vbox
),
1477 GTK_WIDGET(bugs_window
));
1479 gtk_widget_show_all(GTK_WIDGET(bugs_dialog
));
1480 gtk_dialog_run(GTK_DIALOG(bugs_dialog
));
1484 /* --------------------------------------------------------- */
1485 gdouble
callbacks_calculate_actual_distance (gdouble inputDimension
) {
1486 gdouble returnValue
= 0.0;
1488 if (screen
.unit
== GERBV_MILS
) {
1489 returnValue
= COORD2MILS(inputDimension
);
1490 } else if (screen
.unit
== GERBV_MMS
) {
1491 returnValue
= COORD2MMS(inputDimension
);
1493 returnValue
= COORD2MILS(inputDimension
)/1000;
1498 /* --------------------------------------------------------- */
1499 void callbacks_update_ruler_pointers (void) {
1500 double xPosition
, yPosition
;
1501 xPosition
= screenRenderInfo
.lowerLeftX
+ (screen
.last_x
/ screenRenderInfo
.scaleFactorX
);
1502 yPosition
= screenRenderInfo
.lowerLeftY
+ ((screenRenderInfo
.displayHeight
- screen
.last_y
) / screenRenderInfo
.scaleFactorY
);
1504 if (!((screen
.unit
== GERBV_MILS
) && ((screenRenderInfo
.scaleFactorX
< 80)||(screenRenderInfo
.scaleFactorY
< 80)))) {
1505 xPosition
= callbacks_calculate_actual_distance (xPosition
);
1506 yPosition
= callbacks_calculate_actual_distance (yPosition
);
1508 g_object_set (G_OBJECT (screen
.win
.hRuler
), "position", xPosition
, NULL
);
1509 g_object_set (G_OBJECT (screen
.win
.vRuler
), "position", yPosition
, NULL
);
1512 /* --------------------------------------------------------- */
1513 void callbacks_update_ruler_scales (void) {
1514 double xStart
, xEnd
, yStart
, yEnd
;
1516 xStart
= screenRenderInfo
.lowerLeftX
;
1517 yStart
= screenRenderInfo
.lowerLeftY
;
1518 xEnd
= screenRenderInfo
.lowerLeftX
+ (screenRenderInfo
.displayWidth
/ screenRenderInfo
.scaleFactorX
);
1519 yEnd
= screenRenderInfo
.lowerLeftY
+ (screenRenderInfo
.displayHeight
/ screenRenderInfo
.scaleFactorY
);
1520 /* mils can get super crowded with large boards, but inches are too
1521 large for most boards. So, we leave mils in for now and just switch
1522 to inches if the scale factor gets too small */
1523 if (!((screen
.unit
== GERBV_MILS
) && ((screenRenderInfo
.scaleFactorX
< 80)||(screenRenderInfo
.scaleFactorY
< 80)))) {
1524 xStart
= callbacks_calculate_actual_distance (xStart
);
1525 xEnd
= callbacks_calculate_actual_distance (xEnd
);
1526 yStart
= callbacks_calculate_actual_distance (yStart
);
1527 yEnd
= callbacks_calculate_actual_distance (yEnd
);
1529 /* make sure the widgets actually exist before setting (in case this gets
1530 called before everything is realized */
1531 if (screen
.win
.hRuler
)
1532 gtk_ruler_set_range (GTK_RULER (screen
.win
.hRuler
), xStart
, xEnd
, 0, xEnd
- xStart
);
1533 /* reverse y min and max, since the ruler starts at the top */
1534 if (screen
.win
.vRuler
)
1535 gtk_ruler_set_range (GTK_RULER (screen
.win
.vRuler
), yEnd
, yStart
, 0, yEnd
- yStart
);
1538 /* --------------------------------------------------------- */
1539 void callbacks_update_scrollbar_limits (void){
1540 gerbv_render_info_t tempRenderInfo
= {0, 0, 0, 0, 3, screenRenderInfo
.displayWidth
,
1541 screenRenderInfo
.displayHeight
};
1543 GtkAdjustment
*hAdjust
= (GtkAdjustment
*)screen
.win
.hAdjustment
;
1544 GtkAdjustment
*vAdjust
= (GtkAdjustment
*)screen
.win
.vAdjustment
;
1545 gerbv_render_zoom_to_fit_display (mainProject
, &tempRenderInfo
);
1546 hAdjust
->lower
= tempRenderInfo
.lowerLeftX
;
1547 hAdjust
->page_increment
= hAdjust
->page_size
;
1548 hAdjust
->step_increment
= hAdjust
->page_size
/ 10.0;
1549 vAdjust
->lower
= tempRenderInfo
.lowerLeftY
;
1550 vAdjust
->page_increment
= vAdjust
->page_size
;
1551 vAdjust
->step_increment
= vAdjust
->page_size
/ 10.0;
1552 hAdjust
->upper
= tempRenderInfo
.lowerLeftX
+ (tempRenderInfo
.displayWidth
/ tempRenderInfo
.scaleFactorX
);
1553 hAdjust
->page_size
= screenRenderInfo
.displayWidth
/ screenRenderInfo
.scaleFactorX
;
1554 vAdjust
->upper
= tempRenderInfo
.lowerLeftY
+ (tempRenderInfo
.displayHeight
/ tempRenderInfo
.scaleFactorY
);
1555 vAdjust
->page_size
= screenRenderInfo
.displayHeight
/ screenRenderInfo
.scaleFactorY
;
1556 callbacks_update_scrollbar_positions ();
1559 /* --------------------------------------------------------- */
1560 void callbacks_update_scrollbar_positions (void){
1561 gdouble positionX
,positionY
;
1563 positionX
= screenRenderInfo
.lowerLeftX
;
1564 if (positionX
< ((GtkAdjustment
*)screen
.win
.hAdjustment
)->lower
)
1565 positionX
= ((GtkAdjustment
*)screen
.win
.hAdjustment
)->lower
;
1566 if (positionX
> (((GtkAdjustment
*)screen
.win
.hAdjustment
)->upper
- ((GtkAdjustment
*)screen
.win
.hAdjustment
)->page_size
))
1567 positionX
= (((GtkAdjustment
*)screen
.win
.hAdjustment
)->upper
- ((GtkAdjustment
*)screen
.win
.hAdjustment
)->page_size
);
1568 gtk_adjustment_set_value ((GtkAdjustment
*)screen
.win
.hAdjustment
, positionX
);
1570 positionY
= ((GtkAdjustment
*)screen
.win
.vAdjustment
)->upper
- screenRenderInfo
.lowerLeftY
-
1571 ((GtkAdjustment
*)screen
.win
.vAdjustment
)->page_size
+
1572 ((GtkAdjustment
*)screen
.win
.vAdjustment
)->lower
;
1573 if (positionY
< ((GtkAdjustment
*)screen
.win
.vAdjustment
)->lower
)
1574 positionY
= ((GtkAdjustment
*)screen
.win
.vAdjustment
)->lower
;
1575 if (positionY
> (((GtkAdjustment
*)screen
.win
.vAdjustment
)->upper
- ((GtkAdjustment
*)screen
.win
.vAdjustment
)->page_size
))
1576 positionY
= (((GtkAdjustment
*)screen
.win
.vAdjustment
)->upper
- ((GtkAdjustment
*)screen
.win
.vAdjustment
)->page_size
);
1577 gtk_adjustment_set_value ((GtkAdjustment
*)screen
.win
.vAdjustment
, positionY
);
1580 /* --------------------------------------------------------- */
1582 callbacks_scrollbar_button_released (GtkWidget
*widget
, GdkEventButton
*event
){
1585 screen
.state
= NORMAL
;
1586 render_refresh_rendered_image_on_screen();
1590 /* --------------------------------------------------------- */
1592 callbacks_scrollbar_button_pressed (GtkWidget
*widget
, GdkEventButton
*event
){
1593 //screen.last_x = ((GtkAdjustment *)screen.win.hAdjustment)->value;
1594 screen
.state
= SCROLLBAR
;
1598 /* --------------------------------------------------------- */
1599 void callbacks_hadjustment_value_changed (GtkAdjustment
*adjustment
, gpointer user_data
){
1600 /* make sure we're actually using the scrollbar to make sure we don't reset
1601 lowerLeftX during a scrollbar redraw during something else */
1602 if (screen
.state
== SCROLLBAR
) {
1603 screenRenderInfo
.lowerLeftX
= gtk_adjustment_get_value (adjustment
);
1607 /* --------------------------------------------------------- */
1608 void callbacks_vadjustment_value_changed (GtkAdjustment
*adjustment
, gpointer user_data
){
1609 /* make sure we're actually using the scrollbar to make sure we don't reset
1610 lowerLeftY during a scrollbar redraw during something else */
1611 if (screen
.state
== SCROLLBAR
) {
1612 screenRenderInfo
.lowerLeftY
= adjustment
->upper
-
1613 (gtk_adjustment_get_value (adjustment
) + adjustment
->page_size
) + adjustment
->lower
;
1617 /* --------------------------------------------------------- */
1619 callbacks_layer_tree_visibility_button_toggled (GtkCellRendererToggle
*cell_renderer
,
1621 gpointer user_data
){
1622 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
1623 ((GtkTreeView
*) screen
.win
.layerTree
);
1625 gboolean newVisibility
=TRUE
;
1628 gtk_tree_model_get_iter_from_string ((GtkTreeModel
*)list_store
, &iter
, path
);
1630 GtkTreePath
*treePath
= gtk_tree_path_new_from_string (path
);
1631 if (gtk_tree_model_get_iter((GtkTreeModel
*)list_store
, &iter
, treePath
)) {
1634 indeces
= gtk_tree_path_get_indices (treePath
);
1636 if (mainProject
->file
[index
]->isVisible
)
1637 newVisibility
= FALSE
;
1638 mainProject
->file
[index
]->isVisible
= newVisibility
;
1640 callbacks_update_layer_tree ();
1641 if (screenRenderInfo
.renderType
< 2) {
1642 render_refresh_rendered_image_on_screen();
1645 render_recreate_composite_surface (screen
.drawing_area
);
1646 callbacks_force_expose_event_for_screen ();
1651 /* --------------------------------------------------------- */
1653 callbacks_get_col_number_from_tree_view_column (GtkTreeViewColumn
*col
)
1658 g_return_val_if_fail ( col
!= NULL
, -1 );
1659 g_return_val_if_fail ( col
->tree_view
!= NULL
, -1 );
1660 cols
= gtk_tree_view_get_columns(GTK_TREE_VIEW(col
->tree_view
));
1661 num
= g_list_index(cols
, (gpointer
) col
);
1666 /* --------------------------------------------------------- */
1668 callbacks_add_layer_button_clicked (GtkButton
*button
, gpointer user_data
) {
1669 callbacks_open_layer_activate (NULL
, NULL
);
1672 /* --------------------------------------------------------- */
1674 callbacks_unselect_all_tool_buttons (void) {
1679 callbacks_switch_to_normal_tool_cursor (gint toolNumber
) {
1682 switch (toolNumber
) {
1684 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1688 cursor
= gdk_cursor_new(GDK_FLEUR
);
1689 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1691 gdk_cursor_destroy(cursor
);
1694 cursor
= gdk_cursor_new(GDK_SIZING
);
1695 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1697 gdk_cursor_destroy(cursor
);
1700 cursor
= gdk_cursor_new(GDK_CROSSHAIR
);
1701 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1703 gdk_cursor_destroy(cursor
);
1710 /* --------------------------------------------------------- */
1712 callbacks_switch_to_correct_cursor (void) {
1715 if (screen
.state
== IN_MOVE
) {
1716 cursor
= gdk_cursor_new(GDK_FLEUR
);
1717 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1719 gdk_cursor_destroy(cursor
);
1722 else if (screen
.state
== IN_ZOOM_OUTLINE
) {
1723 cursor
= gdk_cursor_new(GDK_SIZING
);
1724 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
1726 gdk_cursor_destroy(cursor
);
1729 callbacks_switch_to_normal_tool_cursor (screen
.tool
);
1732 /* --------------------------------------------------------- */
1734 callbacks_change_tool (GtkButton
*button
, gpointer user_data
) {
1735 gint toolNumber
= GPOINTER_TO_INT (user_data
);
1737 /* make sure se don't get caught in endless recursion here */
1738 if (screen
.win
.updatingTools
)
1740 screen
.win
.updatingTools
= TRUE
;
1741 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonPointer
), FALSE
);
1742 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonPan
), FALSE
);
1743 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonZoom
), FALSE
);
1744 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonMeasure
), FALSE
);
1745 switch (toolNumber
) {
1747 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonPointer
), TRUE
);
1748 screen
.tool
= POINTER
;
1749 screen
.state
= NORMAL
;
1750 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
1751 "Click to select objects in the current layer. Middle click and drag to pan.");
1754 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonPan
), TRUE
);
1756 screen
.state
= NORMAL
;
1757 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
1758 "Click and drag to pan. Right click and drag to zoom.");
1761 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonZoom
), TRUE
);
1763 screen
.state
= NORMAL
;
1764 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
1765 "Click and drag to zoom in. Shift+click to zoom out.");
1768 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (screen
.win
.toolButtonMeasure
), TRUE
);
1769 screen
.tool
= MEASURE
;
1770 screen
.state
= NORMAL
;
1771 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
, "Click and drag to measure a distance.");
1776 callbacks_switch_to_normal_tool_cursor (toolNumber
);
1777 callbacks_update_statusbar();
1778 screen
.win
.updatingTools
= FALSE
;
1779 callbacks_force_expose_event_for_screen();
1782 /* --------------------------------------------------------- */
1784 callbacks_select_row (gint rowIndex
) {
1785 GtkTreeSelection
*selection
;
1787 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
1788 ((GtkTreeView
*) screen
.win
.layerTree
);
1790 selection
= gtk_tree_view_get_selection((GtkTreeView
*) screen
.win
.layerTree
);
1791 if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store
), &iter
, NULL
, rowIndex
)) {
1792 gtk_tree_selection_select_iter (selection
, &iter
);
1796 /* --------------------------------------------------------- */
1798 * This fcn returns the index of selected layer (selected in
1799 * the layer window on left).
1803 callbacks_get_selected_row_index (void) {
1804 GtkTreeSelection
*selection
;
1806 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
1807 ((GtkTreeView
*) screen
.win
.layerTree
);
1810 /* This will only work in single or browse selection mode! */
1811 selection
= gtk_tree_view_get_selection((GtkTreeView
*) screen
.win
.layerTree
);
1812 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) {
1813 while (gtk_tree_model_iter_nth_child ((GtkTreeModel
*)list_store
,
1815 if (gtk_tree_selection_iter_is_selected (selection
, &iter
)) {
1824 /* --------------------------------------------------------- */
1826 callbacks_remove_layer_button_clicked (GtkButton
*button
, gpointer user_data
) {
1827 gint index
=callbacks_get_selected_row_index();
1829 if ((index
>= 0) && (index
<= mainProject
->last_loaded
)) {
1830 render_remove_selected_objects_belonging_to_layer (index
);
1831 gerbv_unload_layer (mainProject
, index
);
1832 callbacks_update_layer_tree ();
1833 callbacks_select_row (0);
1835 if (screenRenderInfo
.renderType
< 2) {
1836 render_refresh_rendered_image_on_screen();
1839 render_recreate_composite_surface (screen
.drawing_area
);
1840 callbacks_force_expose_event_for_screen ();
1845 /* --------------------------------------------------------- */
1847 callbacks_move_layer_down_button_clicked (GtkButton
*button
, gpointer user_data
) {
1848 gint index
=callbacks_get_selected_row_index();
1850 if ((index
>= 0) && (index
< mainProject
->last_loaded
)) {
1851 gerbv_change_layer_order (mainProject
, index
, index
+ 1);
1852 callbacks_update_layer_tree ();
1853 callbacks_select_row (index
+ 1);
1854 if (screenRenderInfo
.renderType
< 2) {
1855 render_refresh_rendered_image_on_screen();
1858 render_recreate_composite_surface (screen
.drawing_area
);
1859 callbacks_force_expose_event_for_screen ();
1864 /* --------------------------------------------------------- */
1866 callbacks_move_layer_up_clicked (GtkButton
*button
, gpointer user_data
) {
1867 gint index
=callbacks_get_selected_row_index();
1870 gerbv_change_layer_order (mainProject
, index
, index
- 1);
1871 callbacks_update_layer_tree ();
1872 callbacks_select_row (index
- 1);
1873 if (screenRenderInfo
.renderType
< 2) {
1874 render_refresh_rendered_image_on_screen();
1877 render_recreate_composite_surface (screen
.drawing_area
);
1878 callbacks_force_expose_event_for_screen ();
1883 /* --------------------------------------------------------- */
1884 void callbacks_layer_tree_row_inserted (GtkTreeModel
*tree_model
, GtkTreePath
*path
,
1885 GtkTreeIter
*oIter
, gpointer user_data
) {
1886 gint
*indeces
=NULL
,oldPosition
,newPosition
;
1888 if ((!screen
.win
.treeIsUpdating
)&&(path
!= NULL
)) {
1889 indeces
= gtk_tree_path_get_indices (path
);
1891 newPosition
= indeces
[0];
1892 oldPosition
= callbacks_get_selected_row_index ();
1893 /* compensate for the fact that the old row has already
1895 if (oldPosition
< newPosition
)
1899 gerbv_change_layer_order (mainProject
, oldPosition
, newPosition
);
1901 if (screenRenderInfo
.renderType
< 2) {
1902 render_refresh_rendered_image_on_screen();
1905 render_recreate_composite_surface (screen
.drawing_area
);
1906 callbacks_force_expose_event_for_screen ();
1908 /* select the new line */
1909 GtkTreeSelection
*selection
;
1911 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
1912 ((GtkTreeView
*) screen
.win
.layerTree
);
1914 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(screen
.win
.layerTree
));
1915 if (gtk_tree_model_get_iter ((GtkTreeModel
*)list_store
, &iter
, path
))
1916 gtk_tree_selection_select_iter (selection
, &iter
);
1921 /* --------------------------------------------------------- */
1923 callbacks_show_color_picker_dialog (gint index
){
1924 screen
.win
.colorSelectionDialog
= NULL
;
1925 GtkColorSelectionDialog
*cs
= (GtkColorSelectionDialog
*) gtk_color_selection_dialog_new ("Select a color");
1926 GtkColorSelection
*colorsel
= (GtkColorSelection
*) cs
->colorsel
;
1928 screen
.win
.colorSelectionDialog
= (GtkWidget
*) cs
;
1929 screen
.win
.colorSelectionIndex
= index
;
1931 gtk_color_selection_set_current_color (colorsel
, &mainProject
->file
[index
]->color
);
1933 gtk_color_selection_set_current_color (colorsel
, &mainProject
->background
);
1934 if ((screenRenderInfo
.renderType
>= 2)&&(index
>= 0)) {
1935 gtk_color_selection_set_has_opacity_control (colorsel
, TRUE
);
1936 gtk_color_selection_set_current_alpha (colorsel
, mainProject
->file
[index
]->alpha
);
1938 gtk_widget_show_all((GtkWidget
*)cs
);
1939 if (gtk_dialog_run ((GtkDialog
*)cs
) == GTK_RESPONSE_OK
) {
1940 GtkColorSelection
*colorsel
= (GtkColorSelection
*) cs
->colorsel
;
1941 gint rowIndex
= screen
.win
.colorSelectionIndex
;
1944 gtk_color_selection_get_current_color (colorsel
, &mainProject
->file
[rowIndex
]->color
);
1945 gdk_colormap_alloc_color(gdk_colormap_get_system(), &mainProject
->file
[rowIndex
]->color
, FALSE
, TRUE
);
1948 gtk_color_selection_get_current_color (colorsel
, &mainProject
->background
);
1949 gdk_colormap_alloc_color(gdk_colormap_get_system(), &mainProject
->background
, FALSE
, TRUE
);
1951 if ((screenRenderInfo
.renderType
>= 2)&&(index
>= 0)) {
1952 mainProject
->file
[rowIndex
]->alpha
= gtk_color_selection_get_current_alpha (colorsel
);
1955 callbacks_update_layer_tree ();
1956 render_refresh_rendered_image_on_screen();
1958 gtk_widget_destroy ((GtkWidget
*)cs
);
1959 screen
.win
.colorSelectionDialog
= NULL
;
1962 /* --------------------------------------------------------- */
1964 callbacks_invert_layer_clicked (GtkButton
*button
, gpointer user_data
) {
1965 gint index
=callbacks_get_selected_row_index();
1967 mainProject
->file
[index
]->transform
.inverted
= !mainProject
->file
[index
]->transform
.inverted
;
1968 render_refresh_rendered_image_on_screen ();
1969 callbacks_update_layer_tree ();
1972 /* --------------------------------------------------------- */
1974 callbacks_change_layer_color_clicked (GtkButton
*button
, gpointer user_data
) {
1975 gint index
=callbacks_get_selected_row_index();
1977 callbacks_show_color_picker_dialog (index
);
1981 callbacks_change_background_color_clicked (GtkButton
*button
, gpointer user_data
) {
1982 callbacks_show_color_picker_dialog (-1);
1985 /* --------------------------------------------------------------------------- */
1987 callbacks_reload_layer_clicked (GtkButton
*button
, gpointer user_data
) {
1988 gint index
= callbacks_get_selected_row_index();
1990 render_remove_selected_objects_belonging_to_layer (index
);
1991 gerbv_revert_file (mainProject
, index
);
1992 render_refresh_rendered_image_on_screen ();
1993 callbacks_update_layer_tree();
1997 callbacks_change_layer_orientation_clicked (GtkButton
*button
, gpointer userData
){
1998 gint index
= callbacks_get_selected_row_index();
2001 interface_show_alert_dialog("No layers are currently loaded",
2002 "A layer must be loaded before the orientation can be modified.",
2008 interface_show_modify_orientation_dialog(&mainProject
->file
[index
]->transform
,screen
.unit
);
2009 render_refresh_rendered_image_on_screen ();
2010 callbacks_update_layer_tree ();
2013 /* --------------------------------------------------------------------------- */
2015 callbacks_change_layer_format_clicked (GtkButton
*button
, gpointer user_data
)
2017 gerbv_HID_Attribute
*attr
= NULL
;
2020 gerbv_HID_Attr_Val
* results
= NULL
;
2021 gint index
= callbacks_get_selected_row_index();
2025 dprintf ("%s(): index = %d\n", __FUNCTION__
, index
);
2026 attr
= mainProject
->file
[index
]->image
->info
->attr_list
;
2027 n
= mainProject
->file
[index
]->image
->info
->n_attr
;
2028 type
= mainProject
->file
[index
]->image
->info
->type
;
2032 if (attr
== NULL
|| n
== 0)
2034 interface_show_alert_dialog("This file type does not currently have any editable features",
2035 "Format editing is currently only supported for Excellon drill file formats.",
2041 dprintf ("%s(): n = %d, attr = %p\n", __FUNCTION__
, n
, attr
);
2044 if (mainProject
->file
[index
]->layer_dirty
) {
2045 rc
= interface_get_alert_dialog_response ("This layer has changed!",
2046 "Editing the file type will reload the layer, destroying your changes. Click OK to edit the file type and destroy your changes, or Cancel to leave.",
2049 if (rc
== 0) return; /* Return if user hit Cancel */
2052 results
= (gerbv_HID_Attr_Val
*) malloc (n
* sizeof (gerbv_HID_Attr_Val
));
2053 if (results
== NULL
)
2055 fprintf (stderr
, "%s() -- malloc failed\n", __FUNCTION__
);
2059 /* non-zero means cancel was picked */
2060 if (attribute_interface_dialog (attr
, n
, results
,
2069 dprintf ("%s(): Reloading layer\n", __FUNCTION__
);
2070 gerbv_revert_file (mainProject
, index
);
2072 for (i
= 0; i
< n
; i
++)
2074 if (results
[i
].str_value
)
2075 free (results
[i
].str_value
);
2080 render_refresh_rendered_image_on_screen();
2081 callbacks_update_layer_tree();
2084 /* --------------------------------------------------------------------------- */
2086 callbacks_layer_tree_button_press (GtkWidget
*widget
, GdkEventButton
*event
,
2087 gpointer user_data
) {
2090 GtkTreeViewColumn
*column
;
2094 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
2095 ((GtkTreeView
*) screen
.win
.layerTree
);
2096 if (event
->button
== 1) {
2097 if (gtk_tree_view_get_path_at_pos ((GtkTreeView
*) widget
, event
->x
, event
->y
,
2098 &path
, &column
, &x
, &y
)) {
2099 if (gtk_tree_model_get_iter((GtkTreeModel
*)list_store
, &iter
, path
)) {
2101 indeces
= gtk_tree_path_get_indices (path
);
2103 columnIndex
= callbacks_get_col_number_from_tree_view_column (column
);
2104 if ((columnIndex
== 1) && (indeces
[0] <= mainProject
->last_loaded
)){
2105 callbacks_show_color_picker_dialog (indeces
[0]);
2106 /* don't propagate the signal, since drag and drop can
2107 sometimes activated during color selection */
2114 /* don't pop up the menu if we don't have any loaded files */
2115 else if ((event
->button
== 3)&&(mainProject
->last_loaded
>= 0)) {
2116 gtk_menu_popup(GTK_MENU(screen
.win
.layerTreePopupMenu
), NULL
, NULL
, NULL
, NULL
,
2117 event
->button
, event
->time
);
2119 /* always allow the click to propagate and make sure the line is activated */
2123 /* --------------------------------------------------------------------------- */
2125 callbacks_update_layer_tree (void) {
2126 GtkListStore
*list_store
= (GtkListStore
*) gtk_tree_view_get_model
2127 ((GtkTreeView
*) screen
.win
.layerTree
);
2130 GtkTreeSelection
*selection
;
2131 gint oldSelectedRow
;
2133 if (!screen
.win
.treeIsUpdating
) {
2134 screen
.win
.treeIsUpdating
= TRUE
;
2136 oldSelectedRow
= callbacks_get_selected_row_index();
2137 if (oldSelectedRow
< 0)
2139 gtk_list_store_clear (list_store
);
2141 for (idx
= 0; idx
<= mainProject
->last_loaded
; idx
++) {
2142 if (mainProject
->file
[idx
]) {
2143 GdkPixbuf
*pixbuf
,*blackPixbuf
;
2146 unsigned char red
, green
, blue
, alpha
;
2148 red
= (unsigned char) (mainProject
->file
[idx
]->color
.red
* 255 / G_MAXUINT16
) ;
2149 green
= (unsigned char) (mainProject
->file
[idx
]->color
.green
* 255 / G_MAXUINT16
) ;
2150 blue
= (unsigned char) (mainProject
->file
[idx
]->color
.blue
*255 / G_MAXUINT16
) ;
2151 alpha
= (unsigned char) (mainProject
->file
[idx
]->alpha
* 255 / G_MAXUINT16
) ;
2153 color
= (red
)* (256*256*256) + (green
) * (256*256) + (blue
)* (256) + (alpha
);
2154 pixbuf
= gdk_pixbuf_new (GDK_COLORSPACE_RGB
, TRUE
, 8, 20, 15);
2155 gdk_pixbuf_fill (pixbuf
, color
);
2157 blackPixbuf
= gdk_pixbuf_new (GDK_COLORSPACE_RGB
, TRUE
, 8, 20, 15);
2158 color
= (100 )* (256*256*256) + (100 ) * (256*256) + (100 )* (256) + (150 );
2159 gdk_pixbuf_fill (blackPixbuf
, color
);
2161 /* copy the color area into the black pixbuf */
2162 gdk_pixbuf_copy_area (pixbuf
, 1, 1, 18, 13, blackPixbuf
, 1, 1);
2163 /* free the color buffer, since we don't need it anymore */
2164 g_object_unref(pixbuf
);
2166 gtk_list_store_append (list_store
, &iter
);
2168 gchar startChar
[2],*modifiedCode
;
2169 /* terminate the letter string */
2172 gint numberOfModifications
= 0;
2173 if (mainProject
->file
[idx
]->transform
.inverted
) {
2175 numberOfModifications
++;
2177 if (mainProject
->file
[idx
]->transform
.mirrorAroundX
||
2178 mainProject
->file
[idx
]->transform
.mirrorAroundY
) {
2180 numberOfModifications
++;
2182 if ((abs(mainProject
->file
[idx
]->transform
.translateX
) > 0.000001) ||
2183 (fabs(mainProject
->file
[idx
]->transform
.translateY
) > 0.000001)) {
2185 numberOfModifications
++;
2187 if ((abs(mainProject
->file
[idx
]->transform
.scaleX
- 1) > 0.000001) ||
2188 (fabs(mainProject
->file
[idx
]->transform
.scaleY
- 1) > 0.000001)) {
2190 numberOfModifications
++;
2192 if ((fabs(mainProject
->file
[idx
]->transform
.rotation
) > 0.000001)) {
2194 numberOfModifications
++;
2196 if (numberOfModifications
> 1)
2198 if (numberOfModifications
== 0)
2199 modifiedCode
= g_strdup ("");
2201 modifiedCode
= g_strdup (startChar
);
2203 /* display any unsaved layers differently to show the user they are
2206 if (mainProject
->file
[idx
]->layer_dirty
== TRUE
) {
2207 /* The layer has unsaved changes in it. Show layer name in italics. */
2208 layerName
= g_strconcat ("*","<i>",mainProject
->file
[idx
]->name
,"</i>",NULL
);
2211 /* layer is clean. Show layer name using normal font. */
2212 layerName
= g_strdup (mainProject
->file
[idx
]->name
);
2214 gtk_list_store_set (list_store
, &iter
,
2215 0, mainProject
->file
[idx
]->isVisible
,
2221 g_free (modifiedCode
);
2222 /* pixbuf has a refcount of 2 now, as the list store has added its own reference */
2223 g_object_unref(blackPixbuf
);
2227 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(screen
.win
.layerTree
));
2229 /* if no line is selected yet, select the first row (if it has data) */
2230 /* or, select the line that was previously selected */
2232 if (!gtk_tree_selection_get_selected (selection
, NULL
, &iter
)) {
2233 if (gtk_tree_model_iter_nth_child ((GtkTreeModel
*) list_store
,
2234 &iter
, NULL
, oldSelectedRow
)) {
2235 gtk_tree_selection_select_iter (selection
, &iter
);
2238 screen
.win
.treeIsUpdating
= FALSE
;
2242 /* --------------------------------------------------------------------------- */
2244 callbacks_display_object_properties_clicked (GtkButton
*button
, gpointer user_data
){
2248 gboolean validAperture
;
2250 gint index
=callbacks_get_selected_row_index();
2254 if (screen
.selectionInfo
.type
== GERBV_SELECTION_EMPTY
) {
2255 interface_show_alert_dialog("No object is currently selected",
2256 "Objects must be selected using the pointer tool before you can view the object properties.",
2262 for (i
=0; i
<screen
.selectionInfo
.selectedNodeArray
->len
; i
++){
2263 gerbv_selection_item_t sItem
= g_array_index (screen
.selectionInfo
.selectedNodeArray
,
2264 gerbv_selection_item_t
, i
);
2266 gerbv_net_t
*net
= sItem
.net
;
2267 gerbv_image_t
*image
= sItem
.image
;
2270 /* get the aperture definition for the selected item */
2271 if (net
->aperture
> 0) {
2272 ap_type
= image
->aperture
[net
->aperture
]->type
;
2273 validAperture
= TRUE
;
2276 validAperture
= FALSE
;
2279 /* Also get layer name specified in file by %LN directive
2280 * (if it exists). */
2281 if (net
->layer
->name
== NULL
) {
2282 layer_name
= g_strdup("<unnamed layer>");
2284 layer_name
= g_strdup(net
->layer
->name
);
2287 if (net
->label
== NULL
) {
2288 net_label
= g_strdup("<unlabeled net>");
2290 net_label
= g_strdup((gchar
*)net
->label
);
2292 if (net
->interpolation
== GERBV_INTERPOLATION_PAREA_START
) {
2293 g_message ("Object type: Polygon\n");
2296 switch (net
->aperture_state
){
2297 case GERBV_APERTURE_STATE_OFF
:
2299 case GERBV_APERTURE_STATE_ON
:
2300 if (i
!=0) g_message ("\n"); /* Spacing for a pretty display */
2301 switch (net
->interpolation
) {
2302 case GERBV_INTERPOLATION_x10
:
2303 case GERBV_INTERPOLATION_LINEARx01
:
2304 case GERBV_INTERPOLATION_LINEARx001
:
2305 case GERBV_INTERPOLATION_LINEARx1
:
2306 g_message ("Object type: Line\n");
2308 case GERBV_INTERPOLATION_CW_CIRCULAR
:
2309 case GERBV_INTERPOLATION_CCW_CIRCULAR
:
2310 g_message ("Object type: Arc\n");
2313 g_message ("Object type: Unknown\n");
2316 g_message (" Exposure: On\n");
2317 if (validAperture
) {
2318 g_message (" Aperture used: D%d\n", net
->aperture
);
2319 g_message (" Aperture type: %s\n", ap_names
[ap_type
]);
2321 g_message (" Start location: (%g, %g)\n", net
->start_x
, net
->start_y
);
2322 g_message (" Stop location: (%g, %g)\n", net
->stop_x
, net
->stop_y
);
2323 g_message (" Layer name: %s\n", layer_name
);
2324 g_message (" Net label: %s\n", net_label
);
2325 g_message (" In file: %s\n", mainProject
->file
[index
]->name
);
2327 case GERBV_APERTURE_STATE_FLASH
:
2328 if (i
!=0) g_message ("\n"); /* Spacing for a pretty display */
2329 g_message ("Object type: Flashed aperture\n");
2330 if (validAperture
) {
2331 g_message (" Aperture used: D%d\n", net
->aperture
);
2332 g_message (" Aperture type: %s\n", ap_names
[ap_type
]);
2334 g_message (" Location: (%g, %g)\n", net
->stop_x
, net
->stop_y
);
2335 g_message (" Layer name: %s\n", layer_name
);
2336 g_message (" Net label: %s\n", net_label
);
2337 g_message (" In file: %s\n", mainProject
->file
[index
]->name
);
2342 g_free (layer_name
);
2344 /* Use separator for different report requests */
2345 g_message ("---------------------------------------\n");
2349 callbacks_support_benchmark (gerbv_render_info_t
*renderInfo
) {
2352 GdkPixmap
*renderedPixmap
= gdk_pixmap_new (NULL
, renderInfo
->displayWidth
,
2353 renderInfo
->displayHeight
, 24);
2355 // start by running the GDK (fast) rendering test
2359 while( now
- 30 < start
) {
2361 dprintf("Benchmark(): Starting redraw #%d\n", i
);
2362 gerbv_render_to_pixmap_using_gdk (mainProject
, renderedPixmap
, renderInfo
, NULL
, NULL
);
2364 dprintf("Elapsed time = %ld seconds\n", (long int) (now
- start
));
2366 g_message("FAST mode benchmark: %d redraws in %ld seconds (%g redraws/second)\n",
2367 i
, (long int) (now
- start
), (double) i
/ (double)(now
- start
));
2368 gdk_pixmap_unref(renderedPixmap
);
2370 // run the cairo (normal) render mode
2374 renderInfo
->renderType
= 2;
2375 while( now
- 30 < start
) {
2377 dprintf("Benchmark(): Starting redraw #%d\n", i
);
2378 cairo_surface_t
*cSurface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
,
2379 renderInfo
->displayWidth
, renderInfo
->displayHeight
);
2380 cairo_t
*cairoTarget
= cairo_create (cSurface
);
2381 gerbv_render_all_layers_to_cairo_target (mainProject
, cairoTarget
, renderInfo
);
2382 cairo_destroy (cairoTarget
);
2383 cairo_surface_destroy (cSurface
);
2385 dprintf("Elapsed time = %ld seconds\n", (long int) (now
- start
));
2387 g_message("NORMAL mode benchmark: %d redraws in %ld seconds (%g redraws/second)\n",
2388 i
, (long int) (now
- start
), (double) i
/ (double)(now
- start
));
2391 /* --------------------------------------------------------------------------- */
2393 callbacks_benchmark_clicked (GtkButton
*button
, gpointer user_data
)
2395 // prepare render size and options (canvas size width and height are last 2 variables)
2396 gerbv_render_info_t renderInfo
= {1.0, 1.0, 0, 0, 0, 640, 480};
2397 // autoscale the image for now...maybe we don't want to do this in order to
2398 // allow benchmarking of different zoom levels?
2399 gerbv_render_zoom_to_fit_display (mainProject
, &renderInfo
);
2401 g_message("Full zoom benchmarks\n");
2402 callbacks_support_benchmark (&renderInfo
);
2405 g_message("x5 zoom benchmarks\n");
2406 renderInfo
.lowerLeftX
+= (screenRenderInfo
.displayWidth
/
2407 screenRenderInfo
.scaleFactorX
) / 3.0;
2408 renderInfo
.lowerLeftY
+= (screenRenderInfo
.displayHeight
/
2409 screenRenderInfo
.scaleFactorY
) / 3.0;
2410 renderInfo
.scaleFactorX
*= 5;
2411 renderInfo
.scaleFactorY
*= 5;
2412 callbacks_support_benchmark (&renderInfo
);
2415 /* --------------------------------------------------------------------------- */
2417 callbacks_edit_object_properties_clicked (GtkButton
*button
, gpointer user_data
){
2420 /* --------------------------------------------------------------------------- */
2422 callbacks_move_objects_clicked (GtkButton
*button
, gpointer user_data
){
2423 /* for testing, just hard code in some translations here */
2424 gerbv_image_move_selected_objects (screen
.selectionInfo
.selectedNodeArray
, -0.050, 0.050);
2425 callbacks_update_layer_tree();
2426 render_clear_selection_buffer ();
2427 render_refresh_rendered_image_on_screen ();
2430 /* --------------------------------------------------------------------------- */
2432 callbacks_reduce_object_area_clicked (GtkButton
*button
, gpointer user_data
){
2433 /* for testing, just hard code in some parameters */
2434 gerbv_image_reduce_area_of_selected_objects (screen
.selectionInfo
.selectedNodeArray
, 0.20, 3, 3, 0.01);
2435 render_clear_selection_buffer ();
2436 render_refresh_rendered_image_on_screen ();
2439 /* --------------------------------------------------------------------------- */
2441 callbacks_delete_objects_clicked (GtkButton
*button
, gpointer user_data
){
2442 if (screen
.selectionInfo
.type
== GERBV_SELECTION_EMPTY
) {
2443 interface_show_alert_dialog("No object is currently selected",
2444 "Objects must be selected using the pointer tool before they can be deleted.",
2450 gint index
=callbacks_get_selected_row_index();
2454 if (mainProject
->check_before_delete
== TRUE
) {
2455 if (!interface_get_alert_dialog_response(
2456 "Do you want to permanently delete the selected objects?",
2457 "Gerbv currently has no undo function, so this action cannot be undone. This action will not change the saved file unless you save the file afterwards.",
2459 &(mainProject
->check_before_delete
)))
2463 gerbv_image_delete_selected_nets (mainProject
->file
[index
]->image
,
2464 screen
.selectionInfo
.selectedNodeArray
);
2465 render_refresh_rendered_image_on_screen ();
2466 /* Set layer_dirty flag to TRUE */
2467 mainProject
->file
[index
]->layer_dirty
= TRUE
;
2468 callbacks_update_layer_tree();
2470 render_clear_selection_buffer ();
2471 callbacks_update_selected_object_message(FALSE
);
2474 /* --------------------------------------------------------------------------- */
2476 callbacks_drawingarea_configure_event (GtkWidget
*widget
, GdkEventConfigure
*event
)
2478 GdkDrawable
*drawable
= widget
->window
;
2480 gdk_drawable_get_size (drawable
, &screenRenderInfo
.displayWidth
, &screenRenderInfo
.displayHeight
);
2482 /* set this up if cairo is compiled, since we may need to switch over to
2483 using the surface at any time */
2484 int x_off
=0, y_off
=0;
2487 if (GDK_IS_WINDOW(widget
->window
)) {
2488 /* query the window's backbuffer if it has one */
2489 GdkWindow
*window
= GDK_WINDOW(widget
->window
);
2490 gdk_window_get_internal_paint_info (window
, &drawable
, &x_off
, &y_off
);
2492 visual
= gdk_drawable_get_visual (drawable
);
2493 if (screen
.windowSurface
)
2494 cairo_surface_destroy ((cairo_surface_t
*)
2495 screen
.windowSurface
);
2497 cairo_t
*cairoTarget
= gdk_cairo_create (GDK_WINDOW(widget
->window
));
2499 screen
.windowSurface
= cairo_get_target (cairoTarget
);
2500 /* increase surface reference by one so it isn't freed when the cairo_t
2501 is destroyed next */
2502 screen
.windowSurface
= cairo_surface_reference (screen
.windowSurface
);
2503 cairo_destroy (cairoTarget
);
2505 screen
.windowSurface
= (gpointer
) cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY (drawable
),
2506 GDK_DRAWABLE_XID (drawable
),
2507 GDK_VISUAL_XVISUAL (visual
),
2508 screenRenderInfo
.displayWidth
,
2509 screenRenderInfo
.displayHeight
);
2511 /* if this is the first time, go ahead and call autoscale even if we don't
2512 have a model loaded */
2513 if ((screenRenderInfo
.scaleFactorX
< 0.001)||(screenRenderInfo
.scaleFactorY
< 0.001)) {
2514 gerbv_render_zoom_to_fit_display (mainProject
, &screenRenderInfo
);
2516 render_refresh_rendered_image_on_screen();
2520 /* --------------------------------------------------------- */
2522 callbacks_drawingarea_expose_event (GtkWidget
*widget
, GdkEventExpose
*event
)
2524 if (screenRenderInfo
.renderType
< 2) {
2525 GdkPixmap
*new_pixmap
;
2526 GdkGC
*gc
= gdk_gc_new(widget
->window
);
2529 * Create a pixmap with default background
2531 new_pixmap
= gdk_pixmap_new(widget
->window
,
2532 widget
->allocation
.width
,
2533 widget
->allocation
.height
,
2536 gdk_gc_set_foreground(gc
, &mainProject
->background
);
2538 gdk_draw_rectangle(new_pixmap
, gc
, TRUE
,
2539 event
->area
.x
, event
->area
.y
,
2540 event
->area
.width
, event
->area
.height
);
2543 * Copy gerber pixmap onto background if we have one to copy.
2544 * Do translation at the same time.
2546 if (screen
.pixmap
!= NULL
) {
2547 gdk_draw_pixmap(new_pixmap
,
2548 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
2550 event
->area
.x
- screen
.off_x
,
2551 event
->area
.y
- screen
.off_y
,
2552 event
->area
.x
, event
->area
.y
,
2553 event
->area
.width
, event
->area
.height
);
2557 * Draw the whole thing onto screen
2559 gdk_draw_pixmap(widget
->window
,
2560 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
2562 event
->area
.x
, event
->area
.y
,
2563 event
->area
.x
, event
->area
.y
,
2564 event
->area
.width
, event
->area
.height
);
2566 gdk_pixmap_unref(new_pixmap
);
2570 * Draw Zooming outline if we are in that mode
2572 if (screen
.state
== IN_ZOOM_OUTLINE
) {
2573 render_draw_zoom_outline(screen
.centered_outline_zoom
);
2575 else if (screen
.state
== IN_MEASURE
) {
2576 render_draw_measure_distance();
2578 if (screen
.tool
== MEASURE
&& screen
.state
!= IN_MEASURE
) {
2579 render_toggle_measure_line();
2587 int x_off
=0, y_off
=0;
2588 GdkDrawable
*drawable
= widget
->window
;
2591 if (GDK_IS_WINDOW(widget
->window
)) {
2592 /* query the window's backbuffer if it has one */
2593 GdkWindow
*window
= GDK_WINDOW(widget
->window
);
2594 gdk_window_get_internal_paint_info (window
,
2595 &drawable
, &x_off
, &y_off
);
2597 visual
= gdk_drawable_get_visual (drawable
);
2598 gdk_drawable_get_size (drawable
, &width
, &height
);
2602 cr
= gdk_cairo_create (GDK_WINDOW(widget
->window
));
2604 cairo_surface_t
*buffert
;
2606 buffert
= (gpointer
) cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY (drawable
),
2607 GDK_DRAWABLE_XID (drawable
),
2608 GDK_VISUAL_XVISUAL (visual
),
2609 event
->area
.width
, event
->area
.height
);
2610 cr
= cairo_create (buffert
);
2612 cairo_translate (cr
, -event
->area
.x
+ screen
.off_x
, -event
->area
.y
+ screen
.off_y
);
2613 render_project_to_cairo_target (cr
);
2616 cairo_surface_destroy (buffert
);
2619 if (screen
.tool
== MEASURE
)
2620 render_toggle_measure_line();
2624 /* Transforms screen coordinates to board ones */
2626 callbacks_screen2board(gdouble
*X
, gdouble
*Y
, gint x
, gint y
) {
2628 /* make sure we don't divide by zero (which is possible if the gui
2629 isn't displayed yet */
2630 if ((screenRenderInfo
.scaleFactorX
> 0.001)||(screenRenderInfo
.scaleFactorY
> 0.001)) {
2631 *X
= screenRenderInfo
.lowerLeftX
+ (x
/ screenRenderInfo
.scaleFactorX
);
2632 *Y
= screenRenderInfo
.lowerLeftY
+ ((screenRenderInfo
.displayHeight
- y
)
2633 / screenRenderInfo
.scaleFactorY
);
2640 /* --------------------------------------------------------- */
2642 callbacks_update_statusbar_coordinates (gint x
, gint y
) {
2645 callbacks_screen2board(&X
, &Y
, x
, y
);
2646 if (screen
.unit
== GERBV_MILS
) {
2647 snprintf(screen
.statusbar
.coordstr
, MAX_COORDLEN
,
2649 COORD2MILS(X
), COORD2MILS(Y
));
2650 } else if (screen
.unit
== GERBV_MMS
) {
2651 snprintf(screen
.statusbar
.coordstr
, MAX_COORDLEN
,
2653 COORD2MMS(X
), COORD2MMS(Y
));
2655 snprintf(screen
.statusbar
.coordstr
, MAX_COORDLEN
,
2657 COORD2MILS(X
) / 1000.0, COORD2MILS(Y
) / 1000.0);
2659 callbacks_update_statusbar();
2663 callbacks_update_selected_object_message (gboolean userTriedToSelect
) {
2664 if (screen
.tool
!= POINTER
)
2667 gint selectionLength
= screen
.selectionInfo
.selectedNodeArray
->len
;
2668 if ((selectionLength
== 0)&&(userTriedToSelect
)) {
2669 /* update status bar message to make sure the user knows
2670 about needing to select the layer */
2671 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2672 "<b>No object selected. Objects can only be selected in the active layer.</b>");
2674 else if (selectionLength
== 0) {
2675 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2676 "Click to select objects in the current layer. Middle click and drag to pan.");
2678 else if (selectionLength
== 1) {
2679 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2680 "1 object is currently selected");
2683 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2684 "%d objects are currently selected",selectionLength
);
2686 callbacks_update_statusbar();
2689 /* --------------------------------------------------------- */
2691 callbacks_drawingarea_motion_notify_event (GtkWidget
*widget
, GdkEventMotion
*event
)
2694 GdkModifierType state
;
2697 gdk_window_get_pointer (event
->window
, &x
, &y
, &state
);
2701 state
= event
->state
;
2704 switch (screen
.state
) {
2706 if (screen
.last_x
!= 0 || screen
.last_y
!= 0) {
2707 /* Move pixmap to get a snappier feel of movement */
2708 screen
.off_x
+= x
- screen
.last_x
;
2709 screen
.off_y
+= y
- screen
.last_y
;
2711 screenRenderInfo
.lowerLeftX
-= ((x
- screen
.last_x
) / screenRenderInfo
.scaleFactorX
);
2712 screenRenderInfo
.lowerLeftY
+= ((y
- screen
.last_y
) / screenRenderInfo
.scaleFactorY
);
2713 callbacks_force_expose_event_for_screen ();
2714 callbacks_update_scrollbar_positions ();
2719 case IN_ZOOM_OUTLINE
: {
2720 if (screen
.last_x
|| screen
.last_y
)
2721 render_draw_zoom_outline(screen
.centered_outline_zoom
);
2724 render_draw_zoom_outline(screen
.centered_outline_zoom
);
2728 /* clear the previous drawn line by drawing over it */
2729 render_toggle_measure_line();
2730 callbacks_screen2board(&(screen
.measure_last_x
), &(screen
.measure_last_y
),
2732 /* screen.last_[xy] are updated to move the ruler pointers */
2735 /* draw the new line and write the new distance */
2736 render_draw_measure_distance();
2739 case IN_SELECTION_DRAG
: {
2740 if (screen
.last_x
|| screen
.last_y
)
2741 render_draw_selection_box_outline();
2744 render_draw_selection_box_outline();
2752 callbacks_update_statusbar_coordinates (x
, y
);
2753 callbacks_update_ruler_pointers ();
2755 } /* motion_notify_event */
2757 /* --------------------------------------------------------- */
2759 callbacks_drawingarea_button_press_event (GtkWidget
*widget
, GdkEventButton
*event
)
2763 switch (event
->button
) {
2765 if (screen
.tool
== POINTER
) {
2767 /* selection will only work with cairo, so do nothing if it's
2769 screen
.state
= IN_SELECTION_DRAG
;
2770 screen
.start_x
= event
->x
;
2771 screen
.start_y
= event
->y
;
2773 else if (screen
.tool
== PAN
) {
2775 screen
.state
= IN_MOVE
;
2776 screen
.last_x
= event
->x
;
2777 screen
.last_y
= event
->y
;
2779 else if (screen
.tool
== ZOOM
) {
2780 screen
.state
= IN_ZOOM_OUTLINE
;
2781 /* Zoom outline mode initiated */
2782 screen
.start_x
= event
->x
;
2783 screen
.start_y
= event
->y
;
2784 screen
.centered_outline_zoom
= event
->state
;
2786 else if (screen
.tool
== MEASURE
) {
2787 screen
.state
= IN_MEASURE
;
2788 callbacks_screen2board(&(screen
.measure_start_x
), &(screen
.measure_start_y
),
2789 event
->x
, event
->y
);
2790 screen
.measure_last_x
= screen
.measure_start_x
;
2791 screen
.measure_last_y
= screen
.measure_start_y
;
2792 /* force an expose event to clear any previous measure lines */
2793 callbacks_force_expose_event_for_screen ();
2797 screen
.state
= IN_MOVE
;
2798 screen
.last_x
= event
->x
;
2799 screen
.last_y
= event
->y
;
2800 cursor
= gdk_cursor_new(GDK_FLEUR
);
2801 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
2803 gdk_cursor_destroy(cursor
);
2806 if (screen
.tool
== POINTER
) {
2807 /* if no items are selected, try and find the item the user
2809 if (screen
.selectionInfo
.type
== GERBV_SELECTION_EMPTY
) {
2810 gint index
=callbacks_get_selected_row_index();
2812 (index
<= mainProject
->last_loaded
) &&
2813 (mainProject
->file
[index
]->isVisible
)) {
2814 render_fill_selection_buffer_from_mouse_click(event
->x
,event
->y
,index
,TRUE
);
2816 render_clear_selection_buffer ();
2817 render_refresh_rendered_image_on_screen ();
2820 /* only show the popup if we actually have something selected now */
2821 if (screen
.selectionInfo
.type
!= GERBV_SELECTION_EMPTY
)
2822 gtk_menu_popup(GTK_MENU(screen
.win
.drawWindowPopupMenu
), NULL
, NULL
, NULL
, NULL
,
2823 event
->button
, event
->time
);
2825 /* Zoom outline mode initiated */
2826 screen
.state
= IN_ZOOM_OUTLINE
;
2827 screen
.start_x
= event
->x
;
2828 screen
.start_y
= event
->y
;
2829 screen
.centered_outline_zoom
= event
->state
& GDK_SHIFT_MASK
;
2830 cursor
= gdk_cursor_new(GDK_SIZING
);
2831 gdk_window_set_cursor(GDK_WINDOW(screen
.drawing_area
->window
),
2833 gdk_cursor_destroy(cursor
);
2836 case 4 : /* Scroll wheel */
2837 render_zoom_display (ZOOM_IN_CMOUSE
, 0, event
->x
, event
->y
);
2839 case 5 : /* Scroll wheel */
2840 render_zoom_display (ZOOM_OUT_CMOUSE
, 0, event
->x
, event
->y
);
2845 callbacks_switch_to_correct_cursor ();
2849 /* --------------------------------------------------------- */
2851 callbacks_drawingarea_button_release_event (GtkWidget
*widget
, GdkEventButton
*event
)
2853 if (event
->type
== GDK_BUTTON_RELEASE
) {
2854 if (screen
.state
== IN_MOVE
) {
2857 render_refresh_rendered_image_on_screen();
2858 callbacks_switch_to_normal_tool_cursor (screen
.tool
);
2860 else if (screen
.state
== IN_ZOOM_OUTLINE
) {
2861 if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
2862 render_zoom_display (ZOOM_OUT_CMOUSE
, 0, event
->x
, event
->y
);
2864 /* if the user just clicks without dragging, then simply
2865 zoom in a preset amount */
2866 else if ((abs(screen
.start_x
- event
->x
) < 4) &&
2867 (abs(screen
.start_y
- event
->y
) < 4)) {
2868 render_zoom_display (ZOOM_IN_CMOUSE
, 0, event
->x
, event
->y
);
2871 render_calculate_zoom_from_outline (widget
, event
);
2872 callbacks_switch_to_normal_tool_cursor (screen
.tool
);
2874 else if (screen
.state
== IN_SELECTION_DRAG
) {
2875 /* selection will only work with cairo, so do nothing if it's
2877 gint index
=callbacks_get_selected_row_index();
2878 /* determine if this was just a click or a box drag */
2880 (mainProject
->file
[index
]->isVisible
)) {
2881 gboolean eraseOldSelection
= TRUE
;
2883 if ((event
->state
& GDK_SHIFT_MASK
) ||
2884 (event
->state
& GDK_CONTROL_MASK
)) {
2885 eraseOldSelection
= FALSE
;
2887 if ((fabs((double)(screen
.last_x
- screen
.start_x
)) < 5) &&
2888 (fabs((double)(screen
.last_y
- screen
.start_y
)) < 5))
2889 render_fill_selection_buffer_from_mouse_click(event
->x
,event
->y
,index
,eraseOldSelection
);
2891 render_fill_selection_buffer_from_mouse_drag(event
->x
,event
->y
,
2892 screen
.start_x
,screen
.start_y
,index
,eraseOldSelection
);
2893 /* check if anything was selected */
2894 callbacks_update_selected_object_message (TRUE
);
2896 render_clear_selection_buffer ();
2897 render_refresh_rendered_image_on_screen ();
2900 screen
.last_x
= screen
.last_y
= 0;
2901 screen
.state
= NORMAL
;
2904 } /* button_release_event */
2906 /* --------------------------------------------------------- */
2908 callbacks_window_key_press_event (GtkWidget
*widget
, GdkEventKey
*event
)
2910 switch(event
->keyval
) {
2912 if (screen
.tool
== POINTER
) {
2913 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2914 "No objects are currently selected");
2915 callbacks_update_statusbar();
2916 render_clear_selection_buffer ();
2923 /* Escape may be used to abort outline zoom and just plain repaint */
2924 if (event
->keyval
== GDK_Escape
) {
2925 screen
.state
= NORMAL
;
2926 render_refresh_rendered_image_on_screen();
2930 } /* key_press_event */
2932 /* --------------------------------------------------------- */
2934 callbacks_window_key_release_event (GtkWidget
*widget
, GdkEventKey
*event
)
2937 } /* key_release_event */
2939 /* --------------------------------------------------------- */
2942 callbacks_window_scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
2944 switch (event
->direction
) {
2946 render_zoom_display (ZOOM_IN_CMOUSE
, 0, event
->x
, event
->y
);
2948 case GDK_SCROLL_DOWN
:
2949 render_zoom_display (ZOOM_OUT_CMOUSE
, 0, event
->x
, event
->y
);
2951 case GDK_SCROLL_LEFT
:
2953 case GDK_SCROLL_RIGHT
:
2959 } /* scroll_event */
2962 /* ------------------------------------------------------------------ */
2963 /** Displays additional information in the statusbar.
2964 The Statusbar is divided into three sections:\n
2965 statusbar.coordstr for coords\n
2966 statusbar.diststr for displaying measured distances or the designator
2967 (right click on a graphically marked and also actively selected part)\n
2968 statusbar.msg for e.g. showing progress of actions*/
2970 callbacks_update_statusbar(void)
2972 if ((screen
.statusbar
.coordstr
!= NULL
)&&(GTK_IS_LABEL(screen
.win
.statusMessageLeft
))) {
2973 gtk_label_set_text(GTK_LABEL(screen
.win
.statusMessageLeft
), screen
.statusbar
.coordstr
);
2975 if ((screen
.statusbar
.diststr
!= NULL
)&&(GTK_IS_LABEL(screen
.win
.statusMessageRight
))) {
2976 gtk_label_set_markup(GTK_LABEL(screen
.win
.statusMessageRight
), screen
.statusbar
.diststr
);
2980 /* --------------------------------------------------------- */
2982 callbacks_update_statusbar_measured_distance (gdouble dx
, gdouble dy
){
2983 gdouble delta
= sqrt(dx
*dx
+ dy
*dy
);
2985 if (screen
.unit
== GERBV_MILS
) {
2986 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2987 "Measured distance: %8.2f mils (%8.2f x, %8.2f y)",
2988 COORD2MILS(delta
), COORD2MILS(dx
), COORD2MILS(dy
));
2990 else if (screen
.unit
== GERBV_MMS
) {
2991 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2992 "Measured distance: %8.3f mms (%8.3f x, %8.3f y)",
2993 COORD2MMS(delta
), COORD2MMS(dx
), COORD2MMS(dy
));
2996 snprintf(screen
.statusbar
.diststr
, MAX_DISTLEN
,
2997 "Measured distance: %4.5f inches (%4.5f x, %4.5f y)",
2998 COORD2MILS(delta
) / 1000.0, COORD2MILS(dx
) / 1000.0,
2999 COORD2MILS(dy
) / 1000.0);
3001 callbacks_update_statusbar();
3004 /* --------------------------------------------------------- */
3006 callbacks_sidepane_render_type_combo_box_changed (GtkComboBox
*widget
, gpointer user_data
) {
3007 int activeRow
= gtk_combo_box_get_active (widget
);
3009 dprintf ("%s(): activeRow = %d\n", __FUNCTION__
, activeRow
);
3010 screenRenderInfo
.renderType
= activeRow
;
3012 render_refresh_rendered_image_on_screen();
3015 /* --------------------------------------------------------- */
3017 callbacks_statusbar_unit_combo_box_changed (GtkComboBox
*widget
, gpointer user_data
) {
3018 int activeRow
= gtk_combo_box_get_active (widget
);
3020 if (activeRow
>= 0) {
3021 screen
.unit
= activeRow
;
3023 callbacks_update_ruler_scales();
3024 callbacks_update_statusbar_coordinates (screen
.last_x
, screen
.last_y
);
3026 if (screen
.tool
== MEASURE
)
3027 callbacks_update_statusbar_measured_distance (screen
.win
.lastMeasuredX
,
3028 screen
.win
.lastMeasuredY
);
3031 /* --------------------------------------------------------- */
3033 callbacks_clear_messages_button_clicked (GtkButton
*button
, gpointer user_data
) {
3034 GtkTextBuffer
*textbuffer
;
3035 GtkTextIter start
, end
;
3037 textbuffer
= gtk_text_view_get_buffer((GtkTextView
*)screen
.win
.messageTextView
);
3038 gtk_text_buffer_get_start_iter(textbuffer
, &start
);
3039 gtk_text_buffer_get_end_iter(textbuffer
, &end
);
3040 gtk_text_buffer_delete (textbuffer
, &start
, &end
);
3043 /* --------------------------------------------------------- */
3045 callbacks_handle_log_messages(const gchar
*log_domain
, GLogLevelFlags log_level
,
3046 const gchar
*message
, gpointer user_data
) {
3047 GtkTextBuffer
*textbuffer
= NULL
;
3050 GtkTextMark
*StartMark
= NULL
, *StopMark
= NULL
;
3051 GtkTextIter StartIter
, StopIter
;
3053 if (!screen
.win
.messageTextView
)
3056 textbuffer
= gtk_text_view_get_buffer((GtkTextView
*)screen
.win
.messageTextView
);
3058 /* create a mark for the end of the text. */
3059 gtk_text_buffer_get_end_iter(textbuffer
, &iter
);
3061 /* get the current end position of the text (it will be the
3062 start of the new text. */
3063 StartMark
= gtk_text_buffer_create_mark(textbuffer
,
3064 "NewTextStart", &iter
, TRUE
);
3066 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3068 /* the tag does not exist: create it and let them exist in the tag table.*/
3070 tag
= gtk_text_buffer_create_tag(textbuffer
, "black_foreground",
3071 "foreground", "black", NULL
);
3072 tag
= gtk_text_buffer_create_tag(textbuffer
, "blue_foreground",
3073 "foreground", "blue", NULL
);
3074 tag
= gtk_text_buffer_create_tag(textbuffer
, "red_foreground",
3075 "foreground", "red", NULL
);
3076 tag
= gtk_text_buffer_create_tag(textbuffer
, "darkred_foreground",
3077 "foreground", "darkred", NULL
);
3078 tag
= gtk_text_buffer_create_tag(textbuffer
, "darkblue_foreground",
3079 "foreground", "darkblue", NULL
);
3080 tag
= gtk_text_buffer_create_tag (textbuffer
, "darkgreen_foreground",
3081 "foreground", "darkgreen", NULL
);
3082 tag
= gtk_text_buffer_create_tag (textbuffer
,
3083 "saddlebrown_foreground",
3084 "foreground", "saddlebrown", NULL
);
3088 * See rgb.txt for the color names definition
3089 * (on my PC it is on /usr/X11R6/lib/X11/rgb.txt)
3091 switch (log_level
& G_LOG_LEVEL_MASK
) {
3092 case G_LOG_LEVEL_ERROR
:
3093 /* a message of this kind aborts the application calling abort() */
3094 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3096 gtk_notebook_set_current_page(GTK_NOTEBOOK(screen
.win
.sidepane_notebook
), 1);
3097 gtk_widget_show(screen
.win
.sidepane_notebook
);
3099 case G_LOG_LEVEL_CRITICAL
:
3100 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3102 gtk_notebook_set_current_page(GTK_NOTEBOOK(screen
.win
.sidepane_notebook
), 1);
3103 gtk_widget_show(screen
.win
.sidepane_notebook
);
3105 case G_LOG_LEVEL_WARNING
:
3106 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3107 "darkred_foreground");
3108 gtk_notebook_set_current_page(GTK_NOTEBOOK(screen
.win
.sidepane_notebook
), 1);
3109 gtk_widget_show(screen
.win
.sidepane_notebook
);
3111 case G_LOG_LEVEL_MESSAGE
:
3112 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3113 "darkblue_foreground");
3114 gtk_notebook_set_current_page(GTK_NOTEBOOK(screen
.win
.sidepane_notebook
), 1);
3115 gtk_widget_show(screen
.win
.sidepane_notebook
);
3117 case G_LOG_LEVEL_INFO
:
3118 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3119 "darkgreen_foreground");
3121 case G_LOG_LEVEL_DEBUG
:
3122 tag
= gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textbuffer
),
3123 "saddlebrown_foreground");
3126 tag
= gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table(textbuffer
),
3127 "black_foreground");
3132 * Fatal aborts application. We will try to get the message out anyhow.
3134 if (log_level
& G_LOG_FLAG_FATAL
)
3135 fprintf(stderr
, "Fatal error : %s\n", message
);
3137 gtk_text_buffer_insert(textbuffer
, &iter
, message
, -1);
3139 gtk_text_buffer_get_end_iter(textbuffer
, &iter
);
3141 StopMark
= gtk_text_buffer_create_mark(textbuffer
,
3142 "NewTextStop", &iter
, TRUE
);
3144 gtk_text_buffer_get_iter_at_mark(textbuffer
, &StartIter
, StartMark
);
3145 gtk_text_buffer_get_iter_at_mark(textbuffer
, &StopIter
, StopMark
);
3147 gtk_text_buffer_apply_tag(textbuffer
, tag
, &StartIter
, &StopIter
);
3150 /* --------------------------------------------------------- */
3151 void callbacks_force_expose_event_for_screen (void){
3153 GdkRectangle update_rect
;
3157 update_rect
.width
= screenRenderInfo
.displayWidth
;
3158 update_rect
.height
= screenRenderInfo
.displayHeight
;
3160 /* Calls expose_event */
3161 gdk_window_invalidate_rect (screen
.drawing_area
->window
, &update_rect
, FALSE
);
3163 /* update other gui things that could have changed */
3164 callbacks_update_ruler_scales();
3165 callbacks_update_scrollbar_limits();
3166 callbacks_update_scrollbar_positions();