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