Bump gEDA version
[geda-gaf.git] / gschem / src / o_picture.c
blob0ed0d21635a5d459d734dc53ac613c66264aac2e
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
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.
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.
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
20 #include <config.h>
21 #include <math.h>
22 #include <stdio.h>
24 #include "gschem.h"
25 #include "actions.decl.x"
27 /* This works, but using one macro inside of other doesn't */
28 #define GET_PICTURE_WIDTH(w) \
29 abs((w)->second_wx - (w)->first_wx)
30 #define GET_PICTURE_HEIGHT(w) \
31 (w)->pixbuf_wh_ratio == 0 ? 0 : abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
32 #define GET_PICTURE_LEFT(w) \
33 min((w)->first_wx, (w)->second_wx)
34 #define GET_PICTURE_TOP(w) \
35 (w)->first_wy > (w)->second_wy ? (w)->first_wy : \
36 (w)->first_wy+abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
38 /*! \brief Start process to input a new picture.
39 * \par Function Description
40 * This function starts the process to input a new picture. Parameters
41 * for this picture are put into/extracted from the <B>w_current</B> toplevel
42 * structure.
43 * <B>w_x</B> and <B>w_y</B> are current coordinates of the pointer in world
44 * coordinates.
46 * The first step is to input one corner of the picture. This corner is
47 * (<B>w_x</B>,<B>w_y</B>) snapped to the grid and saved in
48 * <B>w_current->first_wx</B> and <B>w_current->first_wy</B>.
50 * The other corner will be saved in (<B>w_current->second_wx</B>,
51 * <B>w_current->second_wy</B>).
53 * \param [in] w_current The GschemToplevel object.
54 * \param [in] w_x Current x coordinate of pointer in world units.
55 * \param [in] w_y Current y coordinate of pointer in world units.
57 void o_picture_start(GschemToplevel *w_current, int w_x, int w_y)
59 i_action_start (w_current);
61 /* init first_w[x|y], second_w[x|y] to describe box */
62 w_current->first_wx = w_current->second_wx = w_x;
63 w_current->first_wy = w_current->second_wy = w_y;
65 /* start to draw the box */
66 o_picture_invalidate_rubber (w_current);
67 w_current->rubber_visible = 1;
70 /*! \brief End the input of a circle.
71 * \par Function Description
72 * This function ends the input of the second corner of a picture.
73 * The picture is defined by (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>
74 * and (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>.
76 * The temporary picture frame is erased ; a new picture object is allocated,
77 * initialized and linked to the object list ; The object is finally
78 * drawn on the current sheet.
80 * \param [in] w_current The GschemToplevel object.
81 * \param [in] w_x (unused)
82 * \param [in] w_y (unused)
84 void o_picture_end(GschemToplevel *w_current, int w_x, int w_y)
86 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
87 OBJECT *new_obj;
88 int picture_width, picture_height;
89 int picture_left, picture_top;
91 g_assert( w_current->inside_action != 0 );
93 /* erase the temporary picture */
94 /* o_picture_draw_rubber(w_current); */
95 w_current->rubber_visible = 0;
97 picture_width = GET_PICTURE_WIDTH (w_current);
98 picture_height = GET_PICTURE_HEIGHT(w_current);
99 picture_left = GET_PICTURE_LEFT (w_current);
100 picture_top = GET_PICTURE_TOP (w_current);
102 /* pictures with null width or height are not allowed */
103 if ((picture_width != 0) && (picture_height != 0)) {
105 /* create the object */
106 new_obj = o_picture_new(toplevel,
107 NULL, 0, w_current->pixbuf_filename,
108 OBJ_PICTURE,
109 picture_left, picture_top,
110 picture_left + picture_width,
111 picture_top - picture_height,
112 0, FALSE, FALSE);
113 s_page_append (toplevel, toplevel->page_current, new_obj);
115 /* Run %add-objects-hook */
116 g_run_hook_object (w_current, "%add-objects-hook", new_obj);
118 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
119 o_undo_savestate_old (w_current, UNDO_ALL, _("Add Picture"));
121 i_action_stop (w_current);
124 /*! \brief Creates the add image dialog
125 * \par Function Description
126 * This function creates the add image dialog and loads the selected picture.
128 void picture_selection_dialog (GschemToplevel *w_current)
130 gchar *filename;
131 GdkPixbuf *pixbuf;
132 GError *error = NULL;
134 w_current->pfswindow = gtk_file_chooser_dialog_new (_("Select a picture file..."),
135 GTK_WINDOW(w_current->main_window),
136 GTK_FILE_CHOOSER_ACTION_OPEN,
137 GTK_STOCK_CANCEL,
138 GTK_RESPONSE_CANCEL,
139 GTK_STOCK_OPEN,
140 GTK_RESPONSE_ACCEPT,
141 NULL);
142 /* Set the alternative button order (ok, cancel, help) for other systems */
143 gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current->pfswindow),
144 GTK_RESPONSE_ACCEPT,
145 GTK_RESPONSE_CANCEL,
146 -1);
148 if (w_current->pixbuf_filename)
149 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current->pfswindow),
150 w_current->pixbuf_filename);
152 if (gtk_dialog_run (GTK_DIALOG (w_current->pfswindow)) == GTK_RESPONSE_ACCEPT) {
154 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current->pfswindow));
155 gtk_widget_destroy(w_current->pfswindow);
156 w_current->pfswindow=NULL;
158 pixbuf = gdk_pixbuf_new_from_file (filename, &error);
160 if (!pixbuf) {
161 GtkWidget *dialog;
163 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
164 GTK_DIALOG_DESTROY_WITH_PARENT,
165 GTK_MESSAGE_ERROR,
166 GTK_BUTTONS_CLOSE,
167 _("Failed to load picture: %s"),
168 error->message);
169 /* Wait for any user response */
170 gtk_dialog_run (GTK_DIALOG (dialog));
172 g_error_free (error);
173 gtk_widget_destroy(dialog);
175 else {
176 #if DEBUG
177 printf("Picture loaded succesfully.\n");
178 #endif
180 o_invalidate_rubber(w_current);
181 i_update_middle_button (w_current, action_add_picture, _("Picture"));
182 i_action_stop (w_current);
184 o_picture_set_pixbuf(w_current, pixbuf, filename);
186 i_set_state(w_current, PICTUREMODE);
188 g_free (filename);
191 if (w_current->pfswindow) {
192 gtk_widget_destroy(w_current->pfswindow);
193 w_current->pfswindow=NULL;
197 /*! \todo Finish function documentation!!!
198 * \brief
199 * \par Function Description
201 * \note
202 * used in button cancel code in x_events.c
204 void o_picture_invalidate_rubber (GschemToplevel *w_current)
206 g_return_if_fail (w_current != NULL);
208 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
209 g_return_if_fail (page_view != NULL);
211 gschem_page_view_invalidate_world_rect (page_view,
212 GET_PICTURE_LEFT (w_current),
213 GET_PICTURE_TOP (w_current),
214 GET_PICTURE_LEFT (w_current) + GET_PICTURE_WIDTH (w_current),
215 GET_PICTURE_TOP (w_current) + GET_PICTURE_HEIGHT (w_current));
218 /*! \brief Draw temporary picture while dragging edge.
219 * \par Function Description
220 * This function is used to draw the box while dragging one of its edge or
221 * angle. It erases the previous temporary box drawn before, and draws
222 * a new updated one. <B>w_x</B> and <B>w_y</B> are the new position of the mobile
223 * point, ie the mouse.
225 * The old values are inside the <B>w_current</B> pointed structure. Old
226 * width, height and left and top values are recomputed by the corresponding
227 * macros.
229 * \param [in] w_current The GschemToplevel object.
230 * \param [in] w_x Current x coordinate of pointer in world units.
231 * \param [in] w_y Current y coordinate of pointer in world units.
233 void o_picture_motion (GschemToplevel *w_current, int w_x, int w_y)
235 #if DEBUG
236 printf("o_picture_rubberbox called\n");
237 #endif
238 g_assert( w_current->inside_action != 0 );
240 /* erase the previous temporary box */
241 if (w_current->rubber_visible)
242 o_picture_invalidate_rubber (w_current);
245 * New values are fixed according to the <B>w_x</B> and <B>w_y</B> parameters.
246 * These are saved in <B>w_current</B> pointed structure as new temporary values.
247 * The new box is then drawn.
250 /* update the coords of the corner */
251 w_current->second_wx = w_x;
252 w_current->second_wy = w_y;
254 /* draw the new temporary box */
255 o_picture_invalidate_rubber (w_current);
256 w_current->rubber_visible = 1;
259 /*! \brief Draw picture from GschemToplevel object.
260 * \par Function Description
261 * This function draws the box from the variables in the GschemToplevel
262 * structure <B>*w_current</B>.
263 * One corner of the box is at (<B>w_current->first_wx</B>,
264 * <B>w_current->first_wy</B>) and the second corner is at
265 * (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>.
267 * \param [in] w_current The GschemToplevel object.
269 void o_picture_draw_rubber (GschemToplevel *w_current, EdaRenderer *renderer)
271 int left, top, width, height;
272 double wwidth = 0;
273 cairo_t *cr = eda_renderer_get_cairo_context (renderer);
274 GArray *color_map = eda_renderer_get_color_map (renderer);
275 int flags = eda_renderer_get_cairo_flags (renderer);
277 /* get the width/height and the upper left corner of the picture */
278 left = GET_PICTURE_LEFT (w_current);
279 top = GET_PICTURE_TOP (w_current);
280 width = GET_PICTURE_WIDTH (w_current);
281 height = GET_PICTURE_HEIGHT (w_current);
283 eda_cairo_box (cr, flags, wwidth, left, top - height, left + width, top);
284 eda_cairo_set_source_color (cr, SELECT_COLOR, color_map);
285 eda_cairo_stroke (cr, flags, TYPE_SOLID, END_NONE, wwidth, -1, -1);
288 /*! \brief Replace all selected pictures with a new picture
289 * \par Function Description
290 * Replaces all pictures in the current selection with a new image.
292 * \param [in] w_current The GschemToplevel object
293 * \param [in] filename The filename of the new picture
294 * \param [out] error The location to return error information.
295 * \return TRUE on success, FALSE on failure.
297 gboolean
298 o_picture_exchange (GschemToplevel *w_current,
299 const gchar *filename, GError **error)
301 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
302 GList *iter;
304 for (iter = geda_list_get_glist (toplevel->page_current->selection_list);
305 iter != NULL;
306 iter = g_list_next (iter)) {
308 OBJECT *object = (OBJECT *) iter->data;
309 g_assert (object != NULL);
311 if (object->type == OBJ_PICTURE) {
312 gboolean status;
314 /* Erase previous picture */
315 o_invalidate (w_current, object);
317 status = o_picture_set_from_file (toplevel, object, filename, error);
318 if (!status) return FALSE;
320 /* Draw new picture */
321 o_invalidate (w_current, object);
324 return TRUE;
327 /*! \brief Create dialog to exchange picture objects
328 * \par Function Description
329 * This function opens a file chooser and replaces all pictures of the selections
330 * with the new picture.
332 * \todo Maybe merge this dialog function with picture_selection_dialog()
334 void picture_change_filename_dialog (GschemToplevel *w_current)
336 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
337 gchar *filename;
338 gboolean result;
339 GError *error = NULL;
341 w_current->pfswindow = gtk_file_chooser_dialog_new (_("Select a picture file..."),
342 GTK_WINDOW(w_current->main_window),
343 GTK_FILE_CHOOSER_ACTION_OPEN,
344 GTK_STOCK_CANCEL,
345 GTK_RESPONSE_CANCEL,
346 GTK_STOCK_OPEN,
347 GTK_RESPONSE_ACCEPT,
348 NULL);
350 /* Set the alternative button order (ok, cancel, help) for other systems */
351 gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current->pfswindow),
352 GTK_RESPONSE_ACCEPT,
353 GTK_RESPONSE_CANCEL,
354 -1);
356 if (w_current->pixbuf_filename)
357 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current->pfswindow),
358 w_current->pixbuf_filename);
360 if (gtk_dialog_run (GTK_DIALOG (w_current->pfswindow)) == GTK_RESPONSE_ACCEPT) {
362 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current->pfswindow));
363 gtk_widget_destroy(w_current->pfswindow);
364 w_current->pfswindow=NULL;
366 /* Actually update the pictures */
367 result = o_picture_exchange (w_current, filename, &error);
369 if (!result) {
370 GtkWidget *dialog;
372 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
373 GTK_DIALOG_DESTROY_WITH_PARENT,
374 GTK_MESSAGE_ERROR,
375 GTK_BUTTONS_CLOSE,
376 _("Failed to replace pictures: %s"),
377 error->message);
378 /* Wait for any user response */
379 gtk_dialog_run (GTK_DIALOG (dialog));
381 g_error_free (error);
382 gtk_widget_destroy(dialog);
383 } else {
384 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
385 o_undo_savestate_old (w_current, UNDO_ALL, _("Replace Picture"));
387 g_free (filename);
390 if (w_current->pfswindow) {
391 gtk_widget_destroy(w_current->pfswindow);
392 w_current->pfswindow=NULL;
396 /*! \todo Finish function documentation!!!
397 * \brief
398 * \par Function Description
400 * \param [in] w_current The GschemToplevel object.
401 * \param [in] pixbuf
402 * \param [in] filename
404 void o_picture_set_pixbuf(GschemToplevel *w_current,
405 GdkPixbuf *pixbuf, char *filename)
408 /* need to put an error messages here */
409 if (pixbuf == NULL) {
410 fprintf(stderr, "error! picture in set pixbuf was NULL\n");
411 return;
414 if (w_current->current_pixbuf != NULL) {
415 g_object_unref(w_current->current_pixbuf);
416 w_current->current_pixbuf=NULL;
419 if (w_current->pixbuf_filename != NULL) {
420 g_free(w_current->pixbuf_filename);
421 w_current->pixbuf_filename=NULL;
424 w_current->current_pixbuf = pixbuf;
425 w_current->pixbuf_filename = (char *) g_strdup(filename);
427 w_current->pixbuf_wh_ratio = (double) gdk_pixbuf_get_width(pixbuf) /
428 gdk_pixbuf_get_height(pixbuf);
430 /* be sure to free this pixbuf somewhere */