1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
26 /* This works, but using one macro inside of other doesn't */
27 #define GET_PICTURE_WIDTH(w) \
28 abs((w)->second_wx - (w)->first_wx)
29 #define GET_PICTURE_HEIGHT(w) \
30 (w)->pixbuf_wh_ratio == 0 ? 0 : abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
31 #define GET_PICTURE_LEFT(w) \
32 min((w)->first_wx, (w)->second_wx)
33 #define GET_PICTURE_TOP(w) \
34 (w)->first_wy > (w)->second_wy ? (w)->first_wy : \
35 (w)->first_wy+abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
37 struct grip_foreach_context
{
38 GSCHEM_TOPLEVEL
*w_current
;
39 enum { PICTURE_GRIP_DRAW
, PICTURE_GRIP_ERASE
} action
;
46 /*! \brief Start process to input a new picture.
47 * \par Function Description
48 * This function starts the process to input a new picture. Parameters
49 * for this picture are put into/extracted from the <B>w_current</B> toplevel
51 * <B>w_x</B> and <B>w_y</B> are current coordinates of the pointer in world
54 * The first step is to input one corner of the picture. This corner is
55 * (<B>w_x</B>,<B>w_y</B>) snapped to the grid and saved in
56 * <B>w_current->first_wx</B> and <B>w_current->first_wy</B>.
58 * The other corner will be saved in (<B>w_current->second_wx</B>,
59 * <B>w_current->second_wy</B>).
61 * \param [in] w_current The GSCHEM_TOPLEVEL object.
62 * \param [in] w_x Current x coordinate of pointer in world units.
63 * \param [in] w_y Current y coordinate of pointer in world units.
65 void o_picture_start(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
67 /* init first_w[x|y], second_w[x|y] to describe box */
68 w_current
->first_wx
= w_current
->second_wx
= w_x
;
69 w_current
->first_wy
= w_current
->second_wy
= w_y
;
71 /* start to draw the box */
72 o_picture_rubberbox_xor(w_current
);
73 w_current
->rubber_visible
= 1;
76 /*! \brief End the input of a circle.
77 * \par Function Description
78 * This function ends the input of the second corner of a picture.
79 * The picture is defined by (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>
80 * and (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>.
82 * The temporary picture frame is erased ; a new picture object is allocated,
83 * initialized and linked to the object list ; The object is finally
84 * drawn on the current sheet.
86 * \param [in] w_current The GSCHEM_TOPLEVEL object.
87 * \param [in] w_x (unused)
88 * \param [in] w_y (unused)
90 void o_picture_end(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
92 TOPLEVEL
*toplevel
= w_current
->toplevel
;
94 int picture_width
, picture_height
;
95 int picture_left
, picture_top
;
97 g_assert( w_current
->inside_action
!= 0 );
99 /* erase the temporary picture */
100 o_picture_rubberbox_xor(w_current
);
101 w_current
->rubber_visible
= 0;
103 picture_width
= GET_PICTURE_WIDTH (w_current
);
104 picture_height
= GET_PICTURE_HEIGHT(w_current
);
105 picture_left
= GET_PICTURE_LEFT (w_current
);
106 picture_top
= GET_PICTURE_TOP (w_current
);
108 /* pictures with null width and height are not allowed */
109 if ((picture_width
== 0) && (picture_height
== 0)) {
110 /* cancel the object creation */
114 /* create the object */
115 new_obj
= o_picture_new(toplevel
, w_current
->current_pixbuf
,
116 NULL
, 0, w_current
->pixbuf_filename
,
117 w_current
->pixbuf_wh_ratio
, OBJ_PICTURE
,
118 picture_left
, picture_top
,
119 picture_left
+ picture_width
,
120 picture_top
- picture_height
,
122 s_page_append(toplevel
->page_current
, new_obj
);
125 o_redraw_single(w_current
, toplevel
->page_current
->object_tail
);
127 toplevel
->page_current
->CHANGED
= 1;
128 o_undo_savestate(w_current
, UNDO_ALL
);
131 /*! \brief Creates the add image dialog
132 * \par Function Description
133 * This function creates the add image dialog and loads the selected picture.
135 void picture_selection_dialog (GSCHEM_TOPLEVEL
*w_current
)
137 TOPLEVEL
*toplevel
= w_current
->toplevel
;
140 GError
*error
= NULL
;
142 w_current
->pfswindow
= gtk_file_chooser_dialog_new ("Select a picture file...",
143 GTK_WINDOW(w_current
->main_window
),
144 GTK_FILE_CHOOSER_ACTION_OPEN
,
150 /* Set the alternative button order (ok, cancel, help) for other systems */
151 gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current
->pfswindow
),
156 if (w_current
->pixbuf_filename
)
157 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current
->pfswindow
),
158 w_current
->pixbuf_filename
);
160 if (gtk_dialog_run (GTK_DIALOG (w_current
->pfswindow
)) == GTK_RESPONSE_ACCEPT
) {
162 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current
->pfswindow
));
163 gtk_widget_destroy(w_current
->pfswindow
);
164 w_current
->pfswindow
=NULL
;
166 pixbuf
= gdk_pixbuf_new_from_file (filename
, &error
);
171 dialog
= gtk_message_dialog_new (GTK_WINDOW (w_current
->main_window
),
172 GTK_DIALOG_DESTROY_WITH_PARENT
,
175 _("Failed to load picture: %s"),
177 /* Wait for any user response */
178 gtk_dialog_run (GTK_DIALOG (dialog
));
180 g_error_free (error
);
181 gtk_widget_destroy(dialog
);
185 printf("Picture loaded successfully.\n");
188 o_erase_rubber(w_current
);
189 i_update_middle_button(w_current
, i_callback_add_picture
, _("Picture"));
190 w_current
->inside_action
= 0;
192 o_picture_set_pixbuf(w_current
, pixbuf
, filename
);
194 toplevel
->page_current
->CHANGED
=1;
195 i_set_state(w_current
, DRAWPICTURE
);
200 i_update_toolbar(w_current
);
201 if (w_current
->pfswindow
) {
202 gtk_widget_destroy(w_current
->pfswindow
);
203 w_current
->pfswindow
=NULL
;
207 /*! \todo Finish function documentation!!!
209 * \par Function Description
212 * used in button cancel code in x_events.c
214 void o_picture_eraserubber(GSCHEM_TOPLEVEL
*w_current
)
217 printf("o_picture_eraserubber called\n");
219 o_picture_rubberbox_xor(w_current
);
222 /*! \brief Draw temporary picture while dragging edge.
223 * \par Function Description
224 * This function is used to draw the box while dragging one of its edge or
225 * angle. It erases the previous temporary box drawn before, and draws
226 * a new updated one. <B>w_x</B> and <B>w_y</B> are the new position of the mobile
227 * point, ie the mouse.
229 * The old values are inside the <B>w_current</B> pointed structure. Old
230 * width, height and left and top values are recomputed by the corresponding
231 * macros. The box is then erased by performing a xor-drawing over the box.
233 * \param [in] w_current The GSCHEM_TOPLEVEL object.
234 * \param [in] w_x Current x coordinate of pointer in world units.
235 * \param [in] w_y Current y coordinate of pointer in world units.
237 void o_picture_rubberbox(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
240 printf("o_picture_rubberbox called\n");
242 g_assert( w_current
->inside_action
!= 0 );
244 /* erase the previous temporary box */
245 if (w_current
->rubber_visible
)
246 o_picture_rubberbox_xor(w_current
);
249 * New values are fixed according to the <B>w_x</B> and <B>w_y</B> parameters.
250 * These are saved in <B>w_current</B> pointed structure as new temporary values.
251 * The new box is then drawn.
254 /* update the coords of the corner */
255 w_current
->second_wx
= w_x
;
256 w_current
->second_wy
= w_y
;
258 /* draw the new temporary box */
259 o_picture_rubberbox_xor(w_current
);
260 w_current
->rubber_visible
= 1;
263 /*! \brief Draw picture from GSCHEM_TOPLEVEL object.
264 * \par Function Description
265 * This function draws the box from the variables in the GSCHEM_TOPLEVEL
266 * structure <B>*w_current</B>.
267 * One corner of the box is at (<B>w_current->first_wx</B>,
268 * <B>w_current->first_wy</B>) and the second corner is at
269 * (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>.
271 * The box is drawn with a xor-function over the current sheet with the
274 * \param [in] w_current The GSCHEM_TOPLEVEL object.
276 void o_picture_rubberbox_xor(GSCHEM_TOPLEVEL
*w_current
)
278 TOPLEVEL
*toplevel
= w_current
->toplevel
;
279 PAGE
*page
= toplevel
->page_current
;
280 int left
, top
, width
, height
;
282 /* get the width/height and the upper left corner of the picture */
284 GET_PICTURE_LEFT(w_current
), GET_PICTURE_TOP(w_current
),
286 width
= SCREENabs(page
, GET_PICTURE_WIDTH (w_current
));
287 height
= SCREENabs(page
, GET_PICTURE_HEIGHT(w_current
));
290 printf("o_picture_rubberbox_xor called:\n");
291 printf("pixbuf wh ratio: %i\n", w_current
->pixbuf_wh_ratio
);
292 printf("first: %i, %i\n", w_current
->first_wx
, w_current
->first_wy
);
293 printf("second: %i, %i\n", w_current
->second_wx
, w_current
->second_wy
);
294 printf("Left: %i\nTop: %i\nWidth: %i\nHeight: %i\n",
295 picture_left
, picture_top
, picture_width
, picture_height
);
297 /* draw the picture from the previous variables */
298 gdk_gc_set_foreground(w_current
->xor_gc
,
299 x_get_darkcolor(w_current
->select_color
));
300 gdk_gc_set_line_attributes(w_current
->xor_gc
, 0,
301 GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
,
303 gdk_draw_rectangle(w_current
->backingstore
, w_current
->xor_gc
,
304 FALSE
, left
, top
, width
, height
);
305 o_invalidate_rect(w_current
,
306 left
, top
, left
+ width
, top
+ height
);
309 /*! \brief Draw a picture on the screen.
310 * \par Function Description
311 * This function is used to draw a picture on screen. The picture is
312 * described in the OBJECT which is referred by <B>o_current</B>. The picture
313 * is displayed according to the current state, described in the
314 * GSCHEM_TOPLEVEL object pointed by <B>w_current</B>.
316 * It first checks if the OBJECT pointed is valid or not. If not it
317 * returns and do not output anything. That should never happen though.
319 * \param [in] w_current The GSCHEM_TOPLEVEL object.
320 * \param [in] o_current Picture OBJECT to draw.
322 void o_picture_draw(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
324 TOPLEVEL
*toplevel
= w_current
->toplevel
;
325 PAGE
*page
= toplevel
->page_current
;
326 int wleft
, wright
, wtop
, wbottom
; /* world bounds */
327 int s_upper_x
, s_upper_y
, s_lower_x
, s_lower_y
;
329 if (o_current
->picture
== NULL
) {
333 /* Get read to check for visibility of this line by using it's
336 world_get_single_object_bounds(o_current
, &wleft
, &wtop
, &wright
, &wbottom
);
338 if (!visible(page
, wleft
, wtop
, wright
, wbottom
)) {
342 WORLDtoSCREEN(page
, wleft
, wbottom
, &s_upper_x
, &s_upper_y
);
343 WORLDtoSCREEN(page
, wright
, wtop
, &s_lower_x
, &s_lower_y
);
346 printf("drawing picture\n\n");
348 printf("drawing picture: %d %d %d %d\n",
349 s_upper_x
, s_upper_y
,
350 s_upper_x
+ abs(s_lower_x
- s_upper_x
),
351 s_upper_y
+ abs(s_lower_y
- s_upper_y
));
355 * First, the picture is drawn.
356 * Finally the function takes care of the grips.
359 /* If it's not drawing using the background color then draw the image */
360 if (toplevel
->override_color
!= toplevel
->background_color
) {
363 pixbuf
= o_picture_make_drawable(o_current
,
364 abs(s_lower_x
- s_upper_x
),
365 abs(s_lower_y
- s_upper_y
));
366 if (pixbuf
== NULL
) {
367 s_log_message(_("Can't draw picture - not enough memory?\n"));
371 if (toplevel
->DONT_REDRAW
== 0) {
372 gdk_draw_pixbuf(w_current
->backingstore
, w_current
->gc
,
374 0, 0, s_upper_x
, s_upper_y
,
375 -1, -1, GDK_RGB_DITHER_NONE
, 0, 0);
379 if (toplevel
->DONT_REDRAW
== 0) {
380 /* Erase the picture, drawing a rectangle with the background color */
381 gdk_gc_set_foreground(w_current
->gc
,
382 x_get_color(toplevel
->background_color
));
383 gdk_draw_rectangle(w_current
->backingstore
, w_current
->gc
, TRUE
,
384 s_upper_x
, s_upper_y
,
385 abs(s_lower_x
-s_upper_x
),
386 abs(s_lower_y
- s_upper_y
));
390 /* Grip specific stuff */
391 if ((o_current
->draw_grips
== TRUE
) && (w_current
->draw_grips
== TRUE
)) {
392 if (toplevel
->DONT_REDRAW
== 0) {
395 if (!o_current
->selected
) {
396 /* Deselected: use the background color. */
397 color
= x_get_color(toplevel
->background_color
);
398 } else if (toplevel
->override_color
!= -1 ) {
399 /* override : use the override_color instead */
400 color
= x_get_color(toplevel
->override_color
);
402 /* use the normal selection color */
403 color
= x_get_color(w_current
->select_color
);
406 gdk_gc_set_foreground(w_current
->gc
, color
);
407 gdk_draw_rectangle(w_current
->backingstore
, w_current
->gc
, FALSE
,
408 s_upper_x
, s_upper_y
,
409 abs(s_lower_x
-s_upper_x
),
410 abs(s_lower_y
- s_upper_y
));
415 /*! \brief Draw a picture described by OBJECT with translation
416 * \par Function Description
417 * This function draws the picture object described by <B>*o_current</B>
418 * translated by the vector (<B>dx</B>,<B>dy</B>) with an xor-function over
420 * The translation vector is in world unit.
422 * The picture is displayed with the color of the object.
424 * \param [in] w_current The GSCHEM_TOPLEVEL object.
425 * \param [in] dx Delta x coordinate for picture.
426 * \param [in] dy Delta y coordinate for picture.
427 * \param [in] o_current Picture OBJECT to draw.
429 void o_picture_draw_xor(GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*o_current
)
431 TOPLEVEL
*toplevel
= w_current
->toplevel
;
432 PAGE
*page
= toplevel
->page_current
;
433 int screen_x1
, screen_y1
;
434 int screen_x2
, screen_y2
;
436 int wright
, world_y2
;
440 printf("o_picture_draw_xor called.\n");
442 if (o_current
->picture
== NULL
) {
446 world_get_single_object_bounds(o_current
, &wleft
, &world_y1
, &wright
, &world_y2
);
447 WORLDtoSCREEN(page
, wleft
+ dx
, world_y2
+ dy
, &screen_x1
, &screen_y1
);
448 WORLDtoSCREEN(page
, wright
+ dx
, world_y1
+ dy
, &screen_x2
, &screen_y2
);
450 if (o_current
->saved_color
!= -1) {
451 color
= o_current
->saved_color
;
453 color
= o_current
->color
;
456 gdk_gc_set_foreground(w_current
->outline_xor_gc
,
457 x_get_darkcolor(color
));
458 gdk_draw_rectangle(w_current
->backingstore
,
459 w_current
->outline_xor_gc
, FALSE
,
462 abs(screen_x2
- screen_x1
),
463 abs(screen_y2
- screen_y1
));
466 /*! \brief Replace all selected pictures with a new picture
467 * \par Function Description
468 * This function replaces all pictures in the current selection with a
471 * \param [in] w_current The GSCHEM_TOPLEVEL object
472 * \param [in] pixbuf New GdkPixbuf object
473 * \param [in] filename The filename of the new picture
476 void o_picture_exchange (GSCHEM_TOPLEVEL
*w_current
, GdkPixbuf
*pixbuf
,
477 const gchar
*filename
)
479 TOPLEVEL
*toplevel
= w_current
->toplevel
;
482 list
= geda_list_get_glist( toplevel
->page_current
->selection_list
);
483 while (list
!= NULL
) {
487 if (object
== NULL
) {
488 fprintf(stderr
, _("ERROR: NULL object!\n"));
491 if (!object
->attached_to
) {
492 /* It's selected. Then change picture if it's a picture */
493 if (object
->type
== OBJ_PICTURE
) {
494 /* Erase previous picture */
495 o_erase_single(w_current
, object
);
497 o_picture_set(object
, pixbuf
, filename
);
499 /* Draw new picture */
500 o_picture_draw(w_current
, object
);
503 list
= g_list_next(list
);
507 /*! \brief Create dialog to exchange picture objects
508 * \par Function Description
509 * This function opens a file chooser and replaces all pictures of the selections
510 * with the new picture.
512 * \todo Maybe merge this dialog function with picture_selection_dialog()
514 void picture_change_filename_dialog (GSCHEM_TOPLEVEL
*w_current
)
516 TOPLEVEL
*toplevel
= w_current
->toplevel
;
519 GError
*error
= NULL
;
521 w_current
->pfswindow
= gtk_file_chooser_dialog_new ("Select a picture file...",
522 GTK_WINDOW(w_current
->main_window
),
523 GTK_FILE_CHOOSER_ACTION_OPEN
,
530 /* Set the alternative button order (ok, cancel, help) for other systems */
531 gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current
->pfswindow
),
536 if (w_current
->pixbuf_filename
)
537 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current
->pfswindow
),
538 w_current
->pixbuf_filename
);
540 if (gtk_dialog_run (GTK_DIALOG (w_current
->pfswindow
)) == GTK_RESPONSE_ACCEPT
) {
542 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current
->pfswindow
));
543 gtk_widget_destroy(w_current
->pfswindow
);
544 w_current
->pfswindow
=NULL
;
546 pixbuf
= gdk_pixbuf_new_from_file (filename
, &error
);
551 dialog
= gtk_message_dialog_new (GTK_WINDOW (w_current
->main_window
),
552 GTK_DIALOG_DESTROY_WITH_PARENT
,
555 _("Failed to load picture: %s"),
557 /* Wait for any user response */
558 gtk_dialog_run (GTK_DIALOG (dialog
));
560 g_error_free (error
);
561 gtk_widget_destroy(dialog
);
565 printf("Picture loaded successfully.\n");
568 o_erase_rubber(w_current
);
569 w_current
->inside_action
= 0;
571 /* \FIXME Should we set the pixbuf buffer in GSCHEM_TOPLEVEL to store
572 the current pixbuf? (Werner)
573 o_picture_set_pixbuf(w_current, pixbuf, filename); */
575 o_picture_exchange(w_current
, pixbuf
, filename
);
577 g_object_unref(pixbuf
);
578 toplevel
->page_current
->CHANGED
=1;
583 i_update_toolbar(w_current
);
584 if (w_current
->pfswindow
) {
585 gtk_widget_destroy(w_current
->pfswindow
);
586 w_current
->pfswindow
=NULL
;
590 /*! \todo Finish function documentation!!!
592 * \par Function Description
594 * \param [in] w_current The GSCHEM_TOPLEVEL object.
596 * \param [in] filename
598 void o_picture_set_pixbuf(GSCHEM_TOPLEVEL
*w_current
,
599 GdkPixbuf
*pixbuf
, char *filename
)
602 /* need to put an error messages here */
603 if (pixbuf
== NULL
) {
604 fprintf(stderr
, "error! picture in set pixbuf was NULL\n");
608 if (w_current
->current_pixbuf
!= NULL
) {
609 g_object_unref(w_current
->current_pixbuf
);
610 w_current
->current_pixbuf
=NULL
;
613 if (w_current
->pixbuf_filename
!= NULL
) {
614 g_free(w_current
->pixbuf_filename
);
615 w_current
->pixbuf_filename
=NULL
;
618 w_current
->current_pixbuf
= pixbuf
;
619 w_current
->pixbuf_filename
= (char *) g_strdup(filename
);
621 w_current
->pixbuf_wh_ratio
= (double) gdk_pixbuf_get_width(pixbuf
) /
622 gdk_pixbuf_get_height(pixbuf
);
624 /* be sure to free this pixbuf somewhere */