/*! \brief Invalidate bounding box or outline for OBJECT placement * * \par Function Description * This function invalidates the bounding box where objects would be * drawn by o_place_draw_rubber() * * The function applies manhatten mode constraints to the coordinates * before drawing if the CONTROL key is recording as being pressed in * the w_current structure. * * The "drawing" parameter is used to indicate if this drawing should * immediately use the selected feedback mode and positioning constraints. * * With drawing=TRUE, the selected conditions are used immediately, * otherwise the conditions from the last drawing operation are used, * saving the new state for next time. * * This function should be called with drawing=TRUE when starting a * rubberbanding operation and when otherwise refreshing the rubberbanded * outline (e.g. after a screen redraw). For any undraw operation, should * be called with drawing=FALSE, ensuring that the undraw XOR matches the * mode and constraints of the corresponding "draw" operation. * * If any mode / constraint changes are made between a undraw, redraw XOR * pair, the latter (draw) operation must be called with drawing=TRUE. If * no mode / constraint changes were made between the pair, it is not * harmful to call the draw operation with "drawing=FALSE". * * \param [in] w_current GschemToplevel which we're drawing for. * \param [in] drawing Set to FALSE for undraw operations to ensure * matching conditions to a previous draw operation. */ void o_place_invalidate_rubber (GschemToplevel *w_current, int drawing) { int diff_x, diff_y; int left, top, bottom, right; g_return_if_fail (w_current != NULL); GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (page_view != NULL); PAGE *page = gschem_page_view_get_page (page_view); g_return_if_fail (page != NULL); g_return_if_fail (page->place_list != NULL); /* If drawing is true, then don't worry about the previous drawing * method and movement constraints, use with the current settings */ if (drawing) { /* Ensure we set this to flag there is "something" supposed to be * drawn when the invalidate call below causes an expose event. */ w_current->last_drawb_mode = w_current->actionfeedback_mode; w_current->drawbounding_action_mode = (w_current->CONTROLKEY && ! ((w_current->event_state == PASTEMODE) || (w_current->event_state == COMPMODE) || (w_current->event_state == TEXTMODE))) ? CONSTRAINED : FREE; } /* Calculate delta of X-Y positions from buffer's origin */ diff_x = w_current->second_wx - w_current->first_wx; diff_y = w_current->second_wy - w_current->first_wy; /* Adjust the coordinates according to the movement constraints */ /* Need to update the w_current->{first,second}_w{x,y} coords even * though we're only invalidating because the move rubberband code * (which may execute right after this function) expects these * coordinates to be correct. */ if (w_current->drawbounding_action_mode == CONSTRAINED) { if (abs (diff_x) >= abs (diff_y)) { w_current->second_wy = w_current->first_wy; diff_y = 0; } else { w_current->second_wx = w_current->first_wx; diff_x = 0; } } /* Find the bounds of the drawing to be done */ world_get_object_glist_bounds (page->toplevel, page->place_list, &left, &top, &right, &bottom); gschem_page_view_invalidate_world_rect (page_view, left + diff_x, top + diff_y, right + diff_x, bottom + diff_y); }
/*! \brief Updates the preview widget. * \par Function Description * This function update the preview: if the preview is active and a * filename has been given, it opens the file and display * it. Otherwise it display a blank page. * * \param [in] preview The preview widget. */ static void preview_update (Preview *preview) { GSCHEM_TOPLEVEL *preview_w_current = preview->preview_w_current; TOPLEVEL *preview_toplevel = preview_w_current->toplevel; int left, top, right, bottom; int width, height; if (preview_toplevel->page_current == NULL) { return; } /* delete old preview, create new page */ /* it would be better to just resets current page - Fix me */ s_page_delete (preview_toplevel, preview_toplevel->page_current); s_page_goto (preview_toplevel, s_page_new (preview_toplevel, "preview")); if (preview->active) { g_assert ((preview->filename == NULL) || (preview->buffer == NULL)); if (preview->filename != NULL) { /* open up file in current page */ f_open_flags (preview_toplevel, preview_toplevel->page_current, preview->filename, F_OPEN_RC | F_OPEN_RESTORE_CWD, NULL); /* test value returned by f_open... - Fix me */ /* we should display something if there an error occured - Fix me */ } if (preview->buffer != NULL) { /* Load the data buffer */ s_page_append_list (preview_toplevel, preview_toplevel->page_current, o_read_buffer (preview_toplevel, NULL, preview->buffer, -1, _("Preview Buffer"))); } } if (world_get_object_glist_bounds (preview_toplevel, s_page_objects (preview_toplevel->page_current), &left, &top, &right, &bottom)) { /* Clamp the canvas size to the extents of the page being previewed */ width = right - left; height = bottom - top; preview_toplevel->init_left = left - ((double)width * OVER_ZOOM_FACTOR); preview_toplevel->init_right = right + ((double)width * OVER_ZOOM_FACTOR); preview_toplevel->init_top = top - ((double)height * OVER_ZOOM_FACTOR); preview_toplevel->init_bottom = bottom + ((double)height * OVER_ZOOM_FACTOR); } /* display current page (possibly empty) */ a_zoom_extents (preview_w_current, s_page_objects (preview_toplevel->page_current), A_PAN_DONT_REDRAW); o_invalidate_all (preview_w_current); }
/*! \brief Draw a bounding box or outline for OBJECT placement * * \par Function Description * This function draws either the OBJECTS in the place list * or a rectangle around their bounding box, depending upon the * currently selected w_current->actionfeedback_mode. This takes the * value BOUNDINGBOX or OUTLINE. * * The function applies manhatten mode constraints to the coordinates * before drawing if the CONTROL key is recording as being pressed in * the w_current structure. * * The "drawing" parameter is used to indicate if this drawing should * immediately use the selected feedback mode and positioning constraints. * * With drawing=TRUE, the selected conditions are used immediately, * otherwise the conditions from the last drawing operation are used, * saving the new state for next time. * * This function should be called with drawing=TRUE when starting a * rubberbanding operation and when otherwise refreshing the rubberbanded * outline (e.g. after a screen redraw). For any undraw operation, should * be called with drawing=FALSE, ensuring that the undraw XOR matches the * mode and constraints of the corresponding "draw" operation. * * If any mode / constraint changes are made between a undraw, redraw XOR * pair, the latter (draw) operation must be called with drawing=TRUE. If * no mode / constraint changes were made between the pair, it is not * harmful to call the draw operation with "drawing=FALSE". * * \param [in] w_current GSCHEM_TOPLEVEL which we're drawing for. * \param [in] drawing Set to FALSE for undraw operations to ensure * matching conditions to a previous draw operation. */ void o_place_draw_rubber (GSCHEM_TOPLEVEL *w_current, int drawing) { TOPLEVEL *toplevel = w_current->toplevel; int diff_x, diff_y; int left, top, bottom, right; g_return_if_fail (toplevel->page_current->place_list != NULL); /* If drawing is true, then don't worry about the previous drawing * method and movement constraints, use with the current settings */ if (drawing) { w_current->last_drawb_mode = w_current->actionfeedback_mode; w_current->drawbounding_action_mode = (w_current->CONTROLKEY) ? CONSTRAINED : FREE; } /* Calculate delta of X-Y positions from buffer's origin */ diff_x = w_current->second_wx - w_current->first_wx; diff_y = w_current->second_wy - w_current->first_wy; /* Adjust the coordinates according to the movement constraints */ if (w_current->drawbounding_action_mode == CONSTRAINED ) { if (abs(diff_x) >= abs(diff_y)) { w_current->second_wy = w_current->first_wy; diff_y = 0; } else { w_current->second_wx = w_current->first_wx; diff_x = 0; } } /* Draw with the appropriate mode */ if (w_current->last_drawb_mode == BOUNDINGBOX) { /* Find the bounds of the drawing to be done */ world_get_object_glist_bounds (toplevel, toplevel->page_current->place_list, &left, &top, &right, &bottom); gschem_cairo_box (w_current, 0, left + diff_x, top + diff_y, right + diff_x, bottom + diff_y); gschem_cairo_set_source_color (w_current, x_color_lookup_dark (BOUNDINGBOX_COLOR)); gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1); } else { o_glist_draw_place (w_current, diff_x, diff_y, toplevel->page_current->place_list); } /* Save movement constraints and drawing method for any * corresponding undraw operation. */ w_current->last_drawb_mode = w_current->actionfeedback_mode; w_current->drawbounding_action_mode = (w_current->CONTROLKEY) ? CONSTRAINED : FREE; }
/*! \brief Queries the bounds of a complex object. * \par Function Description * This function returns the bounding box of the complex object * <B>object</B>. * * \param [in] toplevel The toplevel environment. * \param [in] complex The complex object. * \param [out] left The leftmost edge of the bounding box (in * world units). * \param [out] top The upper edge of the bounding box (in * world units). * \param [out] right The rightmost edge of the bounding box (in * world units). * \param [out] bottom The bottom edge of the bounding box (in * screen units). */ void world_get_complex_bounds(TOPLEVEL *toplevel, OBJECT *complex, int *left, int *top, int *right, int *bottom) { g_return_if_fail (complex != NULL && (complex->type == OBJ_COMPLEX || complex->type == OBJ_PLACEHOLDER) && complex->complex != NULL); world_get_object_glist_bounds (toplevel, complex->complex->prim_objs, left, top, right, bottom); }
/*! \brief Invalidate bounding box or outline for OBJECT placement * * \par Function Description * This function invalidates the bounding box where objects would be * drawn by o_place_draw_rubber() * * The function applies manhatten mode constraints to the coordinates * before drawing if the CONTROL key is recording as being pressed in * the w_current structure. * * The "drawing" parameter is used to indicate if this drawing should * immediately use the selected feedback mode and positioning constraints. * * With drawing=TRUE, the selected conditions are used immediately, * otherwise the conditions from the last drawing operation are used, * saving the new state for next time. * * This function should be called with drawing=TRUE when starting a * rubberbanding operation and when otherwise refreshing the rubberbanded * outline (e.g. after a screen redraw). For any undraw operation, should * be called with drawing=FALSE, ensuring that the undraw XOR matches the * mode and constraints of the corresponding "draw" operation. * * If any mode / constraint changes are made between a undraw, redraw XOR * pair, the latter (draw) operation must be called with drawing=TRUE. If * no mode / constraint changes were made between the pair, it is not * harmful to call the draw operation with "drawing=FALSE". * * \param [in] w_current GSCHEM_TOPLEVEL which we're drawing for. * \param [in] drawing Set to FALSE for undraw operations to ensure * matching conditions to a previous draw operation. */ void o_place_invalidate_rubber (GSCHEM_TOPLEVEL *w_current, int drawing) { TOPLEVEL *toplevel = w_current->toplevel; int diff_x, diff_y; int left, top, bottom, right; int s_left, s_top, s_bottom, s_right; g_return_if_fail (toplevel->page_current->place_list != NULL); /* If drawing is true, then don't worry about the previous drawing * method and movement constraints, use with the current settings */ if (drawing) { /* Ensure we set this to flag there is "something" supposed to be * drawn when the invaliate call below causes an expose event. */ w_current->last_drawb_mode = w_current->actionfeedback_mode; w_current->drawbounding_action_mode = (w_current->CONTROLKEY) ? CONSTRAINED : FREE; } /* Calculate delta of X-Y positions from buffer's origin */ diff_x = w_current->second_wx - w_current->first_wx; diff_y = w_current->second_wy - w_current->first_wy; /* Adjust the coordinates according to the movement constraints */ /* Need to update the w_current->{first,second}_w{x,y} coords even * though we're only invalidating because the move rubberband code * (which may execute right after this function) expects these * coordinates to be correct. */ if (w_current->drawbounding_action_mode == CONSTRAINED) { if (abs (diff_x) >= abs (diff_y)) { w_current->second_wy = w_current->first_wy; diff_y = 0; } else { w_current->second_wx = w_current->first_wx; diff_x = 0; } } /* Find the bounds of the drawing to be done */ world_get_object_glist_bounds (toplevel, toplevel->page_current->place_list, &left, &top, &right, &bottom); WORLDtoSCREEN (w_current, left + diff_x, top + diff_y, &s_left, &s_top); WORLDtoSCREEN (w_current, right + diff_x, bottom + diff_y, &s_right, &s_bottom); o_invalidate_rect (w_current, s_left, s_top, s_right, s_bottom); }
/*! \brief Export a figure-style PDF file of the current page. * \par Function Description * Exports the current page as a PDF file to \a filename. The export * is carried out using a page size matching the size of the visible * extents of the schematic page. * * \param w_current A #GschemToplevel structure. * \param filename The filename for generated PDF. * * \returns TRUE if the operation was successful. */ gboolean x_print_export_pdf (GschemToplevel *w_current, const gchar *filename) { cairo_surface_t *surface; cairo_status_t cr_status; cairo_t *cr; int status, wx_min, wy_min, wx_max, wy_max; double width, height; /* First, calculate a transformation matrix for the cairo * context. We want to center the extents of the page in the * available page area. */ status = world_get_object_glist_bounds (w_current->toplevel, s_page_objects (w_current->toplevel->page_current), &wx_min, &wy_min, &wx_max, &wy_max); if (status) { width = (wx_max - wx_min) * DEFAULT_ADOBE_PDF_PPI / DEFAULT_GSCHEM_PPI; height = (wy_max - wy_min) * DEFAULT_ADOBE_PDF_PPI / DEFAULT_GSCHEM_PPI; } else { /* Fallback size if there are no drawable objects */ width = height = DEFAULT_PDF_SIZE; } surface = cairo_pdf_surface_create (filename, width, height); cr = cairo_create (surface); x_print_draw_page (w_current->toplevel, w_current->toplevel->page_current, cr, NULL, width, height, w_current->toplevel->image_color, FALSE); cairo_destroy (cr); cairo_surface_finish (surface); cr_status = cairo_surface_status (surface); if (cr_status != CAIRO_STATUS_SUCCESS) { g_warning (_("Failed to write PDF to '%1$s': %2$s\n"), filename, cairo_status_to_string (cr_status)); return FALSE; } cairo_surface_destroy (surface); return TRUE; }
/*! \brief Calculate the bounds of a complex object * * On failure, this function sets the bounds to empty. * * \param [in] toplevel The toplevel object. * \param [in] object The complex object. * \param [out] bounds The bounds of the complex object */ void geda_complex_object_calculate_bounds (TOPLEVEL *toplevel, const OBJECT *object, GedaBounds *bounds) { geda_bounds_init (bounds); g_return_if_fail (object != NULL); g_return_if_fail (((object->type == OBJ_COMPLEX) || (object->type == OBJ_PLACEHOLDER))); g_return_if_fail (object->complex != NULL); world_get_object_glist_bounds (toplevel, object->complex->prim_objs, &(bounds->min_x), &(bounds->min_y), &(bounds->max_x), &(bounds->max_y)); }
/*! \brief Create a default page setup for a schematic page. * \par Function Description * Creates and returns a new #GtkPageSetup for \a page, taking into * account the requested \a paper_size_name. If \a paper_size_name is * NULL, the system default paper size is used. The \a orientation may * be LANDSCAPE, PORTRAIT or AUTOLAYOUT. If \a AUTOLAYOUT is chosen, * the page orientation that best fits the page contents is chosen. * * \param toplevel A #TOPLEVEL structure. * \param page The #PAGE to generate a page setup for. * \param paper_size_name The name of the paper size to use. * \param orientation The paper orientation to use. * * \returns A newly-created page setup. */ static GtkPageSetup * x_print_default_page_setup (TOPLEVEL *toplevel, PAGE *page) { GtkPageSetup *setup = gtk_page_setup_new (); GtkPaperSize *papersize; int status, wx_min, wy_min, wx_max, wy_max; EdaConfig *cfg; gchar *paper, *orientation; /* Get configuration values */ cfg = eda_config_get_context_for_path (s_page_get_filename (page)); paper = eda_config_get_string (cfg, CFG_GROUP_PRINTING, CFG_KEY_PRINTING_PAPER, NULL); orientation = eda_config_get_string (cfg, CFG_GROUP_PRINTING, CFG_KEY_PRINTING_ORIENTATION, NULL); /* If the paper size is valid, set it up with default margins. */ papersize = gtk_paper_size_new (paper); if (papersize != NULL) { gtk_page_setup_set_paper_size_and_default_margins (setup, papersize); } if (g_strcmp0 (orientation, "landscape") == 0) { gtk_page_setup_set_orientation (setup, GTK_PAGE_ORIENTATION_LANDSCAPE); } else if (g_strcmp0 (orientation, "portrait") == 0) { gtk_page_setup_set_orientation (setup, GTK_PAGE_ORIENTATION_PORTRAIT); } else if (orientation == NULL || g_strcmp0 (orientation, "auto") == 0) { /* Automatically choose the orientation that fits best */ status = world_get_object_glist_bounds (toplevel, s_page_objects (page), &wx_min, &wy_min, &wx_max, &wy_max); if (!status || (wx_max - wx_min) > (wy_max - wy_min)) { /* Default to landscape */ gtk_page_setup_set_orientation (setup, GTK_PAGE_ORIENTATION_LANDSCAPE); } else { gtk_page_setup_set_orientation (setup, GTK_PAGE_ORIENTATION_PORTRAIT); } } g_free (paper); g_free (orientation); return setup; }
/*! \brief Draw a page. * \par Function Description * Draws the \a page on the Cairo context \a cr, which should have * dimensions \a cr_width and \a cr_height. If the Pango context \a * pc is provided, it is used for rendering of text. The parameter \a * is_color controls whether to enable color printing, and \a * is_raster should be set if drawing to a raster surface such as an * image. * * \param toplevel A #TOPLEVEL structure. * \param page The #PAGE to be rendered. * \param cr The Cairo context to render to. * \param pc A Pango context for text rendering, or NULL. * \param cr_width The width of the drawing area. * \param cr_height The height of the drawing area. * \param is_color TRUE if drawing should be in color; FALSE otherwise. * \param is_raster TRUE if drawing to a raster image surface; FALSE otherwise. */ static void x_print_draw_page (TOPLEVEL *toplevel, PAGE *page, cairo_t *cr, PangoContext *pc, double cr_width, double cr_height, gboolean is_color, gboolean is_raster) { EdaRenderer *renderer; cairo_matrix_t mtx; GArray *color_map; int status, wx_min, wy_min, wx_max, wy_max; double w_width, w_height, scale; GList *iter; /* First, calculate a transformation matrix for the cairo * context. We want to center the extents of the page in the * available page area. */ status = world_get_object_glist_bounds (toplevel, s_page_objects (page), &wx_min, &wy_min, &wx_max, &wy_max); /* If there are no printable objects, draw nothing. */ if (!status) return; w_width = wx_max - wx_min; w_height = wy_max - wy_min; scale = fmin (cr_width / w_width, cr_height / w_height); cairo_matrix_init (&mtx, scale, 0, 0, -scale, - (wx_min + 0.5*w_width) * scale + 0.5*cr_width, (wy_min + 0.5*w_height) * scale + 0.5*cr_height); /* Second, build the color map. If no color printing is desired, * transform the print color map into a black-and-white color map by * making the background color transparent and replacing all other * enabled colors with solid black. */ color_map = g_array_sized_new (FALSE, FALSE, sizeof(GedaColor), MAX_COLORS); color_map = g_array_append_vals (color_map, print_colors, MAX_COLORS); if (!is_color) { int i; for (i = 0; i < MAX_COLORS; i++) { GedaColor *c = &g_array_index (color_map, GedaColor, i); if (!c->enabled) continue; /* Disable background color & fully-transparent colors */ if (c->a == 0 || i == BACKGROUND_COLOR) { c->enabled = FALSE; continue; } /* Set any remaining colors solid black */ c->r = 0; c->g = 0; c->b = 0; c->a = ~0; } } /* Thirdly, create and initialise a renderer */ renderer = EDA_RENDERER (g_object_new (EDA_TYPE_RENDERER, "cairo-context", cr, "pango-context", pc, "color-map", color_map, "render-flags", is_raster ? EDA_RENDERER_FLAG_HINTING : 0, NULL)); /* Finally, actually do drawing */ cairo_save (cr); cairo_transform (cr, &mtx); /* Draw background */ eda_cairo_set_source_color (cr, BACKGROUND_COLOR, color_map); cairo_paint (cr); /* Draw all objects and cues */ for (iter = (GList *) s_page_objects (page); iter != NULL; iter = g_list_next (iter)) { eda_renderer_draw (renderer, (OBJECT *) iter->data); } for (iter = (GList *) s_page_objects (page); iter != NULL; iter = g_list_next (iter)) { eda_renderer_draw_cues (renderer, (OBJECT *) iter->data); } cairo_restore (cr); g_object_unref (renderer); g_array_free (color_map, TRUE); }
/*! \brief Draw a bounding box or outline for OBJECT placement * \par Function Description * This function draws either the OBJECTS in the place list * or a rectangle around their bounding box, depending upon the * currently selected w_current->actionfeedback_mode. This takes the * value BOUNDINGBOX or OUTLINE. * * The function applies manhatten mode constraints to the coordinates * before drawing if the CONTROL key is recording as being pressed in * the w_current structure. * * \param w_current GschemToplevel which we're drawing for. * \param renderer Renderer to use for drawing. */ void o_place_draw_rubber (GschemToplevel *w_current, EdaRenderer *renderer) { int diff_x, diff_y; cairo_t *cr = eda_renderer_get_cairo_context (renderer); g_return_if_fail (w_current != NULL); GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (page_view != NULL); PAGE *page = gschem_page_view_get_page (page_view); g_return_if_fail (page != NULL); g_return_if_fail (page->place_list != NULL); /* Don't worry about the previous drawing method and movement * constraints, use with the current settings */ w_current->last_drawb_mode = w_current->actionfeedback_mode; w_current->drawbounding_action_mode = (w_current->CONTROLKEY && ! ((w_current->event_state == PASTEMODE) || (w_current->event_state == COMPMODE) || (w_current->event_state == TEXTMODE))) ? CONSTRAINED : FREE; /* Calculate delta of X-Y positions from buffer's origin */ diff_x = w_current->second_wx - w_current->first_wx; diff_y = w_current->second_wy - w_current->first_wy; /* Adjust the coordinates according to the movement constraints */ if (w_current->drawbounding_action_mode == CONSTRAINED ) { if (abs(diff_x) >= abs(diff_y)) { w_current->second_wy = w_current->first_wy; diff_y = 0; } else { w_current->second_wx = w_current->first_wx; diff_x = 0; } } /* Translate the cairo context to the required offset before drawing. */ cairo_save (cr); cairo_translate (cr, diff_x, diff_y); /* Draw with the appropriate mode */ if (w_current->last_drawb_mode == BOUNDINGBOX) { GArray *map = eda_renderer_get_color_map (renderer); int flags = eda_renderer_get_cairo_flags (renderer); int left, top, bottom, right; /* Find the bounds of the drawing to be done */ world_get_object_glist_bounds (page->toplevel, page->place_list, &left, &top, &right, &bottom); /* Draw box outline */ eda_cairo_box (cr, flags, 0, left, top, right, bottom); eda_cairo_set_source_color (cr, BOUNDINGBOX_COLOR, map); eda_cairo_stroke (cr, flags, TYPE_SOLID, END_NONE, 0, -1, -1); } else { GList *iter; for (iter = page->place_list; iter != NULL; iter = g_list_next (iter)) { eda_renderer_draw (renderer, (OBJECT *) iter->data); } } cairo_restore (cr); }
/* Calculates a page layout. If page is NULL, uses the first page * (this is convenient for single-page rendering). The required size * of the page is returned in extents, and the cairo transformation * matrix needed to fit the drawing into the page is returned in mtx. * Takes into account all of the margin/orientation/paper settings, * and the size of the drawing itself. */ static void export_layout_page (PAGE *page, cairo_rectangle_t *extents, cairo_matrix_t *mtx) { cairo_rectangle_t drawable; int wx_min, wy_min, wx_max, wy_max, w_width, w_height; gboolean landscape = FALSE; gdouble m[4]; /* Calculated margins */ gdouble s; /* Calculated scale */ gdouble slack[2]; /* Calculated alignment slack */ if (page == NULL) { const GList *pages = geda_list_get_glist (toplevel->pages); g_assert (pages != NULL && pages->data != NULL); page = (PAGE *) pages->data; } /* Set the margins. If none were provided by the user, get them * from the paper size (if a paper size is being used) or just use a * sensible default. */ if (settings.margins[0] >= 0) { memcpy (m, settings.margins, 4*sizeof(gdouble)); } else if (settings.paper != NULL) { m[0] = gtk_paper_size_get_default_top_margin (settings.paper, GTK_UNIT_POINTS); m[1] = gtk_paper_size_get_default_left_margin (settings.paper, GTK_UNIT_POINTS); m[2] = gtk_paper_size_get_default_bottom_margin (settings.paper, GTK_UNIT_POINTS); m[3] = gtk_paper_size_get_default_right_margin (settings.paper, GTK_UNIT_POINTS); } else { m[0] = DEFAULT_MARGIN; m[1] = DEFAULT_MARGIN; m[2] = DEFAULT_MARGIN; m[3] = DEFAULT_MARGIN; } /* Now calculate extents of objects within page */ world_get_object_glist_bounds (toplevel, s_page_objects (page), &wx_min, &wy_min, &wx_max, &wy_max); w_width = wx_max - wx_min; w_height = wy_max - wy_min; /* If a size was specified, use it. Otherwise, use paper size, if * provided. Fall back to just using the size of the drawing. */ extents->x = extents->y = 0; if (settings.size[0] >= 0) { /* get extents from size */ extents->width = settings.size[0]; extents->height = settings.size[1]; } else if (settings.paper != NULL) { /* get extents from paper */ gdouble p_width, p_height; /* Select orientation */ switch (settings.layout) { case ORIENTATION_LANDSCAPE: landscape = TRUE; break; case ORIENTATION_PORTRAIT: landscape = FALSE; break; case ORIENTATION_AUTO: default: landscape = (w_width > w_height); break; } p_width = gtk_paper_size_get_width (settings.paper, GTK_UNIT_POINTS); p_height = gtk_paper_size_get_height (settings.paper, GTK_UNIT_POINTS); if (landscape) { extents->width = p_height; extents->height = p_width; } else { extents->width = p_width; extents->height = p_height; } } else { /* get extents from drawing */ extents->width = w_width * settings.scale; /* in points */ extents->height = w_height * settings.scale; /* in points */ /* If the extents were obtained from the drawing, grow the extents * rather than shrinking the drawable area. This ensures that the * overall aspect ratio of the image remains correct. */ extents->width += m[1] + m[3]; extents->height += m[0] + m[2]; } drawable.x = m[1]; drawable.y = m[0]; drawable.width = extents->width - m[1] - m[3]; drawable.height = extents->height - m[0] - m[2]; /* Calculate optimum scale */ s = fmin (drawable.width / w_width, drawable.height / w_height); /* Calculate alignment slack */ slack[0] = fmin (1, fmax (0, settings.align[0])) * (drawable.width - w_width * s); slack[1] = fmin (1, fmax (0, settings.align[1])) * (drawable.height - w_height * s); /* Finally, create and set a cairo transformation matrix that * centres the drawing into the drawable area. */ cairo_matrix_init (mtx, s, 0, 0, -s, - wx_min * s + drawable.x + slack[0], (wy_min + w_height) * s + drawable.y + slack[1]); }
/*! \brief Attach attribute to object. * * Attach the name=value pair to the OBJECT "object". This function * was stolen from gschem/src/o_attrib.c:o_attrib_add_attrib and * hacked for gattrib. * \param toplevel TOPLEVEL to operate on * \param text_string * \param visibility * \param show_name_value * \param object * \returns pointer to the object * \todo Does it need to return OBJECT? */ OBJECT * s_object_attrib_add_attrib_in_object (TOPLEVEL *toplevel, char *text_string, int visibility, int show_name_value, OBJECT * object) { int world_x = -1, world_y = -1; int color; int left, right, top, bottom; OBJECT *o_current; OBJECT *new_obj; o_current = object; /* creating a toplevel or unattached attribute */ if (o_current) { /* get coordinates of where to place the text object */ switch (o_current->type) { case (OBJ_COMPLEX): world_x = o_current->complex->x; world_y = o_current->complex->y; color = ATTRIBUTE_COLOR; break; case (OBJ_NET): world_x = o_current->complex->x; world_y = o_current->complex->y; color = ATTRIBUTE_COLOR; break; default: fprintf(stderr, _("In s_object_attrib_add_attrib_in_object, trying to add attrib to non-complex or non-net!\n")); exit(-1); } } else { /* This must be a floating attrib, but what is that !?!?!?!?! */ world_get_object_glist_bounds (toplevel, s_page_objects (toplevel->page_current), &left, &top, &right, &bottom); /* this really is the lower left hand corner */ world_x = left; world_y = top; /* printf("%d %d\n", world_x, world_y); */ color = DETACHED_ATTRIBUTE_COLOR; } /* first create text item */ #if DEBUG printf("=== In s_object_attrib_add_attrib_in_object, about to attach new text attrib with properties:\n"); printf(" color = %d\n", color); printf(" text_string = %s \n", text_string); printf(" text_size = %d \n", toplevel->text_size); printf(" visibility = %d \n", visibility); printf(" show_name_value = %d \n", show_name_value); #endif new_obj = o_text_new (toplevel, color, world_x, world_y, LOWER_LEFT, 0, /* zero is angle */ text_string, DEFAULT_TEXT_SIZE, visibility, show_name_value); s_page_append (toplevel, toplevel->page_current, new_obj); /* now toplevel->page_current->object_tail contains new text item */ /* now attach the attribute to the object (if o_current is not NULL) */ /* remember that o_current contains the object to get the attribute */ if (o_current) { o_attrib_attach (toplevel, new_obj, o_current, FALSE); } o_selection_add (toplevel, toplevel->page_current->selection_list, new_obj); toplevel->page_current->CHANGED = 1; return new_obj; }
/*! \brief Updates the preview widget. * \par Function Description * This function updates the preview: if the preview is active and a * filename has been given, it opens the file and displays * it. Otherwise it displays a blank page. * * \param [in] preview The preview widget. */ static void preview_update (Preview *preview) { int left, top, right, bottom; int width, height; GError * err = NULL; GschemPageView *preview_view = GSCHEM_PAGE_VIEW (preview); g_return_if_fail (preview_view != NULL); PAGE *preview_page = gschem_page_view_get_page (preview_view); if (preview_page == NULL) { return; } TOPLEVEL *preview_toplevel = preview_page->toplevel; /* delete old preview */ s_page_delete_objects (preview_toplevel, preview_page); if (preview->active) { g_assert ((preview->filename == NULL) || (preview->buffer == NULL)); if (preview->filename != NULL) { /* open up file in current page */ f_open_flags (preview_toplevel, preview_page, preview->filename, F_OPEN_RC | F_OPEN_RESTORE_CWD, NULL); /* test value returned by f_open... - Fix me */ /* we should display something if there an error occured - Fix me */ } if (preview->buffer != NULL) { /* Load the data buffer */ GList * objects = o_read_buffer (preview_toplevel, NULL, preview->buffer, -1, _("Preview Buffer"), &err); if (err == NULL) { s_page_append_list (preview_toplevel, preview_page, objects); } else { s_page_append (preview_toplevel, preview_page, o_text_new(preview_toplevel, OBJ_TEXT, 2, 100, 100, LOWER_MIDDLE, 0, err->message, 10, VISIBLE, SHOW_NAME_VALUE)); g_error_free(err); } } } if (world_get_object_glist_bounds (preview_toplevel, s_page_objects (preview_page), &left, &top, &right, &bottom)) { /* Clamp the canvas size to the extents of the page being previewed */ width = right - left; height = bottom - top; GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (preview_view); geometry->world_left = left - ((double)width * OVER_ZOOM_FACTOR); geometry->world_right = right + ((double)width * OVER_ZOOM_FACTOR); geometry->world_top = top - ((double)height * OVER_ZOOM_FACTOR); geometry->world_bottom = bottom + ((double)height * OVER_ZOOM_FACTOR); } /* display current page (possibly empty) */ gschem_page_view_zoom_extents (preview_view, NULL); }
/* text item */ OBJECT *o_attrib_add_attrib(GschemToplevel *w_current, const char *text_string, int visibility, int show_name_value, OBJECT *object) { TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); OBJECT *new_obj; int world_x = - 1, world_y = -1; int align = LOWER_LEFT; int angle = 0; int color; int left, right, top, bottom; OBJECT *o_current; color = DETACHED_ATTRIBUTE_COLOR; o_current = object; /* creating a toplevel or unattached attribute */ if (o_current) { /* get coordinates of where to place the text object */ switch(o_current->type) { case(OBJ_COMPLEX): case(OBJ_PLACEHOLDER): world_x = o_current->complex->x; world_y = o_current->complex->y; align = LOWER_LEFT; angle = 0; color = ATTRIBUTE_COLOR; break; case(OBJ_ARC): world_x = o_current->arc->x; world_y = o_current->arc->y; align = LOWER_LEFT; angle = 0; color = ATTRIBUTE_COLOR; break; case(OBJ_CIRCLE): world_x = o_current->circle->center_x; world_y = o_current->circle->center_y; align = LOWER_LEFT; angle = 0; color = ATTRIBUTE_COLOR; break; case(OBJ_BOX): world_x = o_current->box->upper_x; world_y = o_current->box->upper_y; align = LOWER_LEFT; angle = 0; color = ATTRIBUTE_COLOR; break; case(OBJ_LINE): case(OBJ_NET): case(OBJ_PIN): case(OBJ_BUS): { int dx = o_current->line->x[1] - o_current->line->x[0]; int dy = o_current->line->y[1] - o_current->line->y[0]; if (dy == 0) { if (dx > 0) { world_x = o_current->line->x[0] + SPACING_FROM_END; world_y = o_current->line->y[0] + SPACING_PERPENDICULAR; align = LOWER_LEFT; angle = 0; } else { world_x = o_current->line->x[0] - SPACING_FROM_END; world_y = o_current->line->y[0] + SPACING_PERPENDICULAR; align = LOWER_RIGHT; angle = 0; } } else if (dx == 0) { if (dy > 0) { world_x = o_current->line->x[0] - SPACING_PERPENDICULAR; world_y = o_current->line->y[0] + SPACING_FROM_END; align = LOWER_LEFT; angle = 90; } else { world_x = o_current->line->x[0] - SPACING_PERPENDICULAR; world_y = o_current->line->y[0] - SPACING_FROM_END; align = LOWER_RIGHT; angle = 90; } } else { world_x = o_current->line->x[0]; world_y = o_current->line->y[0]; align = LOWER_LEFT; angle = 0; } color = ATTRIBUTE_COLOR; } break; case(OBJ_TEXT): world_x = o_current->text->x; world_y = o_current->text->y; color = DETACHED_ATTRIBUTE_COLOR; align = LOWER_LEFT; angle = 0; o_current = NULL; break; } } else { world_get_object_glist_bounds (toplevel, s_page_objects (toplevel->page_current), &left, &top, &right, &bottom); /* this really is the lower left hand corner */ world_x = left; world_y = top; /* printf("%d %d\n", world_x, world_y); */ align = LOWER_LEFT; angle = 0; color = DETACHED_ATTRIBUTE_COLOR; } /* first create text item */ new_obj = o_text_new(toplevel, color, world_x, world_y, align, angle, text_string, w_current->text_size, /* current text size */ visibility, show_name_value); s_page_append (toplevel, toplevel->page_current, new_obj); /* now attach the attribute to the object (if o_current is not NULL) */ /* remember that o_current contains the object to get the attribute */ if (o_current) { o_attrib_attach (toplevel, new_obj, o_current, FALSE); } o_selection_add (toplevel, toplevel->page_current->selection_list, new_obj); /* handle slot= attribute, it's a special case */ if (o_current != NULL && g_ascii_strncasecmp (text_string, "slot=", 5) == 0) { o_slot_end (w_current, o_current, text_string); } /* Call add-objects-hook. */ g_run_hook_object (w_current, "%add-objects-hook", new_obj); g_run_hook_object (w_current, "%select-objects-hook", new_obj); gschem_toplevel_page_content_changed (w_current, toplevel->page_current); return new_obj; }
/*! \brief guess the whichend of pins of object list * \par Function Description * This function determines the whichend of the pins in the \a object_list. * In older libgeda file format versions there was no information about the * active end of pins. * This function calculates the bounding box of all pins in the object list. * The side of the pins that are closer to the boundary of the box are * set as active ends of the pins. * * \param toplevel The TOPLEVEL object * \param object_list list of OBJECTs * \param num_pins pin count in the object list * */ void o_pin_update_whichend(TOPLEVEL *toplevel, GList *object_list, int num_pins) { OBJECT *o_current; GList *iter; int top = 0, left = 0; int right = 0, bottom = 0; int d1, d2, d3, d4; int min0, min1; int min0_whichend, min1_whichend; int rleft, rtop, rright, rbottom; int found; if (object_list && num_pins) { if (num_pins == 1 || toplevel->force_boundingbox) { world_get_object_glist_bounds (toplevel, object_list, &left, &top, &right, &bottom); } else { found = 0; /* only look at the pins to calculate bounds of the symbol */ iter = object_list; while (iter != NULL) { o_current = (OBJECT *)iter->data; if (o_current->type == OBJ_PIN) { rleft = o_current->w_left; rtop = o_current->w_top; rright = o_current->w_right; rbottom = o_current->w_bottom; if ( found ) { left = min( left, rleft ); top = min( top, rtop ); right = max( right, rright ); bottom = max( bottom, rbottom ); } else { left = rleft; top = rtop; right = rright; bottom = rbottom; found = 1; } } iter = g_list_next (iter); } } } else { return; } iter = object_list; while (iter != NULL) { o_current = (OBJECT *)iter->data; /* Determine which end of the pin is on or nearest the boundary */ if (o_current->type == OBJ_PIN && o_current->whichend == -1) { if (o_current->line->y[0] == o_current->line->y[1]) { /* horizontal */ if (o_current->line->x[0] == left) { o_current->whichend = 0; } else if (o_current->line->x[1] == left) { o_current->whichend = 1; } else if (o_current->line->x[0] == right) { o_current->whichend = 0; } else if (o_current->line->x[1] == right) { o_current->whichend = 1; } else { d1 = abs(o_current->line->x[0] - left); d2 = abs(o_current->line->x[1] - left); d3 = abs(o_current->line->x[0] - right); d4 = abs(o_current->line->x[1] - right); if (d1 <= d2) { min0 = d1; min0_whichend = 0; } else { min0 = d2; min0_whichend = 1; } if (d3 <= d4) { min1 = d3; min1_whichend = 0; } else { min1 = d4; min1_whichend = 1; } if (min0 <= min1) { o_current->whichend = min0_whichend; } else { o_current->whichend = min1_whichend; } } } else if (o_current->line->x[0] == o_current->line->x[1]) { /* vertical */ if (o_current->line->y[0] == top) { o_current->whichend = 0; } else if (o_current->line->y[1] == top) { o_current->whichend = 1; } else if (o_current->line->x[0] == bottom) { o_current->whichend = 0; } else if (o_current->line->x[1] == bottom) { o_current->whichend = 1; } else { d1 = abs(o_current->line->y[0] - top); d2 = abs(o_current->line->y[1] - top); d3 = abs(o_current->line->y[0] - bottom); d4 = abs(o_current->line->y[1] - bottom); if (d1 <= d2) { min0 = d1; min0_whichend = 0; } else { min0 = d2; min0_whichend = 1; } if (d3 <= d4) { min1 = d3; min1_whichend = 0; } else { min1 = d4; min1_whichend = 1; } if (min0 <= min1) { o_current->whichend = min0_whichend; } else { o_current->whichend = min1_whichend; } } } } iter = g_list_next (iter); } }