1 /* Lepton EDA Schematic Capture
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 #include <math.h>
23 #include "gschem.h"
24 
25 
26 extern LeptonColorMap display_colors;
27 
28 #define X_IMAGE_DEFAULT_SIZE "800x600"
29 #define X_IMAGE_DEFAULT_TYPE "PNG"
30 
31 static const char *x_image_sizes[] =
32 {
33   "320x240",
34   "640x480",
35   "800x600",
36   "1024x768",
37   "1200x768",
38   "1280x960",
39   "1600x1200",
40   "3200x2400",
41   NULL
42 };
43 
44 
45 
46 /*! \brief Restore last selected item in \a combo combobox
47  *
48  *  \par Function Description
49  *  Helper function used in settings_restore().
50  */
51 static void
settings_restore_combo(EdaConfig * cfg,GtkComboBox * combo,const gchar * group,const gchar * key)52 settings_restore_combo (EdaConfig*   cfg,
53                         GtkComboBox* combo,
54                         const gchar* group,
55                         const gchar* key)
56 {
57   GtkTreeModel* model = gtk_combo_box_get_model (combo);
58   gint          count = gtk_tree_model_iter_n_children (model, NULL);
59 
60   GError* err = NULL;
61   gint index = eda_config_get_int (cfg, group, key, &err);
62 
63   if (err == NULL && index >= 0 && index < count)
64   {
65     gtk_combo_box_set_active (combo, index);
66   }
67 
68   g_clear_error (&err);
69 }
70 
71 
72 
73 /*! \brief Restore "Write image" dialog settings
74  *
75  *  \par Function Description
76  *  Load the following settings from the CACHE configuration
77  *  context ("schematic.write-image-dialog" group):
78  *  - selected directory
79  *  - image size
80  *  - image type
81  *  - image color mode
82  *
83  *  \note Call this function after the dialog is fully constructed.
84  *
85 *   \param dialog      "Write image" dialog widget
86  *  \param size_combo  Combo box widget with list of image sizes
87  *  \param type_combo  Combo box widget with list of image types
88  *  \param color_combo Combo box widget with list of color modes
89  */
90 static void
settings_restore(GtkFileChooser * dialog,GtkComboBox * size_combo,GtkComboBox * type_combo,GtkComboBox * color_combo)91 settings_restore (GtkFileChooser* dialog,
92                   GtkComboBox*    size_combo,
93                   GtkComboBox*    type_combo,
94                   GtkComboBox*    color_combo)
95 {
96   EdaConfig* cfg = eda_config_get_cache_context();
97   GError*    err = NULL;
98 
99   gchar* dir = eda_config_get_string (cfg,
100                                       "schematic.write-image-dialog",
101                                       "save-path",
102                                       &err);
103   if (err == NULL && dir != NULL)
104   {
105     gtk_file_chooser_set_current_folder (dialog, dir);
106     g_free (dir);
107   }
108 
109   settings_restore_combo (cfg,
110                           size_combo,
111                           "schematic.write-image-dialog",
112                           "image-size");
113   settings_restore_combo (cfg,
114                           type_combo,
115                           "schematic.write-image-dialog",
116                           "image-type");
117   settings_restore_combo (cfg,
118                           color_combo,
119                           "schematic.write-image-dialog",
120                           "image-color");
121 }
122 
123 
124 
125 /*! \brief Save "Write image" dialog settings
126  *
127  *  \par Function Description
128  *  Save the following settings to the CACHE configuration
129  *  context ("schematic.write-image-dialog" group):
130  *  - selected directory
131  *  - image size
132  *  - image type
133  *  - image color mode
134  *
135 *   \param dialog      "Write image" dialog widget
136  *  \param size_combo  Combo box widget with list of image sizes
137  *  \param type_combo  Combo box widget with list of image types
138  *  \param color_combo Combo box widget with list of color modes
139  */
140 static void
settings_save(GtkFileChooser * dialog,GtkComboBox * size_combo,GtkComboBox * type_combo,GtkComboBox * color_combo)141 settings_save (GtkFileChooser* dialog,
142                GtkComboBox*    size_combo,
143                GtkComboBox*    type_combo,
144                GtkComboBox*    color_combo)
145 {
146   EdaConfig* cfg = eda_config_get_cache_context();
147 
148   gchar* dir = gtk_file_chooser_get_current_folder (dialog);
149   if (dir != NULL)
150   {
151     eda_config_set_string (cfg,
152                            "schematic.write-image-dialog",
153                            "save-path",
154                            dir);
155     g_free (dir);
156   }
157 
158   eda_config_set_int (cfg,
159                       "schematic.write-image-dialog",
160                       "image-size",
161                       gtk_combo_box_get_active (size_combo));
162   eda_config_set_int (cfg,
163                       "schematic.write-image-dialog",
164                       "image-type",
165                       gtk_combo_box_get_active (type_combo));
166   eda_config_set_int (cfg,
167                       "schematic.write-image-dialog",
168                       "image-color",
169                       gtk_combo_box_get_active (color_combo));
170 
171   eda_config_save (cfg, NULL);
172 }
173 
174 
175 
176 /*! \brief Create the options of the image size combobox
177  *  \par This function adds the options of the image size to the given combobox.
178  *  \param combo [in] the combobox to add the options to.
179  *  \return nothing
180  *  \note
181  *  This function is only used in this file, there are other create_menus...
182  */
create_size_menu(GtkComboBox * combo)183 static void create_size_menu (GtkComboBox *combo)
184 {
185   char *buf;
186   char *default_size;
187   int i, default_index = 0;
188 
189   default_size = g_strdup (X_IMAGE_DEFAULT_SIZE);
190   for (i=0; x_image_sizes[i] != NULL;i++) {
191     /* Create a new string and add it as an option*/
192     buf = g_strdup (x_image_sizes[i]);
193     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), buf);
194 
195     /* Compare with the default size, to get the default index */
196     if (strcasecmp(buf, default_size ) == 0) {
197       default_index = i;
198     }
199     g_free(buf);
200   }
201   g_free(default_size);
202 
203   /* Set the default menu */
204   gtk_combo_box_set_active(GTK_COMBO_BOX (combo), default_index);
205 }
206 
207 /*! \brief Create the options of the image type combobox
208  *  \par This function adds the options of the image type to the given combobox.
209  *  \param combo [in] the combobox to add the options to.
210  *  \return nothing
211  *  \note
212  *  This function is only used in this file, there are other create_menus...
213  */
create_type_menu(GtkComboBoxText * combo)214 static void create_type_menu (GtkComboBoxText *combo)
215 {
216   GSList *formats = gdk_pixbuf_get_formats ();
217   GSList *ptr;
218   char *buf;
219   int i=0, default_index=0;
220 
221   ptr = formats;
222   while (ptr) {
223     if (gdk_pixbuf_format_is_writable ((GdkPixbufFormat*) ptr->data)) {
224       /* Get the format description and add it to the menu */
225       buf = g_strdup (gdk_pixbuf_format_get_description ((GdkPixbufFormat*) ptr->data));
226       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), buf);
227 
228       /* Compare the name with "png" and store the index */
229       buf = g_strdup (gdk_pixbuf_format_get_name ((GdkPixbufFormat*) ptr->data));
230       if (strcasecmp(buf, X_IMAGE_DEFAULT_TYPE) == 0) {
231         default_index = i;
232       }
233       i++;  /* this is the count of items added to the combo box */
234       /* not the total number of pixbuf formats */
235       g_free(buf);
236     }
237     ptr = ptr->next;
238   }
239   g_slist_free (formats);
240   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Portable Document Format");
241 
242   /* Set the default menu */
243   gtk_combo_box_set_active(GTK_COMBO_BOX(combo), default_index);
244 }
245 
246 /*! \brief Given a gdk-pixbuf image type description, it returns the type,
247  *  or extension of the image.
248  *  \par Return the gdk-pixbuf image type, or extension, which has the
249  *  given gdk-pixbuf description.
250  *  \param description The gdk-pixbuf image type description.
251  *  \return The gdk-pixbuf type, or extension, of the image.
252  *  \note This function is only used in this file.
253  *  \note The caller must g_free the result of this function.
254  */
255 static gchar*
x_image_get_type_from_description(const char * description)256 x_image_get_type_from_description (const char *description)
257 {
258   GSList *ptr;
259   gchar *image_type = NULL;
260 
261   if (strcmp (description, "Portable Document Format") == 0) {
262     image_type = g_strdup ("pdf");
263   } else {
264     ptr = gdk_pixbuf_get_formats ();
265 
266     while (ptr != NULL) {
267       gchar *ptr_descr = gdk_pixbuf_format_get_description ((GdkPixbufFormat*) ptr->data);
268 
269       if (ptr_descr && (strcasecmp (ptr_descr, description) == 0)) {
270         image_type = gdk_pixbuf_format_get_name ((GdkPixbufFormat*) ptr->data);
271       }
272 
273       ptr = g_slist_next (ptr);
274     }
275   }
276 
277   return image_type;
278 }
279 
280 /*! \brief Update the filename of a file dialog, when the image type has changed.
281  *  \par Given a combobox inside a file chooser dialog, this function updates
282  *  the filename displayed by the dialog, removing the current extension, and
283  *  adding the extension of the image type selected.
284  *  \param combo     [in] A combobox inside a file chooser dialog, with gdk-pixbuf image type descriptions.
285  *  \param w_current [in] the GschemToplevel structure.
286  *  \return nothing.
287  *
288  */
289 static void
x_image_update_dialog_filename(GtkComboBoxText * combo,GschemToplevel * w_current)290 x_image_update_dialog_filename (GtkComboBoxText *combo,
291                                 GschemToplevel *w_current) {
292   char* image_type_descr = NULL;
293   gchar *image_type = NULL;
294   const char *old_image_filename = NULL;
295   char *file_basename = NULL;
296   char *file_name = NULL ;
297   char *new_image_filename = NULL;
298   GtkWidget *file_chooser;
299 
300   /* Get the current image type */
301   image_type_descr =
302     gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo));
303   image_type = x_image_get_type_from_description(image_type_descr);
304 
305   /* Get the parent dialog */
306   file_chooser = gtk_widget_get_ancestor(GTK_WIDGET(combo),
307       GTK_TYPE_FILE_CHOOSER);
308 
309   /* Get the previous file name. If none, revert to the page filename */
310   old_image_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser));
311   if (!old_image_filename) {
312     LeptonPage *active_page = schematic_window_get_active_page (w_current);
313     old_image_filename = lepton_page_get_filename (active_page);
314   }
315 
316   /* Get the file name, without extension */
317   if (old_image_filename) {
318     file_basename = g_path_get_basename(old_image_filename);
319 
320     if (g_strrstr(file_basename, ".") != NULL) {
321       file_name = g_strndup(file_basename,
322           g_strrstr(file_basename, ".") - file_basename);
323     }
324   }
325 
326   /* Add the extension */
327   if (file_name) {
328     new_image_filename = g_strdup_printf("%s.%s", file_name,
329         image_type);
330   } else {
331     new_image_filename = g_strdup_printf("%s.%s", file_basename,
332         image_type);
333   }
334 
335   g_free (image_type);
336 
337   /* Set the new filename */
338   if (file_chooser) {
339     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser),
340         new_image_filename);
341   } else {
342     g_message ("x_image_update_dialog_filename: No parent file chooser found!.");
343   }
344 
345   g_free(file_name);
346   g_free(file_basename);
347   g_free(new_image_filename);
348 }
349 
350 /*! \brief Write the image file, with the desired options.
351  *  \par This function writes the image file, with the options set in the
352  *  dialog by the user.
353  *  \param w_current [in] the GschemToplevel structure.
354  *  \param filename  [in] the image filename.
355  *  \param width     [in] the image width chosen by the user.
356  *  \param height    [in] the image height chosen by the user.
357  *  \param filetype  [in] image filetype.
358  *  \param is_color  [in] write image using colors (TRUE) or in grayscale (FALSE).
359  *  \return nothing
360  *
361  */
x_image_lowlevel(GschemToplevel * w_current,const char * filename,int width,int height,const char * filetype,gboolean is_color)362 void x_image_lowlevel(GschemToplevel *w_current, const char* filename,
363                        int width, int height, const char *filetype, gboolean is_color)
364 {
365   int save_page_left, save_page_right, save_page_top, save_page_bottom;
366   int page_width, page_height, page_center_left, page_center_top;
367   GdkPixbuf *pixbuf;
368   GError *gerror = NULL;
369   GtkWidget *dialog;
370   float prop;
371   GschemPageView *view = gschem_toplevel_get_current_page_view (w_current);
372 
373   GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view);
374   g_return_if_fail (geometry != NULL);
375 
376   /* Save geometry */
377   save_page_left = geometry->viewport_left;
378   save_page_right = geometry->viewport_right;
379   save_page_top = geometry->viewport_top;
380   save_page_bottom = geometry->viewport_bottom;
381 
382   page_width = geometry->viewport_right - geometry->viewport_left;
383   page_height = geometry->viewport_bottom - geometry->viewport_top;
384 
385   page_center_left = geometry->viewport_left + (page_width / 2);
386   page_center_top = geometry->viewport_top + (page_height / 2);
387 
388   /* Preserve proportions */
389   prop = (float)width / height;
390   if (((float)page_width / page_height) > prop) {
391     page_height = (page_width / prop);
392   }else{
393     page_width = (page_height * prop);
394   }
395 
396   gschem_page_geometry_set_viewport_left   (geometry, page_center_left - (page_width / 2));
397   gschem_page_geometry_set_viewport_right  (geometry, page_center_left + (page_width / 2));
398   gschem_page_geometry_set_viewport_top    (geometry, page_center_top - (page_height / 2));
399   gschem_page_geometry_set_viewport_bottom (geometry, page_center_top + (page_height / 2));
400 
401   /* de select everything first */
402   o_select_unselect_all( w_current );
403 
404   if (strcmp(filetype, "pdf") == 0)
405     x_print_export_pdf (w_current, filename, is_color);
406   else {
407     pixbuf = x_image_get_pixbuf(w_current, width, height, is_color);
408     if (pixbuf != NULL) {
409       if (!gdk_pixbuf_save(pixbuf, filename, filetype, &gerror, NULL)) {
410         g_message ("x_image_lowlevel: ");
411         g_message (_("Unable to write %1$s file %2$s."),
412                    filetype, filename);
413         g_message ("%s", gerror->message);
414 
415         /* Warn the user */
416         dialog = gtk_message_dialog_new (GTK_WINDOW(w_current->main_window),
417                                          (GtkDialogFlags) (GTK_DIALOG_MODAL
418                                                            | GTK_DIALOG_DESTROY_WITH_PARENT),
419                                          GTK_MESSAGE_ERROR,
420                                          GTK_BUTTONS_OK,
421                                          _("There was the following error when saving image with type %1$s to filename:\n%2$s\n\n%3$s.\n"),
422                                          filetype,
423                                          filename,
424                                          gerror->message);
425 
426         gtk_dialog_run (GTK_DIALOG (dialog));
427         gtk_widget_destroy (dialog);
428 
429         /* Free the gerror */
430         g_error_free(gerror);
431         gerror = NULL;
432 
433         /* Unlink the output file */
434         /* It's not safe to unlink the file if there was an error.
435            For example: if the operation was not allowed due to permissions,
436            the _previous existing_ file will be removed */
437         /* unlink(filename); */
438       }
439       else {
440         if (is_color) {
441           g_message (_("Wrote color image to [%1$s] [%2$d x %3$d]"), filename, width, height);
442         } else {
443           g_message (_("Wrote black and white image to [%1$s] [%2$d x %3$d]"), filename, width, height);
444         }
445       }
446 
447       if (pixbuf != NULL)
448         g_object_unref(pixbuf);
449     }
450     else {
451       g_message ("x_image_lowlevel: ");
452       g_message (_("Unable to get pixbuf from lepton-schematic's window."));
453     }
454   }
455 
456   /* Restore geometry */
457   gschem_page_geometry_set_viewport_left   (geometry, save_page_left  );
458   gschem_page_geometry_set_viewport_right  (geometry, save_page_right );
459   gschem_page_geometry_set_viewport_top    (geometry, save_page_top   );
460   gschem_page_geometry_set_viewport_bottom (geometry, save_page_bottom);
461 
462   gschem_page_view_invalidate_all (view);
463 }
464 
465 /*! \brief Display the image file selection dialog.
466  *  \par Display the image file selection dialog, allowing the user to
467  *  set several options, like image size and image type.
468  *  When the user hits "ok", then it writes the image file.
469  *  \param w_current [in] the GschemToplevel structure.
470  *  \return nothing
471  */
x_image_setup(GschemToplevel * w_current)472 void x_image_setup (GschemToplevel *w_current)
473 {
474   GtkWidget *dialog;
475   GtkWidget *vbox1;
476   GtkWidget *hbox;
477   GtkWidget *label1;
478   GtkWidget *size_combo;
479   GtkWidget *vbox2;
480   GtkWidget *label2;
481   GtkWidget *type_combo;
482   char *image_type_descr;
483   gchar *filename, *image_type;
484   char *image_size;
485   int width, height;
486 
487 #ifdef ENABLE_GTK3
488   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
489   GtkWidget* vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
490   GtkWidget* hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
491 #else
492   hbox = gtk_hbox_new(FALSE, 0);
493   GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
494   GtkWidget* hbox2 = gtk_hbox_new (FALSE, 0);
495 #endif
496 
497   GtkWidget* label = gtk_label_new(
498     _("NOTE: print-color-map will be used for PDF export"));
499   gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 10);
500   gtk_box_pack_start (GTK_BOX (vbox),  hbox2, FALSE, FALSE, 5);
501   gtk_box_pack_start (GTK_BOX (vbox),  hbox,  FALSE, FALSE, 0);
502   gtk_widget_show_all (vbox);
503 
504 
505   /* Image size selection */
506 #ifdef ENABLE_GTK3
507   vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
508 #else
509   vbox1 = gtk_vbox_new(TRUE, 0);
510 #endif
511   label1 = gtk_label_new (_("Width x Height"));
512   gtk_widget_show (label1);
513   gtk_misc_set_alignment( GTK_MISC (label1), 0, 0);
514   gtk_misc_set_padding (GTK_MISC (label1), 0, 0);
515   gtk_box_pack_start (GTK_BOX (vbox1),
516       label1, FALSE, FALSE, 0);
517 
518   size_combo =  gtk_combo_box_text_new ();
519   create_size_menu (GTK_COMBO_BOX(size_combo));
520 
521   gtk_widget_show (size_combo);
522   gtk_box_pack_start (GTK_BOX (vbox1), size_combo, TRUE, TRUE, 0);
523   gtk_widget_show(vbox1);
524 
525   /* Image type selection */
526 #ifdef ENABLE_GTK3
527   vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
528 #else
529   vbox2 = gtk_vbox_new(TRUE, 0);
530 #endif
531   label2 = gtk_label_new (_("Image type"));
532   gtk_widget_show (label2);
533   gtk_misc_set_alignment( GTK_MISC (label2), 0, 0);
534   gtk_misc_set_padding (GTK_MISC (label2), 0, 0);
535   gtk_box_pack_start (GTK_BOX (vbox2),
536       label2, FALSE, FALSE, 0);
537 
538   type_combo = gtk_combo_box_text_new ();
539   gtk_box_pack_start (GTK_BOX (vbox2), type_combo, TRUE, TRUE, 0);
540   create_type_menu (GTK_COMBO_BOX_TEXT (type_combo));
541 
542 
543   /* Color/grayscale selection:
544   */
545 #ifdef ENABLE_GTK3
546   GtkWidget* vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
547 #else
548   GtkWidget* vbox3 = gtk_vbox_new (TRUE, 0);
549 #endif
550   GtkWidget* label3 = gtk_label_new (_("Color mode"));
551   gtk_misc_set_alignment (GTK_MISC (label3), 0, 0);
552   gtk_misc_set_padding (GTK_MISC (label3), 0, 0);
553   gtk_box_pack_start (GTK_BOX (vbox3), label3, FALSE, FALSE, 0);
554 
555   GtkWidget* color_combo = gtk_combo_box_text_new();
556   gtk_box_pack_start (GTK_BOX (vbox3), color_combo, TRUE, TRUE, 0);
557   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (color_combo), _("Color"));
558   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (color_combo), _("Grayscale"));
559   gtk_combo_box_set_active (GTK_COMBO_BOX (color_combo), 0);
560 
561   gtk_widget_show_all (vbox3);
562 
563 
564   /* Connect the changed signal to the callback, so the filename
565      gets updated every time the image type is changed */
566   g_signal_connect (type_combo, "changed",
567       G_CALLBACK(x_image_update_dialog_filename),
568       w_current);
569 
570   gtk_widget_show (type_combo);
571   gtk_widget_show(vbox2);
572 
573   /* Create the dialog */
574   dialog = gtk_file_chooser_dialog_new (_("Write Image"),
575       GTK_WINDOW(w_current->main_window),
576       GTK_FILE_CHOOSER_ACTION_SAVE,
577       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
578       GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
579       NULL);
580 
581   /* Set the alternative button order (ok, cancel, help) for other systems */
582   gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
583       GTK_RESPONSE_ACCEPT,
584       GTK_RESPONSE_CANCEL,
585       -1);
586 
587   /* Add the extra widgets to the dialog*/
588   gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, FALSE, 10);
589   gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 10);
590   gtk_box_pack_start(GTK_BOX(hbox), vbox3, FALSE, FALSE, 10);
591 
592   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
593 
594   g_object_set (dialog,
595       /* GtkFileChooser */
596       "select-multiple", FALSE,
597       /* only in GTK 2.8 */
598       "do-overwrite-confirmation", TRUE,
599       NULL);
600 
601   /* Update the filename */
602   x_image_update_dialog_filename (GTK_COMBO_BOX_TEXT (type_combo), w_current);
603 
604   gtk_dialog_set_default_response(GTK_DIALOG(dialog),
605       GTK_RESPONSE_ACCEPT);
606 
607   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
608 
609   gtk_container_set_border_width(GTK_CONTAINER(dialog),
610       DIALOG_BORDER_SPACING);
611   gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
612                        DIALOG_V_SPACING);
613 
614 
615   settings_restore (GTK_FILE_CHOOSER (dialog),
616                     GTK_COMBO_BOX (size_combo),
617                     GTK_COMBO_BOX (type_combo),
618                     GTK_COMBO_BOX (color_combo));
619 
620 
621   gtk_widget_show (dialog);
622 
623   if (gtk_dialog_run((GTK_DIALOG(dialog))) == GTK_RESPONSE_ACCEPT) {
624     image_size =
625       gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (size_combo));
626 
627     image_type_descr =
628       gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (type_combo));
629 
630     image_type = x_image_get_type_from_description(image_type_descr);
631     sscanf(image_size, "%ix%i", &width, &height);
632     filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
633 
634     /* the first item in the color_combo is "Color", 2nd = "Grayscale":
635     */
636     gboolean is_color = gtk_combo_box_get_active (GTK_COMBO_BOX (color_combo)) == 0;
637 
638     x_image_lowlevel(w_current, filename, width, height, image_type, is_color);
639 
640     g_free (filename);
641     g_free (image_type);
642 
643 
644     settings_save (GTK_FILE_CHOOSER (dialog),
645                    GTK_COMBO_BOX (size_combo),
646                    GTK_COMBO_BOX (type_combo),
647                    GTK_COMBO_BOX (color_combo));
648   }
649 
650   gtk_widget_destroy (dialog);
651 }
652 
653 /*! \todo Finish function documentation!!!
654  *  \brief
655  *  \par Function Description
656  *
657  */
x_image_convert_to_greyscale(GdkPixbuf * pixbuf)658 static void x_image_convert_to_greyscale(GdkPixbuf *pixbuf)
659 {
660   int width, height, rowstride, n_channels;
661   guchar *pixels, *p, new_value;
662   int i, j;
663 
664   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
665 
666   if (n_channels != 3)
667   {
668     return;
669   }
670 
671   if (gdk_pixbuf_get_colorspace (pixbuf) != GDK_COLORSPACE_RGB)
672   {
673     return;
674   }
675 
676   if (gdk_pixbuf_get_bits_per_sample (pixbuf) != 8)
677   {
678     return;
679   }
680 
681   width = gdk_pixbuf_get_width (pixbuf);
682   height = gdk_pixbuf_get_height (pixbuf);
683 
684   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
685   pixels = gdk_pixbuf_get_pixels (pixbuf);
686 
687   for (j = 0; j < height; j++)
688   {
689     for (i = 0; i < width; i++)
690     {
691       p = pixels + j * rowstride + i * n_channels;
692 
693       new_value = 0.3 * p[0] + 0.59 * p[1] + 0.11 * p[2];
694       p[0] = new_value;
695       p[1] = new_value;
696       p[2] = new_value;
697     }
698   }
699 }
700 
701 /*! \todo Finish function documentation!!!
702  *  \brief
703  *  \par Function Description
704  *
705  */
706 #ifdef ENABLE_GTK3
707 GdkPixbuf
x_image_get_pixbuf(GschemToplevel * w_current,int width,int height,gboolean is_color)708 *x_image_get_pixbuf (GschemToplevel *w_current, int width, int height, gboolean is_color)
709 {
710   GdkPixbuf *pixbuf;
711   GschemPageView *page_view;
712   LeptonPage *page;
713   int origin_x, origin_y, bottom, right;
714   GschemPageGeometry *old_geometry, *new_geometry;
715 
716   GList *obj_list;
717   GList *iter;
718   LeptonBox *world_rect;
719   EdaRenderer *renderer;
720   int render_flags;
721   GArray *render_color_map = NULL;
722 
723   cairo_surface_t *cs;
724   cairo_t *cr;
725 
726   g_return_val_if_fail (w_current != NULL, NULL);
727 
728   page_view = gschem_toplevel_get_current_page_view (w_current);
729 
730   g_return_val_if_fail (page_view != NULL, NULL);
731 
732   page = gschem_page_view_get_page (page_view);
733 
734   g_return_val_if_fail (page != NULL, NULL);
735 
736   old_geometry = gschem_page_view_get_page_geometry (page_view);
737 
738   cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
739   cr = cairo_create (cs);
740 
741   origin_x = origin_y = 0;
742   right = width;
743   bottom = height;
744 
745   new_geometry =
746     gschem_page_geometry_new_with_values (width,
747                                           height,
748                                           old_geometry->viewport_left,
749                                           old_geometry->viewport_top,
750                                           old_geometry->viewport_right,
751                                           old_geometry->viewport_bottom,
752                                           WORLD_DEFAULT_LEFT,
753                                           WORLD_DEFAULT_TOP,
754                                           WORLD_DEFAULT_RIGHT,
755                                           WORLD_DEFAULT_BOTTOM);
756 
757   cairo_set_matrix (cr, gschem_page_geometry_get_world_to_screen_matrix (new_geometry));
758 
759   world_rect = g_new (LeptonBox, 1);
760 
761   double lower_x = 0;
762   double lower_y = height;
763   double upper_x = width;
764   double upper_y = 0;
765 
766   cairo_device_to_user (cr, &lower_x, &lower_y);
767   cairo_device_to_user (cr, &upper_x, &upper_y);
768 
769   world_rect->lower_x = floor (lower_x);
770   world_rect->lower_y = floor (lower_y);
771   world_rect->upper_x = ceil (upper_x);
772   world_rect->upper_y = ceil (upper_y);
773 
774   gboolean show_hidden_text =
775     gschem_toplevel_get_show_hidden_text (w_current);
776 
777   obj_list = lepton_page_objects_in_regions (page,
778                                              world_rect,
779                                              1,
780                                              show_hidden_text);
781 
782   g_free (world_rect);
783 
784   /* Set up renderer based on configuration in w_current */
785   render_flags = EDA_RENDERER_FLAG_HINTING;
786   if (show_hidden_text)
787     render_flags |= EDA_RENDERER_FLAG_TEXT_HIDDEN;
788 
789   /* This color map is used for "normal" rendering. */
790   render_color_map =
791     g_array_sized_new (FALSE, FALSE, sizeof(LeptonColor), colors_count());
792   render_color_map =
793     g_array_append_vals (render_color_map, display_colors, colors_count());
794 
795   /* Set up renderer */
796   renderer = eda_renderer_new (NULL, NULL);
797   g_object_set (G_OBJECT (renderer),
798                 "cairo-context", cr,
799                 "render-flags", render_flags,
800                 "color-map", render_color_map,
801                 NULL);
802 
803   /* Paint background */
804   LeptonColor *color = x_color_lookup (BACKGROUND_COLOR);
805 
806   cairo_set_source_rgba (cr,
807                          lepton_color_get_red_double (color),
808                          lepton_color_get_green_double (color),
809                          lepton_color_get_blue_double (color),
810                          lepton_color_get_alpha_double (color));
811 
812   cairo_paint (cr);
813   cairo_destroy (cr);
814 
815   for (iter = obj_list; iter != NULL; iter = g_list_next (iter)) {
816     LeptonObject *o_current = (LeptonObject*) iter->data;
817 
818     if (!o_current->dont_redraw) {
819       eda_renderer_draw (renderer, o_current);
820     }
821   }
822 
823   g_list_free (obj_list);
824   g_object_unref (G_OBJECT (renderer));
825   g_array_free (render_color_map, TRUE);
826 
827   gschem_page_geometry_free (new_geometry);
828 
829   /* Get the pixbuf */
830   pixbuf = gdk_pixbuf_get_from_surface (cs, 0, 0, right-origin_x, bottom-origin_y);
831   cairo_surface_destroy (cs);
832 
833   if (!is_color)
834   {
835     x_image_convert_to_greyscale(pixbuf);
836   }
837 
838   return(pixbuf);
839 }
840 
841 #else /* GTK2 */
842 
843 GdkPixbuf
x_image_get_pixbuf(GschemToplevel * w_current,int width,int height,gboolean is_color)844 *x_image_get_pixbuf (GschemToplevel *w_current, int width, int height, gboolean is_color)
845 {
846   GdkPixbuf *pixbuf;
847   GschemPageView *page_view;
848   int origin_x, origin_y, bottom, right;
849   GschemToplevel new_w_current;
850   GschemOptions options;
851   LeptonToplevel toplevel;
852   GdkRectangle rect;
853   GschemPageGeometry *old_geometry, *new_geometry;
854   GdkPixmap *window = NULL;
855 
856   page_view = gschem_toplevel_get_current_page_view (w_current);
857 
858   old_geometry = gschem_page_view_get_page_geometry (page_view);
859 
860   /* Do a copy of the w_current struct and work with it */
861   memcpy(&new_w_current, w_current, sizeof(GschemToplevel));
862   /* Do a copy of the options struct and work with it */
863   memcpy(&options, w_current->options, sizeof(GschemOptions));
864   /* Do a copy of the toplevel struct and work with it */
865   memcpy(&toplevel, w_current->toplevel, sizeof(LeptonToplevel));
866 
867   new_w_current.toplevel = &toplevel;
868   new_w_current.options = &options;
869 
870   window = gdk_pixmap_new (gtk_widget_get_window (GTK_WIDGET(page_view)), width, height, -1);
871 
872   gschem_options_set_grid_mode (new_w_current.options, GRID_MODE_NONE);
873 
874   /*! \bug Need to handle image color setting properly.
875    *       See gEDA Launchpad bug 1086530.
876    *
877    * if (toplevel.image_color == FALSE)
878    * {
879    * }
880   */
881 
882   origin_x = origin_y = 0;
883   right = width;
884   bottom = height;
885 
886   rect.x = origin_x;
887   rect.y = origin_y;
888   rect.width = right - origin_x;
889   rect.height = bottom - origin_y;
890 
891   new_geometry =
892     gschem_page_geometry_new_with_values (width,
893                                           height,
894                                           old_geometry->viewport_left,
895                                           old_geometry->viewport_top,
896                                           old_geometry->viewport_right,
897                                           old_geometry->viewport_bottom,
898                                           WORLD_DEFAULT_LEFT,
899                                           WORLD_DEFAULT_TOP,
900                                           WORLD_DEFAULT_RIGHT,
901                                           WORLD_DEFAULT_BOTTOM);
902 
903   o_redraw_rect (&new_w_current,
904                  window,
905                  toplevel.page_current,
906                  new_geometry,
907                  &rect);
908 
909   gschem_page_geometry_free (new_geometry);
910 
911   /* Get the pixbuf */
912   pixbuf = gdk_pixbuf_get_from_drawable (NULL, window, NULL,
913                                         origin_x, origin_y, 0, 0,
914                                         right-origin_x,
915                                         bottom-origin_y);
916 
917   if (!is_color)
918   {
919     x_image_convert_to_greyscale(pixbuf);
920   }
921 
922   if (window != NULL) {
923     g_object_unref(window);
924   }
925 
926   return(pixbuf);
927 }
928 #endif
929