gschem

o_picture.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * gschem - gEDA Schematic Capture
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details)
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 #include <config.h>
00021 #include <math.h>
00022 #include <stdio.h>
00023 
00024 #include "gschem.h"
00025 
00026 /* This works, but using one macro inside of other doesn't */
00027 #define GET_PICTURE_WIDTH(w)            \
00028   abs((w)->second_wx - (w)->first_wx) 
00029 #define GET_PICTURE_HEIGHT(w)                       \
00030   (w)->pixbuf_wh_ratio == 0 ? 0 : abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
00031 #define GET_PICTURE_LEFT(w)         \
00032   min((w)->first_wx, (w)->second_wx)
00033 #define GET_PICTURE_TOP(w)                      \
00034   (w)->first_wy > (w)->second_wy ? (w)->first_wy  :         \
00035   (w)->first_wy+abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
00036 
00056 void o_picture_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00057 {
00058   /* init first_w[x|y], second_w[x|y] to describe box */
00059   w_current->first_wx = w_current->second_wx = w_x;
00060   w_current->first_wy = w_current->second_wy = w_y;
00061 
00062   /* start to draw the box */
00063   o_picture_invalidate_rubber (w_current);
00064   w_current->rubber_visible = 1;
00065 }
00066 
00081 void o_picture_end(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00082 {
00083   TOPLEVEL *toplevel = w_current->toplevel;
00084   OBJECT *new_obj;
00085   int picture_width, picture_height;
00086   int picture_left, picture_top;
00087 
00088   g_assert( w_current->inside_action != 0 );
00089 
00090   /* erase the temporary picture */
00091   /* o_picture_draw_rubber(w_current); */
00092   w_current->rubber_visible = 0;
00093   
00094   picture_width  = GET_PICTURE_WIDTH (w_current);
00095   picture_height = GET_PICTURE_HEIGHT(w_current);
00096   picture_left   = GET_PICTURE_LEFT  (w_current);
00097   picture_top    = GET_PICTURE_TOP   (w_current);
00098 
00099   /* pictures with null width and height are not allowed */
00100   if ((picture_width == 0) && (picture_height == 0)) {
00101     /* cancel the object creation */
00102     return;
00103   }
00104 
00105   /* create the object */
00106   new_obj = o_picture_new(toplevel,
00107                           NULL, 0, w_current->pixbuf_filename,
00108                           OBJ_PICTURE,
00109                           picture_left, picture_top,
00110                           picture_left + picture_width,
00111                           picture_top - picture_height,
00112                           0, FALSE, FALSE);
00113   s_page_append (toplevel, toplevel->page_current, new_obj);
00114 
00115   /* Run %add-objects-hook */
00116   g_run_hook_object (w_current, "%add-objects-hook", new_obj);
00117 
00118   toplevel->page_current->CHANGED = 1;
00119   o_undo_savestate(w_current, UNDO_ALL);
00120 }
00121 
00126 void picture_selection_dialog (GSCHEM_TOPLEVEL *w_current)
00127 {
00128   TOPLEVEL *toplevel = w_current->toplevel;
00129   gchar *filename;
00130   GdkPixbuf *pixbuf;
00131   GError *error = NULL;
00132   
00133   w_current->pfswindow = gtk_file_chooser_dialog_new ("Select a picture file...",
00134                               GTK_WINDOW(w_current->main_window),
00135                               GTK_FILE_CHOOSER_ACTION_OPEN,
00136                               GTK_STOCK_CANCEL, 
00137                               GTK_RESPONSE_CANCEL,
00138                               GTK_STOCK_OPEN, 
00139                               GTK_RESPONSE_ACCEPT,
00140                               NULL);
00141   /* Set the alternative button order (ok, cancel, help) for other systems */
00142   gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current->pfswindow),
00143                       GTK_RESPONSE_ACCEPT,
00144                       GTK_RESPONSE_CANCEL,
00145                       -1);
00146 
00147   if (w_current->pixbuf_filename)
00148     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current->pfswindow), 
00149                   w_current->pixbuf_filename);
00150     
00151   if (gtk_dialog_run (GTK_DIALOG (w_current->pfswindow)) == GTK_RESPONSE_ACCEPT) {
00152 
00153     filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current->pfswindow));
00154     gtk_widget_destroy(w_current->pfswindow);
00155     w_current->pfswindow=NULL;
00156 
00157     pixbuf = gdk_pixbuf_new_from_file (filename, &error);
00158     
00159     if (!pixbuf) {
00160       GtkWidget *dialog;
00161       
00162       dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
00163                        GTK_DIALOG_DESTROY_WITH_PARENT,
00164                        GTK_MESSAGE_ERROR,
00165                        GTK_BUTTONS_CLOSE,
00166                        _("Failed to load picture: %s"),
00167                        error->message);
00168       /* Wait for any user response */
00169       gtk_dialog_run (GTK_DIALOG (dialog));
00170       
00171       g_error_free (error);
00172       gtk_widget_destroy(dialog);
00173     }
00174     else {
00175 #if DEBUG
00176       printf("Picture loaded succesfully.\n");
00177 #endif
00178       
00179       o_invalidate_rubber(w_current);
00180       i_update_middle_button(w_current, i_callback_add_picture, _("Picture"));
00181       w_current->inside_action = 0;
00182       
00183       o_picture_set_pixbuf(w_current, pixbuf, filename);
00184     
00185       toplevel->page_current->CHANGED=1;
00186       i_set_state(w_current, DRAWPICTURE);
00187     }
00188     g_free (filename);
00189   }
00190 
00191   i_update_toolbar(w_current);
00192   if (w_current->pfswindow) {
00193     gtk_widget_destroy(w_current->pfswindow);
00194     w_current->pfswindow=NULL;
00195   }
00196 }
00197 
00205 void o_picture_invalidate_rubber (GSCHEM_TOPLEVEL *w_current)
00206 {
00207   int left, top, width, height;
00208 
00209   WORLDtoSCREEN (w_current,
00210                  GET_PICTURE_LEFT(w_current), GET_PICTURE_TOP(w_current),
00211                  &left, &top);
00212   width = SCREENabs (w_current, GET_PICTURE_WIDTH (w_current));
00213   height = SCREENabs (w_current, GET_PICTURE_HEIGHT(w_current));
00214 
00215   o_invalidate_rect (w_current, left, top, left + width, top);
00216   o_invalidate_rect (w_current, left, top, left, top + height);
00217   o_invalidate_rect (w_current, left + width, top, left + width, top + height);
00218   o_invalidate_rect (w_current, left, top + height, left + width, top + height);
00219 }
00220 
00236 void o_picture_motion (GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00237 {
00238 #if DEBUG
00239   printf("o_picture_rubberbox called\n");
00240 #endif
00241   g_assert( w_current->inside_action != 0 );
00242 
00243   /* erase the previous temporary box */
00244   if (w_current->rubber_visible)
00245     o_picture_invalidate_rubber (w_current);
00246 
00247   /*
00248    * New values are fixed according to the <B>w_x</B> and <B>w_y</B> parameters. 
00249    * These are saved in <B>w_current</B> pointed structure as new temporary values. 
00250    * The new box is then drawn.
00251    */
00252 
00253   /* update the coords of the corner */
00254   w_current->second_wx = w_x;
00255   w_current->second_wy = w_y;
00256 
00257   /* draw the new temporary box */
00258   o_picture_invalidate_rubber (w_current);
00259   w_current->rubber_visible = 1;
00260 }
00261 
00272 void o_picture_draw_rubber (GSCHEM_TOPLEVEL *w_current)
00273 {
00274   int left, top, width, height;
00275 
00276   /* get the width/height and the upper left corner of the picture */
00277   left =   GET_PICTURE_LEFT (w_current);
00278   top =    GET_PICTURE_TOP (w_current);
00279   width =  GET_PICTURE_WIDTH (w_current);
00280   height = GET_PICTURE_HEIGHT (w_current);
00281 
00282   gschem_cairo_box (w_current, 0, left, top - height, left + width, top);
00283 
00284   gschem_cairo_set_source_color (w_current,
00285                                  x_color_lookup_dark (SELECT_COLOR));
00286   gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00287 }
00288 
00302 void o_picture_draw (GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
00303 {
00304   int s_upper_x, s_upper_y, s_lower_x, s_lower_y;
00305   GdkPixbuf *pixbuf;
00306 
00307   g_return_if_fail (w_current != NULL);
00308   g_return_if_fail (w_current->toplevel != NULL);
00309   g_return_if_fail (o_current != NULL);
00310   g_return_if_fail (o_current->picture != NULL);
00311 
00312   pixbuf = o_picture_get_pixbuf (w_current->toplevel, o_current);
00313 
00314   /* If the image failed to load, get the fallback image. */
00315   if (pixbuf == NULL) pixbuf = o_picture_get_fallback_pixbuf (w_current->toplevel);
00316   /* If the fallback image failed to load, draw a box with a cross in it. */
00317   if (pixbuf == NULL) {
00318     int line_width = (w_current->toplevel->line_style == THICK) ? LINE_WIDTH : 2;
00319     gschem_cairo_set_source_color (w_current,
00320                                    o_drawing_color (w_current, o_current));
00321     gschem_cairo_box (w_current, line_width,
00322                       o_current->picture->lower_x, o_current->picture->lower_y,
00323                       o_current->picture->upper_x, o_current->picture->upper_y);
00324     gschem_cairo_line (w_current, END_ROUND, line_width,
00325                       o_current->picture->lower_x, o_current->picture->lower_y,
00326                       o_current->picture->upper_x, o_current->picture->upper_y);
00327     gschem_cairo_line (w_current, END_ROUND, line_width,
00328                       o_current->picture->lower_x, o_current->picture->upper_y,
00329                       o_current->picture->upper_x, o_current->picture->lower_y);
00330     gschem_cairo_stroke (w_current, TYPE_SOLID, END_ROUND, line_width, -1, -1);
00331     return;
00332   }
00333 
00334   g_assert (GDK_IS_PIXBUF (pixbuf));
00335 
00336   WORLDtoSCREEN (w_current, o_current->picture->upper_x,
00337                             o_current->picture->upper_y, &s_upper_x, &s_upper_y);
00338   WORLDtoSCREEN (w_current, o_current->picture->lower_x,
00339                             o_current->picture->lower_y, &s_lower_x, &s_lower_y);
00340 
00341   cairo_save (w_current->cr);
00342 
00343   int swap_wh = (o_current->picture->angle == 90 || o_current->picture->angle == 270);
00344   float orig_width  = swap_wh ? gdk_pixbuf_get_height (pixbuf) :
00345                                 gdk_pixbuf_get_width  (pixbuf);
00346   float orig_height = swap_wh ? gdk_pixbuf_get_width  (pixbuf) :
00347                                 gdk_pixbuf_get_height (pixbuf);
00348 
00349   cairo_translate (w_current->cr, s_upper_x, s_upper_y);
00350   cairo_scale (w_current->cr,
00351     (float)SCREENabs (w_current, abs (o_current->picture->upper_x -
00352                                       o_current->picture->lower_x)) / orig_width,
00353     (float)SCREENabs (w_current, abs (o_current->picture->upper_y -
00354                                       o_current->picture->lower_y)) / orig_height);
00355 
00356   /* Evil magic translates picture origin to the right position for a given rotation */
00357   switch (o_current->picture->angle) {
00358     case 0:                                                               break;
00359     case 90:   cairo_translate (w_current->cr, 0,          orig_height);  break;
00360     case 180:  cairo_translate (w_current->cr, orig_width, orig_height);  break;
00361     case 270:  cairo_translate (w_current->cr, orig_width, 0          );  break;
00362   }
00363   cairo_rotate (w_current->cr, -o_current->picture->angle * M_PI / 180.);
00364   if (o_current->picture->mirrored) {
00365     cairo_translate (w_current->cr, gdk_pixbuf_get_width (pixbuf), 0);
00366     cairo_scale (w_current->cr, -1, 1);
00367   }
00368 
00369   gdk_cairo_set_source_pixbuf (w_current->cr,
00370                                pixbuf, 0,0);
00371   cairo_rectangle (w_current->cr, 0, 0,
00372                    gdk_pixbuf_get_width (pixbuf),
00373                    gdk_pixbuf_get_height (pixbuf));
00374 
00375   cairo_clip (w_current->cr);
00376   cairo_paint (w_current->cr);
00377   cairo_restore (w_current->cr);
00378 
00379   /* Grip specific stuff */
00380   if (o_current->selected && w_current->draw_grips) {
00381     o_picture_draw_grips (w_current, o_current);
00382   }
00383 
00384   g_object_unref (pixbuf);
00385 }
00386 
00395 void o_picture_draw_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
00396 {
00397   if (w_current->draw_grips == FALSE)
00398     return;
00399 
00400   /* grip on upper left corner (whichone = PICTURE_UPPER_LEFT) */
00401   o_grips_draw (w_current, o_current->picture->upper_x,
00402                            o_current->picture->upper_y);
00403 
00404   /* grip on upper right corner (whichone = PICTURE_UPPER_RIGHT) */
00405   o_grips_draw (w_current, o_current->picture->lower_x,
00406                            o_current->picture->upper_y);
00407 
00408   /* grip on lower left corner (whichone = PICTURE_LOWER_LEFT) */
00409   o_grips_draw (w_current, o_current->picture->upper_x,
00410                            o_current->picture->lower_y);
00411 
00412   /* grip on lower right corner (whichone = PICTURE_LOWER_RIGHT) */
00413   o_grips_draw (w_current, o_current->picture->lower_x,
00414                            o_current->picture->lower_y);
00415 
00416   /* Box surrounding the picture */
00417   gschem_cairo_box (w_current, 0,
00418                     o_current->picture->upper_x, o_current->picture->upper_y,
00419                     o_current->picture->lower_x, o_current->picture->lower_y);
00420 
00421   gschem_cairo_set_source_color (w_current,
00422                                  x_color_lookup_dark (SELECT_COLOR));
00423   gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00424 }
00425 
00426 
00440 void o_picture_draw_place (GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *o_current)
00441 {
00442   if (o_current->picture == NULL) {
00443     return;
00444   }
00445 
00446   gschem_cairo_box (w_current, 0, o_current->picture->upper_x + dx,
00447                                   o_current->picture->upper_y + dy,
00448                                   o_current->picture->lower_x + dx,
00449                                   o_current->picture->lower_y + dy);
00450 
00451   gschem_cairo_set_source_color (w_current,
00452                                  x_color_lookup_dark (o_current->color));
00453   gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00454 }
00455 
00465 gboolean
00466 o_picture_exchange (GSCHEM_TOPLEVEL *w_current,
00467                     const gchar *filename, GError **error)
00468 {
00469   TOPLEVEL *toplevel = w_current->toplevel;
00470   GList *iter;
00471 
00472   for (iter = geda_list_get_glist (toplevel->page_current->selection_list);
00473        iter != NULL;
00474        iter = g_list_next (iter)) {
00475 
00476     OBJECT *object = (OBJECT *) iter->data;
00477     g_assert (object != NULL);
00478 
00479     if (object->type == OBJ_PICTURE) {
00480       gboolean status;
00481 
00482       /* Erase previous picture */
00483       o_invalidate (w_current, object);
00484 
00485       status = o_picture_set_from_file (toplevel, object, filename, error);
00486       if (!status) return FALSE;
00487 
00488       /* Draw new picture */
00489       o_invalidate (w_current, object);
00490     }
00491   }
00492   return TRUE;
00493 }
00494 
00502 void picture_change_filename_dialog (GSCHEM_TOPLEVEL *w_current)
00503 {
00504   TOPLEVEL *toplevel = w_current->toplevel;
00505   gchar *filename;
00506   gboolean result;
00507   GError *error = NULL;
00508   
00509   w_current->pfswindow = gtk_file_chooser_dialog_new ("Select a picture file...",
00510                               GTK_WINDOW(w_current->main_window),
00511                               GTK_FILE_CHOOSER_ACTION_OPEN,
00512                               GTK_STOCK_CANCEL, 
00513                               GTK_RESPONSE_CANCEL,
00514                               GTK_STOCK_OPEN, 
00515                               GTK_RESPONSE_ACCEPT,
00516                               NULL);
00517 
00518   /* Set the alternative button order (ok, cancel, help) for other systems */
00519   gtk_dialog_set_alternative_button_order(GTK_DIALOG(w_current->pfswindow),
00520                       GTK_RESPONSE_ACCEPT,
00521                       GTK_RESPONSE_CANCEL,
00522                       -1);
00523 
00524   if (w_current->pixbuf_filename)
00525     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w_current->pfswindow), 
00526                   w_current->pixbuf_filename);
00527     
00528   if (gtk_dialog_run (GTK_DIALOG (w_current->pfswindow)) == GTK_RESPONSE_ACCEPT) {
00529 
00530     filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w_current->pfswindow));
00531     gtk_widget_destroy(w_current->pfswindow);
00532     w_current->pfswindow=NULL;
00533 
00534     /* Actually update the pictures */
00535     result = o_picture_exchange (w_current, filename, &error);
00536 
00537     if (!result) {
00538       GtkWidget *dialog;
00539 
00540       dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
00541                        GTK_DIALOG_DESTROY_WITH_PARENT,
00542                        GTK_MESSAGE_ERROR,
00543                        GTK_BUTTONS_CLOSE,
00544                        _("Failed to replace pictures: %s"),
00545                        error->message);
00546       /* Wait for any user response */
00547       gtk_dialog_run (GTK_DIALOG (dialog));
00548 
00549       g_error_free (error);
00550       gtk_widget_destroy(dialog);
00551     } else {
00552       toplevel->page_current->CHANGED=1;
00553     }
00554     g_free (filename);
00555   }
00556 
00557   i_update_toolbar(w_current);
00558   if (w_current->pfswindow) {
00559     gtk_widget_destroy(w_current->pfswindow);
00560     w_current->pfswindow=NULL;
00561   }
00562 }
00563 
00572 void o_picture_set_pixbuf(GSCHEM_TOPLEVEL *w_current,
00573                           GdkPixbuf *pixbuf, char *filename)
00574 {
00575 
00576   /* need to put an error messages here */
00577   if (pixbuf == NULL)  {
00578     fprintf(stderr, "error! picture in set pixbuf was NULL\n");
00579     return;
00580   }
00581 
00582   if (w_current->current_pixbuf != NULL) {
00583     g_object_unref(w_current->current_pixbuf);
00584     w_current->current_pixbuf=NULL;
00585   }
00586 
00587   if (w_current->pixbuf_filename != NULL) {
00588     g_free(w_current->pixbuf_filename);
00589     w_current->pixbuf_filename=NULL;
00590   }
00591 
00592   w_current->current_pixbuf = pixbuf;
00593   w_current->pixbuf_filename = (char *) g_strdup(filename);
00594 
00595   w_current->pixbuf_wh_ratio = (double) gdk_pixbuf_get_width(pixbuf) /
00596                                         gdk_pixbuf_get_height(pixbuf);
00597 
00598   /* be sure to free this pixbuf somewhere */
00599 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines