/*! \brief Records a new point for the stroke. * \par Function Description * This function adds the point (<B>x</B>,<B>y</B>) as a new point in * the stroke. * * The footprint is updated and the new point is drawn on the drawing area. * * \param [in] w_current The GschemToplevel object. * \param [in] x The X coord of the new point. * \param [in] Y The X coord of the new point. */ void x_stroke_record (GschemToplevel *w_current, gint x, gint y) { cairo_matrix_t user_to_device_matrix; double x0, y0; GschemPageView *view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (view != NULL); GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view); g_return_if_fail (geometry != NULL); g_assert (stroke_points != NULL); stroke_record (x, y); if (stroke_points->len < STROKE_MAX_POINTS) { StrokePoint point = { x, y }; g_array_append_val (stroke_points, point); cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET(view))); GedaColor *color = x_color_lookup (w_current, STROKE_COLOR); cairo_set_source_rgba (cr, geda_color_get_red_double (color), geda_color_get_green_double (color), geda_color_get_blue_double (color), geda_color_get_alpha_double (color)); cairo_set_matrix (cr, gschem_page_geometry_get_world_to_screen_matrix (geometry)); x0 = x; y0 = y; cairo_device_to_user (cr, &x0, &y0); cairo_get_matrix (cr, &user_to_device_matrix); cairo_save (cr); cairo_identity_matrix (cr); cairo_matrix_transform_point (&user_to_device_matrix, &x0, &y0); cairo_rectangle (cr, x0, y0, 1, 1); cairo_fill (cr); cairo_restore (cr); cairo_destroy (cr); } }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_move_end_rubberband (GschemToplevel *w_current, int w_dx, int w_dy, GList** objects) { GList *s_iter, *s_iter_next; 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); for (s_iter = w_current->stretch_list; s_iter != NULL; s_iter = s_iter_next) { STRETCH *s_current = s_iter->data; OBJECT *object = s_current->object; int whichone = s_current->whichone; /* Store this now, since we may delete the current item */ s_iter_next = g_list_next (s_iter); if (object->type == OBJ_NET || object->type == OBJ_BUS) { /* remove the object's connections */ s_conn_remove_object_connections (page->toplevel, object); object->line->x[whichone] += w_dx; object->line->y[whichone] += w_dy; if (o_move_zero_length (object)) { w_current->stretch_list = s_stretch_remove (w_current->stretch_list, object); o_delete (w_current, object); continue; } object->w_bounds_valid_for = NULL; s_conn_update_object (page, object); *objects = g_list_append (*objects, object); } } }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_move_invalidate_rubber (GschemToplevel *w_current, int drawing) { GList *s_iter; int dx1, dx2, dy1, dy2; gboolean net_rubber_band_mode; 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); net_rubber_band_mode = gschem_options_get_net_rubber_band_mode (w_current->options); o_place_invalidate_rubber (w_current, drawing); if (net_rubber_band_mode) { for (s_iter = w_current->stretch_list; s_iter != NULL; s_iter = g_list_next (s_iter)) { STRETCH *s_current = s_iter->data; OBJECT *object = s_current->object; switch (object->type) { case (OBJ_NET): case (OBJ_BUS): if (s_current->whichone == 0) { dx1 = w_current->second_wx - w_current->first_wx; dy1 = w_current->second_wy - w_current->first_wy; dx2 = dy2 = 0; } else { dx1 = dy1 = 0; dx2 = w_current->second_wx - w_current->first_wx; dy2 = w_current->second_wy - w_current->first_wy; } gschem_page_view_invalidate_world_rect (page_view, object->line->x[0] + dx1, object->line->y[0] + dy1, object->line->x[1] + dx2, object->line->y[1] + dy2); } } } }
/*! \brief End the input of a circle. * \par Function Description * This function ends the input of the radius of the circle. * The (<B>w_x</B>,<B>w_y</B>) point is taken as the other end of the radius * segment, i.e. on the circle. The distance between this point and the * center is the radius of the circle. * <B>w_x</B> and <B>w_y</B> are in world coords. * * The center has previously been input and saved as * (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>). * * The temporary circle drawn during the input of the radius is erased. * A new object is allocated, initialized and linked in the object list. * This new object is finally drawn. * * \param [in] w_current The GschemToplevel object. * \param [in] w_x (unused) * \param [in] w_y (unused) */ void o_circle_end(GschemToplevel *w_current, int w_x, int w_y) { OBJECT *new_obj; GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (page_view != NULL); g_assert( w_current->inside_action != 0 ); PAGE *page = gschem_page_view_get_page (page_view); g_return_if_fail (page != NULL); TOPLEVEL *toplevel = page->toplevel; g_return_if_fail (toplevel != NULL); /* erase the temporary circle */ /* o_circle_invalidate_rubber (w_current); */ w_current->rubber_visible = 0; /* circle with null radius are not allowed */ if (w_current->distance == 0) { /* cancel the object creation */ return; } /* create the object */ new_obj = geda_circle_object_new (toplevel, GRAPHIC_COLOR, w_current->first_wx, w_current->first_wy, w_current->distance); s_page_append (toplevel, page, new_obj); /* Call add-objects-hook */ g_run_hook_object (w_current, "%add-objects-hook", new_obj); gschem_toplevel_page_content_changed (w_current, page); o_undo_savestate(w_current, page, UNDO_ALL); i_action_stop (w_current); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_net_invalidate_rubber (GschemToplevel *w_current) { int size = 0, magnetic_halfsize; int magnetic_x, magnetic_y; gboolean magnetic_net_mode; 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); gschem_page_view_WORLDtoSCREEN (page_view, w_current->magnetic_wx, w_current->magnetic_wy, &magnetic_x, &magnetic_y); size = gschem_page_view_SCREENabs (page_view, NET_WIDTH); magnetic_net_mode = gschem_options_get_magnetic_net_mode (w_current->options); if (magnetic_net_mode) { if (w_current->magnetic_wx != -1 && w_current->magnetic_wy != -1) { magnetic_halfsize = max (4 * size, MAGNETIC_HALFSIZE); o_invalidate_rect (w_current, magnetic_x - magnetic_halfsize, magnetic_y - magnetic_halfsize, magnetic_x + magnetic_halfsize, magnetic_y + magnetic_halfsize); } } gschem_page_view_invalidate_world_rect (page_view, w_current->first_wx, w_current->first_wy, w_current->second_wx, w_current->second_wy); gschem_page_view_invalidate_world_rect (page_view, w_current->second_wx, w_current->second_wy, w_current->third_wx, w_current->third_wy); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_edit_show_hidden (GschemToplevel *w_current, const GList *o_list) { /* this function just shows the hidden text, but doesn't toggle it */ /* this function does not change the CHANGED bit, no real changes are */ /* made to the schematic */ /* toggle show_hidden_text variable, which when it is true */ /* means that hidden text IS drawn */ w_current->toplevel->show_hidden_text = !w_current->toplevel->show_hidden_text; i_show_state(w_current, NULL); /* update screen status */ o_edit_show_hidden_lowlevel(w_current, o_list); gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current)); if (w_current->toplevel->show_hidden_text) { s_log_message(_("Hidden text is now visible\n")); } else { s_log_message(_("Hidden text is now invisible\n")); } }
/*! \brief Mirror the objects being placed * * \par Function Description * This function erases the objects in the place list, mirrors * them, runs %mirror-objects-hook, and redraws the objects after * mirroring. * * \param [in] w_current The GschemToplevel object. */ void o_place_mirror (GschemToplevel *w_current) { 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); o_place_invalidate_rubber (w_current, FALSE); geda_object_list_mirror (page->place_list, w_current->first_wx, w_current->first_wy, page->toplevel); /* Run mirror-objects-hook */ g_run_hook_object_list (w_current, "%mirror-objects-hook", page->place_list); o_place_invalidate_rubber (w_current, TRUE); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_move_prep_rubberband(GschemToplevel *w_current) { GList *s_current; OBJECT *object; OBJECT *o_current; GList *iter; 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); for (s_current = geda_list_get_glist (page->selection_list); s_current != NULL; s_current = g_list_next (s_current)) { object = s_current->data; if (object == NULL) continue; switch (object->type) { case (OBJ_NET): case (OBJ_PIN): case (OBJ_BUS): o_move_check_endpoint (w_current, object); break; case (OBJ_COMPLEX): case (OBJ_PLACEHOLDER): for (iter = object->complex->prim_objs; iter != NULL; iter = g_list_next (iter)) { o_current = iter->data; if (o_current->type == OBJ_PIN) { o_move_check_endpoint (w_current, o_current); } } break; } } }
/*! \brief Rotate the objects being placed * * \par Function Description * This function erases the objects in the place list, rotates * them, runs %rotate-objects-hook, and redraws the objects after * rotating. * * \param [in] w_current The GschemToplevel object. */ void o_place_rotate (GschemToplevel *w_current) { 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); o_place_invalidate_rubber (w_current, FALSE); o_glist_rotate_world (page->toplevel, w_current->first_wx, w_current->first_wy, 90, page->place_list); /* Run rotate-objects-hook */ g_run_hook_object_list (w_current, "%rotate-objects-hook", page->place_list); o_place_invalidate_rubber (w_current, TRUE); }
/*! \brief Changes the current page. * \par Function Description * This function displays the specified page <B>page</B> in the * window attached to <B>toplevel</B>. * * It changes the <B>toplevel</B>'s current page to <B>page</B>, * draws it and updates the user interface. * * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>. * * \param [in] w_current The toplevel environment. * \param [in] page The page to become current page. */ void x_window_set_current_page (GschemToplevel *w_current, PAGE *page) { GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (page != NULL); g_return_if_fail (page_view != NULL); o_redraw_cleanstates (w_current); gschem_page_view_set_page (page_view, page); i_update_menus (w_current); i_set_filename (w_current, page->page_filename); x_pagesel_update (w_current); x_multiattrib_update (w_current); gschem_page_view_update_scroll_adjustments (page_view); gschem_page_view_invalidate_all (page_view); }
/*! \brief draw rubbernet lines to the gc * \par Function Description * This function draws the rubbernets to the graphic context */ void o_net_draw_rubber(GschemToplevel *w_current, EdaRenderer *renderer) { int size = NET_WIDTH, w_magnetic_halfsize; cairo_t *cr = eda_renderer_get_cairo_context (renderer); GArray *color_map = eda_renderer_get_color_map (renderer); int flags = eda_renderer_get_cairo_flags (renderer); gboolean magnetic_net_mode; 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); eda_cairo_set_source_color (cr, SELECT_COLOR, color_map); magnetic_net_mode = gschem_options_get_magnetic_net_mode (w_current->options); if (magnetic_net_mode) { if (w_current->magnetic_wx != -1 && w_current->magnetic_wy != -1) { w_magnetic_halfsize = max (4 * size, gschem_page_view_WORLDabs (page_view, MAGNETIC_HALFSIZE)); eda_cairo_arc (cr, flags, size, w_current->magnetic_wx, w_current->magnetic_wy, w_magnetic_halfsize, 0, 360); } } /* Primary line */ eda_cairo_line (cr, flags, END_NONE, size, w_current->first_wx, w_current->first_wy, w_current->second_wx, w_current->second_wy); /* Secondary line */ eda_cairo_line (cr, flags, END_NONE, size, w_current->second_wx, w_current->second_wy, w_current->third_wx, w_current->third_wy); eda_cairo_stroke (cr, flags, TYPE_SOLID, END_NONE, size, -1, -1); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_text_prepare_place(GschemToplevel *w_current, char *text, int color, int align, int rotate, int size) { 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); if (page == NULL) { return; } TOPLEVEL *toplevel = page->toplevel; g_return_if_fail (toplevel != NULL); /* Insert the new object into the buffer at world coordinates (0,0). * It will be translated to the mouse coordinates during placement. */ w_current->first_wx = 0; w_current->first_wy = 0; w_current->last_drawb_mode = LAST_DRAWB_MODE_NONE; /* remove the old place list if it exists */ s_delete_object_glist(toplevel, page->place_list); page->place_list = NULL; /* here you need to add OBJ_TEXT when it's done */ page->place_list = g_list_append(page->place_list, o_text_new (toplevel, color, 0, 0, align, rotate, /* zero is angle */ text, size, /* has to be visible so you can place it */ /* visibility is set when you create the object */ VISIBLE, SHOW_NAME_VALUE)); i_action_start (w_current); i_set_state (w_current, TEXTMODE); }
/*! \brief Records a new point for the stroke. * \par Function Description * This function adds the point (<B>x</B>,<B>y</B>) as a new point in * the stroke. * * The footprint is updated and the new point is drawn on the drawing area. * * \param [in] w_current The GschemToplevel object. * \param [in] x The X coord of the new point. * \param [in] Y The X coord of the new point. */ void x_stroke_record (GschemToplevel *w_current, gint x, gint y) { GschemPageView *view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (view != NULL); GdkGC *view_gc = gschem_page_view_get_gc (view); g_assert (stroke_points != NULL); stroke_record (x, y); if (stroke_points->len < STROKE_MAX_POINTS) { StrokePoint point = { x, y }; g_array_append_val (stroke_points, point); gdk_gc_set_foreground (view_gc, x_get_color (STROKE_COLOR)); gdk_draw_point (gtk_widget_get_window (GTK_WIDGET(view)), view_gc, x, y); } }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ gboolean o_find_selected_object (GschemToplevel *w_current, int w_x, int w_y) { GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_val_if_fail (page_view != NULL, FALSE); TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); g_return_val_if_fail (toplevel != NULL, FALSE); int w_slack = gschem_page_view_WORLDabs (page_view, w_current->select_slack_pixels); GList *s_current; for (s_current = geda_list_get_glist (toplevel->page_current->selection_list); s_current != NULL; s_current = g_list_next (s_current)) { OBJECT *o_current = s_current->data; if (is_object_hit (w_current, o_current, w_x, w_y, w_slack)) return TRUE; } return FALSE; }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ static void a_zoom_box(GschemToplevel *w_current) { double zx, zy, relativ_zoom_factor; double world_pan_center_x, world_pan_center_y; 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); GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (page_view); g_return_if_fail (geometry != NULL); /*test if there is really a box*/ if (w_current->first_wx == w_current->second_wx || w_current->first_wy == w_current->second_wy) { s_log_message(_("Zoom too small! Cannot zoom further.\n")); return; } /*calc new zoomfactors and choose the smaller one*/ zx = (double) abs(geometry->viewport_left - geometry->viewport_right) / abs(w_current->first_wx - w_current->second_wx); zy = (double) abs(geometry->viewport_top - geometry->viewport_bottom) / abs(w_current->first_wy - w_current->second_wy); relativ_zoom_factor = (zx < zy ? zx : zy); /* calculate the center of the zoom box */ world_pan_center_x = (w_current->first_wx + w_current->second_wx) / 2.0; world_pan_center_y = (w_current->first_wy + w_current->second_wy) / 2.0; /* and create the new window*/ gschem_page_view_pan_general (page_view, world_pan_center_x, world_pan_center_y, relativ_zoom_factor); }
/*! \brief get the pointer position of a given GschemToplevel * \par Function Description * This function gets the pointer position of the drawing area of the * current workspace <b>GschemToplevel</b>. The flag <b>snapped</b> specifies * whether the pointer position should be snapped to the current grid. * * \param [in] w_current The GschemToplevel object. * \param [in] snapped An option flag to specify the wished coords * \param [out] wx snapped/unsnapped world x coordinate * \param [out] wy snapped/unsnapped world y coordinate * * \return Returns TRUE if the pointer position is inside the drawing area. * */ gboolean x_event_get_pointer_position (GschemToplevel *w_current, gboolean snapped, gint *wx, gint *wy) { int width; int height; int sx; int sy; int x; int y; GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_val_if_fail (page_view != NULL, FALSE); g_return_val_if_fail (GTK_WIDGET (page_view)->window != NULL, FALSE); /* \todo The following line is depricated in GDK 2.24 */ gdk_drawable_get_size (GTK_WIDGET (page_view)->window, &width, &height); gtk_widget_get_pointer(GTK_WIDGET (page_view), &sx, &sy); /* check if we are inside the drawing area */ if ((sx < 0) || (sx >= width) || (sy < 0) || (sy >= height)) { return FALSE; } gschem_page_view_SCREENtoWORLD (page_view, sx, sy, &x, &y); if (snapped) { x = snap_grid (w_current, x); y = snap_grid (w_current, y); } *wx = x; *wy = y; return TRUE; }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * * \note * The object passed in should be the REAL object, NOT any copy in any * selection list */ void o_text_change(GschemToplevel *w_current, OBJECT *object, char *string, int visibility, int show) { 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); TOPLEVEL *toplevel = page->toplevel; g_return_if_fail (toplevel != NULL); g_return_if_fail (page != NULL); if (object == NULL) { return; } if (object->type != OBJ_TEXT) { return; } o_text_set_string (toplevel, object, string); o_set_visibility (toplevel, object, visibility); object->show_name_value = show; o_text_recreate(toplevel, object); /* handle slot= attribute, it's a special case */ if (object->attached_to != NULL && g_ascii_strncasecmp (string, "slot=", 5) == 0) { o_slot_end (w_current, object->attached_to, string); } gschem_toplevel_page_content_changed (w_current, page); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_copy_start(GschemToplevel *w_current, int w_x, int w_y) { GList *s_current; 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); /* Copy the objects into the buffer at their current position, * with future motion relative to the mouse origin, (w_x, w_y). */ w_current->first_wx = w_x; w_current->first_wy = w_y; if (!o_select_selected (w_current)) return; s_current = geda_list_get_glist (page->selection_list); if (page->place_list != NULL) { s_delete_object_glist (page->toplevel, page->place_list); page->place_list = NULL; } page->place_list = o_glist_copy_all (page->toplevel, s_current, page->place_list); g_run_hook_object_list (w_current, "%copy-objects-hook", page->place_list); o_place_start (w_current, w_x, w_y); }
/*! \brief Check if a bounding box is visible on the screen. * \par Function Description * This function checks if a given bounding box is visible on the screen. * * WARNING: top and bottom are mis-named in world-coords, * top is the smallest "y" value, and bottom is the largest. * Be careful! This doesn't correspond to what you'd expect. * * \param [in] w_current The GschemToplevel object. * \param [in] wleft Left coordinate of the bounding box. * \param [in] wtop Top coordinate of the bounding box. * \param [in] wright Right coordinate of the bounding box. * \param [in] wbottom Bottom coordinate of the bounding box. * \return TRUE if bounding box is visible, FALSE otherwise */ int visible (GschemToplevel *w_current, int wleft, int wtop, int wright, int wbottom) { int visible=FALSE; GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (gschem_toplevel_get_current_page_view (w_current)); visible = clip_nochange (geometry, wleft, wtop, wright, wtop); #if DEBUG printf("vis1 %d\n", visible); #endif if (!visible) { visible = clip_nochange (geometry, wleft, wbottom, wright, wbottom); } else { return(visible); } #if DEBUG printf("vis2 %d\n", visible); #endif if (!visible) { visible = clip_nochange (geometry, wleft, wtop, wleft, wbottom); } else { return(visible); } #if DEBUG printf("vis3 %d\n", visible); #endif if (!visible) { visible = clip_nochange (geometry, wright, wtop, wright, wbottom); } else { return(visible); } #if DEBUG printf("vis4 %d\n", visible); #endif #if DEBUG printf("%d %d %d\n", wleft, geometry->viewport_top, wright); printf("%d %d %d\n", wtop, geometry->viewport_top, wbottom); printf("%d %d %d\n", wleft, geometry->viewport_right, wright); printf("%d %d %d\n", wtop, geometry->viewport_bottom, wbottom); #endif /* * now check to see if bounding box encompasses the entire viewport. * We only need to test if one point on the screen clipping boundary * is indide the bounding box of the object. */ if (geometry->viewport_left >= wleft && geometry->viewport_left <= wright && geometry->viewport_top >= wtop && geometry->viewport_top <= wbottom ) { visible = 1; } #if DEBUG printf("vis5 %d\n", visible); #endif return(visible); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_move_check_endpoint(GschemToplevel *w_current, OBJECT * object) { GList *cl_current; CONN *c_current; OBJECT *other; int whichone; 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); if (!object) return; if (object->type != OBJ_NET && object->type != OBJ_PIN && object->type != OBJ_BUS) { fprintf(stderr, _("Got a non line object in o_move_check_endpoint\n")); return; } for (cl_current = object->conn_list; cl_current != NULL; cl_current = g_list_next(cl_current)) { c_current = (CONN *) cl_current->data; other = c_current->other_object; if (other == NULL) continue; /* really make sure that the object is not selected */ if (other->selected) continue; /* Catch pins, whos parent object is selected. */ if (other->parent != NULL && other->parent->selected) continue; if (c_current->type != CONN_ENDPOINT && (c_current->type != CONN_MIDPOINT || c_current->other_whichone == -1)) continue; if (/* (net)pin to (net)pin contact */ (object->type == OBJ_PIN && object->pin_type == PIN_TYPE_NET && other->type == OBJ_PIN && other->pin_type == PIN_TYPE_NET)) { /* net to (net)pin contact */ /* (object->type == OBJ_NET && other->type == OBJ_PIN && other->pin_type == PIN_TYPE_NET) */ OBJECT *new_net; /* other object is a pin, insert a net */ new_net = o_net_new (page->toplevel, OBJ_NET, NET_COLOR, c_current->x, c_current->y, c_current->x, c_current->y); s_page_append (page->toplevel, page, new_net); /* This new net object is only picked up for stretching later, * somewhat of a kludge. If the move operation is cancelled, these * new 0 length nets are removed by the "undo" operation invoked. */ } /* Only attempt to stretch nets and buses */ if (other->type != OBJ_NET && other->type != OBJ_BUS) continue; whichone = o_move_return_whichone (other, c_current->x, c_current->y); #if DEBUG printf ("FOUND: %s type: %d, whichone: %d, x,y: %d %d\n", other->name, c_current->type, whichone, c_current->x, c_current->y); printf("other x,y: %d %d\n", c_current->x, c_current->y); printf("type: %d return: %d real: [ %d %d ]\n", c_current->type, whichone, c_current->whichone, c_current->other_whichone); #endif if (whichone >= 0 && whichone <= 1) { w_current->stretch_list = s_stretch_add (w_current->stretch_list, other, whichone); } } }
/*! \brief End the input of a box. * \par Function Description * This function ends the input of the second corner of a box. * The (<B>w_x</B>,<B>w_y</B>) point is set to be this second corner. The box is * then defined by (<B>w_current->first_wx</B>,<B>w_current->first_wy</B> and * (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>. * <B>w_x</B> and <B>w_y</B> are in screen unit. * * The temporary box is erased ; a new box object is allocated, initialized * and linked to the object list ; The object is finally drawn on the * current sheet. * * \param [in] w_current The GschemToplevel object. * \param [in] w_x Current x coordinate of pointer in world units. * \param [in] w_y Current y coordinate of pointer in world units. */ void o_box_end(GschemToplevel *w_current, int w_x, int w_y) { OBJECT *new_obj; GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (page_view != NULL); g_assert( w_current->inside_action != 0 ); PAGE *page = gschem_page_view_get_page (page_view); g_return_if_fail (page != NULL); TOPLEVEL *toplevel = page->toplevel; g_return_if_fail (toplevel != NULL); int box_width, box_height; int box_left, box_top; /* get the last coords of the pointer */ w_current->second_wx = w_x; w_current->second_wy = w_y; /* erase the temporary box */ /* o_box_invalidate_rubber (w_current); */ w_current->rubber_visible = 0; box_width = GET_BOX_WIDTH (w_current); box_height = GET_BOX_HEIGHT(w_current); box_left = GET_BOX_LEFT (w_current); box_top = GET_BOX_TOP (w_current); /* boxes with null width or height are not allowed */ if ((box_width == 0) || (box_height == 0)) { /* cancel the object creation */ w_current->first_wx = (-1); w_current->first_wy = (-1); w_current->second_wx = (-1); w_current->second_wy = (-1); } else { /* create the object */ new_obj = geda_box_object_new (toplevel, OBJ_BOX, GRAPHIC_COLOR, box_left, box_top, box_left + box_width, box_top - box_height); s_page_append (toplevel, page, new_obj); #if DEBUG printf("coords: %d %d %d %d\n", box_left, box_top, box_width, box_height); #endif w_current->first_wx = (-1); w_current->first_wy = (-1); w_current->second_wx = (-1); w_current->second_wy = (-1); /* Call add-objects-hook */ g_run_hook_object (w_current, "%add-objects-hook", new_obj); gschem_toplevel_page_content_changed (w_current, page); o_undo_savestate(w_current, page, UNDO_ALL); } i_action_stop (w_current); }
/*! \brief End placement action * * \par Function Description * This function finishes the current placement action by adding * objects to the current page at the given new world coordinates * and redrawing them on the canvas. It also saves the current * state in the undo list and updates the menus. * * If \a continue_placing is TRUE, a copy of the placement list * is saved to start a new place action. * * \param [in] w_current GschemToplevel which we're drawing for. * \param [in] w_x The current world X coordinate. * \param [in] w_y The current world Y coordinate. * \param [in] hook_name The hook to run after adding the objects. */ void o_place_end (GschemToplevel *w_current, int w_x, int w_y, int continue_placing, const char* hook_name) { int w_diff_x, w_diff_y; OBJECT *o_current; GList *temp_dest_list = NULL; GList *connected_objects = NULL; GList *iter; g_return_if_fail (w_current != NULL); g_assert (w_current->inside_action != 0); 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); /* erase old image */ /* o_place_invalidate_rubber (w_current, FALSE); */ w_current->rubber_visible = 0; /* Calc final object positions */ w_current->second_wx = w_x; w_current->second_wy = w_y; w_diff_x = w_current->second_wx - w_current->first_wx; w_diff_y = w_current->second_wy - w_current->first_wy; if (continue_placing) { /* Make a copy of the place list if we want to keep it afterwards */ temp_dest_list = o_glist_copy_all (page->toplevel, page->place_list, temp_dest_list); } else { /* Otherwise just take it */ temp_dest_list = page->place_list; page->place_list = NULL; } geda_object_list_translate (temp_dest_list, w_diff_x, w_diff_y); /* Attach each item back onto the page's object list. Update object * connectivity and add the new objects to the selection list.*/ for (iter = temp_dest_list; iter != NULL; iter = g_list_next (iter)) { o_current = (OBJECT*) iter->data; s_page_append (page->toplevel, page, o_current); /* Update object connectivity */ s_conn_update_object (page, o_current); connected_objects = s_conn_return_others (connected_objects, o_current); } if (hook_name != NULL) { g_run_hook_object_list (w_current, hook_name, temp_dest_list); } o_invalidate_glist (w_current, connected_objects); g_list_free (connected_objects); connected_objects = NULL; gschem_toplevel_page_content_changed (w_current, page); o_invalidate_glist (w_current, temp_dest_list); /* only redraw new objects */ g_list_free (temp_dest_list); o_undo_savestate_old (w_current, UNDO_ALL); i_update_menus (w_current); if (!continue_placing) { i_set_state(w_current, SELECT); i_action_stop (w_current); } }
/*! \brief Opens a new page from a file. * \par Function Description * This function opens the file whose name is <B>filename</B> in a * new PAGE of <B>toplevel</B>. * * If there is no page for <B>filename</B> in <B>toplevel</B>'s list * of pages, it creates a new PAGE, loads the file in it and returns * a pointer on the new page. Otherwise it returns a pointer on the * existing page. * * If the filename passed is NULL, this function creates an empty, * untitled page. The name of the untitled page is build from * configuration data ('untitled-name') and a counter for uniqueness. * * The opened page becomes the current page of <B>toplevel</B>. * * \param [in] w_current The toplevel environment. * \param [in] filename The name of the file to open or NULL for a blank page. * \returns A pointer on the new page. * * \bug This code should check to make sure any untitled filename * does not conflict with a file on disk. */ PAGE* x_window_open_page (GschemToplevel *w_current, const gchar *filename) { TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); PAGE *page; gchar *fn; g_return_val_if_fail (toplevel != NULL, NULL); /* Generate untitled filename if none was specified */ if (filename == NULL) { gchar *cwd, *tmp, *untitled_name; EdaConfig *cfg; cwd = g_get_current_dir (); cfg = eda_config_get_context_for_path (cwd); untitled_name = eda_config_get_string (cfg, "gschem", "default-filename", NULL); tmp = g_strdup_printf ("%s_%d.sch", untitled_name, ++w_current->num_untitled); fn = g_build_filename (cwd, tmp, NULL); g_free (untitled_name); g_free(cwd); g_free(tmp); } else { fn = g_strdup (filename); } /* Return existing page if it is already loaded */ page = s_page_search (toplevel, fn); if ( page != NULL ) { g_free(fn); return page; } page = s_page_new (toplevel, fn); s_page_goto (toplevel, page); gschem_toplevel_page_changed (w_current); /* Load from file if necessary, otherwise just print a message */ if (filename != NULL) { GError *err = NULL; if (!quiet_mode) s_log_message (_("Loading schematic [%s]\n"), fn); if (!f_open (toplevel, page, (gchar *) fn, &err)) { GtkWidget *dialog; g_warning ("%s\n", err->message); dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (w_current->main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("<b>An error occurred while loading the requested file.</b>\n\nLoading from '%s' failed: %s. The gschem log may contain more information."), fn, err->message); gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to load file")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_error_free (err); } else { gtk_recent_manager_add_item (recent_manager, g_filename_to_uri(fn, NULL, NULL)); } } else { if (!quiet_mode) s_log_message (_("New file [%s]\n"), toplevel->page_current->page_filename); g_run_hook_page (w_current, "%new-page-hook", toplevel->page_current); } gschem_page_view_set_page (page_view, toplevel->page_current); o_undo_savestate (w_current, toplevel->page_current, UNDO_ALL); /* This line is generally un-needed, however if some code * wants to open a page, yet not bring it to the front, it is * needed needed to add it into the page manager. Otherwise, * it will get done in x_window_set_current_page(...) */ x_pagesel_update (w_current); /* ??? */ g_free (fn); return page; }
/*! \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); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * * <B>type</B> can be one of the following values: * <DL> * <DT>*</DT><DD>UNDO_ACTION * <DT>*</DT><DD>REDO_ACTION * </DL> */ void o_undo_callback (GschemToplevel *w_current, PAGE *page, int type) { TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); UNDO *u_current; UNDO *u_next; UNDO *save_bottom; UNDO *save_tos; UNDO *save_current; int save_logging; int find_prev_data=FALSE; char *save_filename; g_return_if_fail (w_current != NULL); g_return_if_fail (page != NULL); if (w_current->undo_control == FALSE) { s_log_message(_("Undo/Redo disabled in rc file\n")); return; } if (page->undo_current == NULL) { return; } if (type == UNDO_ACTION) { u_current = page->undo_current->prev; } else { u_current = page->undo_current->next; } u_next = page->undo_current; if (u_current == NULL) { return; } if (u_next->type == UNDO_ALL && u_current->type == UNDO_VIEWPORT_ONLY) { #if DEBUG printf("Type: %d\n", u_current->type); printf("Current is an undo all, next is viewport only!\n"); #endif find_prev_data = TRUE; if (w_current->undo_type == UNDO_DISK) { u_current->filename = o_undo_find_prev_filename(u_current); } else { u_current->object_list = o_undo_find_prev_object_head (u_current); } } /* save filename */ save_filename = g_strdup (page->page_filename); /* save structure so it's not nuked */ save_bottom = page->undo_bottom; save_tos = page->undo_tos; save_current = page->undo_current; page->undo_bottom = NULL; page->undo_tos = NULL; page->undo_current = NULL; o_select_unselect_all (w_current); if (w_current->undo_type == UNDO_DISK && u_current->filename) { /* delete objects of page */ s_page_delete_objects (toplevel, page); /* Free the objects in the place list. */ geda_object_list_delete (toplevel, page->place_list); page->place_list = NULL; gschem_toplevel_page_content_changed (w_current, page); } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) { /* delete objects of page */ s_page_delete_objects (toplevel, page); /* Free the objects in the place list. */ geda_object_list_delete (toplevel, page->place_list); page->place_list = NULL; gschem_toplevel_page_content_changed (w_current, page); } /* temporarily disable logging */ save_logging = do_logging; do_logging = FALSE; if (w_current->undo_type == UNDO_DISK && u_current->filename) { f_open(toplevel, page, u_current->filename, NULL); } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) { s_page_append_list (toplevel, page, o_glist_copy_all (toplevel, u_current->object_list, NULL)); } page->page_control = u_current->page_control; page->up = u_current->up; gschem_toplevel_page_content_changed (w_current, page); GschemPageView *view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (view != NULL); GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view); if (u_current->scale != 0) { gschem_page_geometry_set_viewport (geometry, u_current->x, u_current->y, u_current->scale); gschem_page_view_invalidate_all (view); } else { gschem_page_view_zoom_extents (view, u_current->object_list); } /* restore logging */ do_logging = save_logging; /* set filename right */ g_free(page->page_filename); page->page_filename = save_filename; /* final redraw */ x_pagesel_update (w_current); x_multiattrib_update (w_current); i_update_menus(w_current); /* restore saved undo structures */ page->undo_bottom = save_bottom; page->undo_tos = save_tos; page->undo_current = save_current; if (type == UNDO_ACTION) { if (page->undo_current) { page->undo_current = page->undo_current->prev; if (page->undo_current == NULL) { page->undo_current = page->undo_bottom; } } } else { /* type is REDO_ACTION */ if (page->undo_current) { page->undo_current = page->undo_current->next; if (page->undo_current == NULL) { page->undo_current = page->undo_tos; } } } /* don't have to free data here since filename, object_list are */ /* just pointers to the real data (lower in the stack) */ if (find_prev_data) { u_current->filename = NULL; u_current->object_list = NULL; } #if DEBUG printf("\n\n---Undo----\n"); s_undo_print_all(page->undo_bottom); printf("TOS: %s\n", page->undo_tos->filename); printf("CURRENT: %s\n", page->undo_current->filename); printf("----\n"); #endif }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * * * <B>flag</B> can be one of the following values: * <DL> * <DT>*</DT><DD>UNDO_ALL * <DT>*</DT><DD>UNDO_VIEWPORT_ONLY * </DL> */ void o_undo_savestate (GschemToplevel *w_current, PAGE *page, int flag) { TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); char *filename = NULL; GList *object_list = NULL; int levels; UNDO *u_current; UNDO *u_current_next; GschemPageView *view = gschem_toplevel_get_current_page_view (w_current); g_return_if_fail (view != NULL); g_return_if_fail (page != NULL); GschemPageGeometry *geometry = gschem_page_view_get_page_geometry (view); /* save autosave backups if necessary */ o_autosave_backups(w_current); if (w_current->undo_control == FALSE) { return; } if (flag == UNDO_ALL) { /* Increment the number of operations since last backup if auto-save is enabled */ if (toplevel->auto_save_interval != 0) { page->ops_since_last_backup++; } /* HACK */ /* Before we save the undo state, consolidate nets as necessary */ /* This is where the net consolidation call would have been * triggered before it was removed from o_save_buffer(). */ if (toplevel->net_consolidate == TRUE) geda_net_object_consolidate (toplevel, page); } if (w_current->undo_type == UNDO_DISK && flag == UNDO_ALL) { filename = g_strdup_printf("%s%cgschem.save%d_%d.sch", tmp_path, G_DIR_SEPARATOR, prog_pid, undo_file_index++); /* Changed from f_save to o_save when adding backup copy creation. */ /* f_save manages the creaton of backup copies. This way, f_save is called only when saving a file, and not when saving an undo backup copy */ o_save (toplevel, s_page_objects (page), filename, NULL); } else if (w_current->undo_type == UNDO_MEMORY && flag == UNDO_ALL) { object_list = o_glist_copy_all (toplevel, s_page_objects (page), object_list); } /* Clear Anything above current */ if (page->undo_current) { s_undo_remove_rest(toplevel, page->undo_current->next); page->undo_current->next = NULL; } else { /* undo current is NULL */ s_undo_remove_rest(toplevel, page->undo_bottom); page->undo_bottom = NULL; } page->undo_tos = page->undo_current; if (geometry != NULL) { page->undo_tos = s_undo_add(page->undo_tos, flag, filename, object_list, (geometry->viewport_left + geometry->viewport_right) / 2, (geometry->viewport_top + geometry->viewport_bottom) / 2, /* scale */ max (((double) abs (geometry->viewport_right - geometry->viewport_left) / geometry->screen_width), ((double) abs (geometry->viewport_top - geometry->viewport_bottom) / geometry->screen_height)), page->page_control, page->up); } else { page->undo_tos = s_undo_add(page->undo_tos, flag, filename, object_list, 0, /* center x */ 0, /* center y */ 0, /* scale */ page->page_control, page->up); } page->undo_current = page->undo_tos; if (page->undo_bottom == NULL) { page->undo_bottom = page->undo_tos; } #if DEBUG printf("\n\n---Undo----\n"); s_undo_print_all(page->undo_bottom); printf("BOTTOM: %s\n", page->undo_bottom->filename); printf("TOS: %s\n", page->undo_tos->filename); printf("CURRENT: %s\n", page->undo_current->filename); printf("----\n"); #endif g_free(filename); /* Now go through and see if we need to free/remove some undo levels */ /* so we stay within the limits */ /* only check history every 10 undo savestates */ if (undo_file_index % 10) { return; } levels = s_undo_levels(page->undo_bottom); #if DEBUG printf("levels: %d\n", levels); #endif if (levels >= w_current->undo_levels + UNDO_PADDING) { levels = levels - w_current->undo_levels; #if DEBUG printf("Trimming: %d levels\n", levels); #endif u_current = page->undo_bottom; while (levels > 0) { /* Because we use a pad you are always guaranteed to never */ /* exhaust the list */ g_assert (u_current != NULL); u_current_next = u_current->next; if (u_current->filename) { #if DEBUG printf("Freeing: %s\n", u_current->filename); #endif unlink(u_current->filename); g_free(u_current->filename); } if (u_current->object_list) { geda_object_list_delete (toplevel, u_current->object_list); u_current->object_list = NULL; } u_current->next = NULL; u_current->prev = NULL; g_free(u_current); u_current = u_current_next; levels--; } g_assert (u_current != NULL); u_current->prev = NULL; page->undo_bottom = u_current; #if DEBUG printf("New current is: %s\n", u_current->filename); #endif } #if DEBUG printf("\n\n---Undo----\n"); s_undo_print_all(page->undo_bottom); printf("BOTTOM: %s\n", page->undo_bottom->filename); printf("TOS: %s\n", page->undo_tos->filename); printf("CURRENT: %s\n", page->undo_current->filename); printf("----\n"); #endif }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ int o_net_add_busrippers(GschemToplevel *w_current, OBJECT *net_obj, GList *prev_conn_objects) { OBJECT *new_obj; GList *cl_current = NULL; OBJECT *bus_object = NULL; CONN *found_conn = NULL; int done; int otherone; BUS_RIPPER rippers[2]; int ripper_count = 0; int i; double length; int sign; double distance1, distance2; int first, second; int made_changes = FALSE; const int ripper_size = w_current->bus_ripper_size; int complex_angle = 0; const CLibSymbol *rippersym = NULL; GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current); g_return_val_if_fail (page_view != NULL, FALSE); PAGE *page = gschem_page_view_get_page (page_view); g_return_val_if_fail (page != NULL, FALSE); length = o_line_length(net_obj); if (!prev_conn_objects) { return(FALSE); } if (length <= ripper_size) { return(FALSE); } /* check for a bus connection and draw rippers if so */ cl_current = prev_conn_objects; while (cl_current != NULL) { bus_object = (OBJECT *) cl_current->data; if (bus_object && bus_object->type == OBJ_BUS) { /* yes, using the net routine is okay */ int bus_orientation = o_net_orientation(bus_object); int net_orientation = o_net_orientation(net_obj); /* find the CONN structure which is associated with this object */ GList *cl_current2 = net_obj->conn_list; done = FALSE; while (cl_current2 != NULL && !done) { CONN *tmp_conn = (CONN *) cl_current2->data; if (tmp_conn && tmp_conn->other_object && tmp_conn->other_object == bus_object) { found_conn = tmp_conn; done = TRUE; } cl_current2 = g_list_next(cl_current2); } if (!found_conn) { return(FALSE); } otherone = !found_conn->whichone; /* now deal with the found connection */ if (bus_orientation == HORIZONTAL && net_orientation == VERTICAL) { /* printf("found horiz bus %s %d!\n", bus_object->name, found_conn->whichone);*/ sign = bus_object->bus_ripper_direction; if (!sign) { if (bus_object->line->x[0] < bus_object->line->x[1]) { first = 0; second = 1; } else { first = 1; second = 0; } distance1 = abs(bus_object->line->x[first] - net_obj->line->x[found_conn->whichone]); distance2 = abs(bus_object->line->x[second] - net_obj->line->x[found_conn->whichone]); if (distance1 <= distance2) { sign = 1; } else { sign = -1; } bus_object->bus_ripper_direction = sign; } /* printf("hor sign: %d\n", sign); */ if (net_obj->line->y[otherone] < bus_object->line->y[0]) { /* new net is below bus */ /*printf("below\n");*/ if (ripper_count >= 2) { /* try to exit gracefully */ fprintf(stderr, _("Tried to add more than two bus rippers. Internal gschem error.\n")); made_changes = FALSE; break; } if (w_current->bus_ripper_rotation == NON_SYMMETRIC) { /* non-symmetric */ if (sign == 1) { complex_angle = 0; } else { complex_angle = 90; } } else { /* symmetric */ complex_angle = 0; } net_obj->line->y[found_conn->whichone] -= ripper_size; net_obj->w_bounds_valid_for = NULL; rippers[ripper_count].x[0] = net_obj->line->x[found_conn->whichone]; rippers[ripper_count].y[0] = net_obj->line->y[found_conn->whichone]; rippers[ripper_count].x[1] = net_obj->line->x[found_conn->whichone] + sign*ripper_size; rippers[ripper_count].y[1] = net_obj->line->y[found_conn->whichone] + ripper_size; ripper_count++; /* printf("done\n"); */ made_changes++; } else { /* new net is above bus */ /* printf("above\n"); */ if (ripper_count >= 2) { /* try to exit gracefully */ fprintf(stderr, _("Tried to add more than two bus rippers. Internal gschem error.\n")); made_changes = FALSE; break; } if (w_current->bus_ripper_rotation == NON_SYMMETRIC) { /* non-symmetric */ if (sign == 1) { complex_angle = 270; } else { complex_angle = 180; } } else { /* symmetric */ complex_angle = 180; } net_obj->line->y[found_conn->whichone] += ripper_size; net_obj->w_bounds_valid_for = NULL; rippers[ripper_count].x[0] = net_obj->line->x[found_conn->whichone]; rippers[ripper_count].y[0] = net_obj->line->y[found_conn->whichone]; rippers[ripper_count].x[1] = net_obj->line->x[found_conn->whichone] + sign*ripper_size; rippers[ripper_count].y[1] = net_obj->line->y[found_conn->whichone] - ripper_size; ripper_count++; /* printf("done\n"); */ made_changes++; } } else if (bus_orientation == VERTICAL && net_orientation == HORIZONTAL) { /* printf("found vert bus %s %d!\n", bus_object->name, found_conn->whichone); */ sign = bus_object->bus_ripper_direction; if (!sign) { if (bus_object->line->y[0] < bus_object->line->y[1]) { first = 0; second = 1; } else { first = 1; second = 0; } distance1 = abs(bus_object->line->y[first] - net_obj->line->y[found_conn->whichone]); distance2 = abs(bus_object->line->y[second] - net_obj->line->y[found_conn->whichone]); if (distance1 <= distance2) { sign = 1; } else { sign = -1; } bus_object->bus_ripper_direction = sign; } /* printf("ver sign: %d\n", sign); */ if (net_obj->line->x[otherone] < bus_object->line->x[0]) { /* new net is to the left of the bus */ /* printf("left\n"); */ if (ripper_count >= 2) { /* try to exit gracefully */ fprintf(stderr, _("Tried to add more than two bus rippers. Internal gschem error.\n")); made_changes = FALSE; break; } if (w_current->bus_ripper_rotation == NON_SYMMETRIC) { /* non-symmetric */ if (sign == 1) { complex_angle = 0; } else { complex_angle = 270; } } else { /* symmetric */ complex_angle = 270; } net_obj->line->x[found_conn->whichone] -= ripper_size; net_obj->w_bounds_valid_for = NULL; rippers[ripper_count].x[0] = net_obj->line->x[found_conn->whichone]; rippers[ripper_count].y[0] = net_obj->line->y[found_conn->whichone]; rippers[ripper_count].x[1] = net_obj->line->x[found_conn->whichone] + ripper_size; rippers[ripper_count].y[1] = net_obj->line->y[found_conn->whichone] + sign*ripper_size; ripper_count++; made_changes++; } else { /* new net is to the right of the bus */ /* printf("right\n"); */ if (ripper_count >= 2) { /* try to exit gracefully */ fprintf(stderr, _("Tried to add more than two bus rippers. Internal gschem error.\n")); made_changes = FALSE; break; } if (w_current->bus_ripper_rotation == NON_SYMMETRIC) { /* non-symmetric */ if (sign == 1) { complex_angle = 90; } else { complex_angle = 180; } } else { /* symmetric */ complex_angle = 90; } net_obj->line->x[found_conn->whichone] += ripper_size; net_obj->w_bounds_valid_for = NULL; rippers[ripper_count].x[0] = net_obj->line->x[found_conn->whichone]; rippers[ripper_count].y[0] = net_obj->line->y[found_conn->whichone]; rippers[ripper_count].x[1] = net_obj->line->x[found_conn->whichone] - ripper_size; rippers[ripper_count].y[1] = net_obj->line->y[found_conn->whichone] + sign*ripper_size; ripper_count++; made_changes++; } } } cl_current = g_list_next(cl_current); } if (made_changes) { s_conn_remove_object_connections (page->toplevel, net_obj); if (w_current->bus_ripper_type == COMP_BUS_RIPPER) { GList *symlist = s_clib_search (page->toplevel->bus_ripper_symname, CLIB_EXACT); if (symlist != NULL) { rippersym = (CLibSymbol *) symlist->data; } g_list_free (symlist); } for (i = 0; i < ripper_count; i++) { if (w_current->bus_ripper_type == NET_BUS_RIPPER) { new_obj = o_net_new(page->toplevel, OBJ_NET, NET_COLOR, rippers[i].x[0], rippers[i].y[0], rippers[i].x[1], rippers[i].y[1]); s_page_append (page->toplevel, page, new_obj); } else { if (rippersym != NULL) { new_obj = o_complex_new (page->toplevel, OBJ_COMPLEX, DEFAULT_COLOR, rippers[i].x[0], rippers[i].y[0], complex_angle, 0, rippersym, page->toplevel->bus_ripper_symname, 1); s_page_append_list (page->toplevel, page, o_complex_promote_attribs (page->toplevel, new_obj)); s_page_append (page->toplevel, page, new_obj); } else { s_log_message(_("Bus ripper symbol [%s] was not found in any component library\n"), page->toplevel->bus_ripper_symname); } } } s_conn_update_object (page, net_obj); return(TRUE); } return(FALSE); }
/*! \brief guess the best direction for the next net drawing action * \par Function Description * This function checks all connectable objects at a starting point. * It determines the best drawing direction for each quadrant of the * possible net endpoint. * * The directions are stored in the GschemToplevel->net_direction variable * as a bitfield. */ void o_net_guess_direction(GschemToplevel *w_current, int wx, int wy) { int up=0, down=0, left=0, right=0; int x1, y1, x2, y2; int xmin, ymin, xmax, ymax; int orientation; GList *object_list, *iter1, *iter2; OBJECT *o_current; int *current_rules; /* badness values {OVERWRITE, ORTHO, CONTINUE} */ const int pin_rules[] = {100, 50, 0}; const int bus_rules[] = {90, 0, 40}; const int net_rules[] = {80, 30, 0}; 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); object_list = g_list_append (NULL, page->connectible_list); for (iter1 = object_list; iter1 != NULL; iter1 = g_list_next(iter1)) { for (iter2 = (GList*) iter1->data; iter2 != NULL; iter2 = g_list_next(iter2)) { o_current = (OBJECT*) iter2->data; if ((orientation = o_net_orientation(o_current)) == NEITHER) continue; switch (o_current->type) { case OBJ_NET: current_rules = (int*) net_rules; break; case OBJ_PIN: current_rules = (int*) pin_rules; break; case OBJ_BUS: current_rules = (int*) bus_rules; break; default: g_assert_not_reached (); } x1 = o_current->line->x[0]; x2 = o_current->line->x[1]; y1 = o_current->line->y[0]; y2 = o_current->line->y[1]; xmin = min(x1, x2); ymin = min(y1, y2); xmax = max(x1, x2); ymax = max(y1, y2); if (orientation == HORIZONTAL && wy == y1) { if (wx == xmin) { up = max(up, current_rules[1]); down = max(down, current_rules[1]); right = max(right, current_rules[0]); left = max(left, current_rules[2]); } else if (wx == xmax) { up = max(up, current_rules[1]); down = max(down, current_rules[1]); right = max(right, current_rules[2]); left = max(left, current_rules[0]); } else if (xmin < wx && wx < xmax) { up = max(up, current_rules[1]); down = max(down, current_rules[1]); right = max(right, current_rules[0]); left = max(left, current_rules[0]); } else { continue; } } if (orientation == VERTICAL && wx == x1) { if (wy == ymin) { up = max(up, current_rules[0]); down = max(down, current_rules[2]); right = max(right, current_rules[1]); left = max(left, current_rules[1]); } else if (wy == ymax) { up = max(up, current_rules[2]); down = max(down, current_rules[0]); right = max(right, current_rules[1]); left = max(left, current_rules[1]); } else if (ymin < wy && wy < ymax) { up = max(up, current_rules[0]); down = max(down, current_rules[0]); right = max(right, current_rules[1]); left = max(left, current_rules[1]); } else { continue; } } } } w_current->net_direction = 0; w_current->net_direction |= up >= right ? 0 : QUADRANT1; w_current->net_direction |= up >= left ? 0 : QUADRANT2; w_current->net_direction |= down >= left ? 0 : QUADRANT3; w_current->net_direction |= down >= right ? 0 : QUADRANT4; #if 0 printf("o_net_guess_direction: up=%d down=%d left=%d right=%d direction=%d\n", up, down, left, right, w_current->net_direction); #endif g_list_free (object_list); }
/*! \brief find the closest possible location to connect to * \par Function Description * This function calculates the distance to all connectable objects * and searches the closest connection point. * It searches for pins, nets and busses. * * The connection point is stored in GschemToplevel->magnetic_wx and * GschemToplevel->magnetic_wy. If no connection is found. Both variables * are set to -1. */ void o_net_find_magnetic(GschemToplevel *w_current, int w_x, int w_y) { int x1, x2, y1, y2, min_x, min_y; double mindist, minbest, dist1, dist2; double weight, min_weight; int magnetic_reach = 0; OBJECT *o_current; OBJECT *o_magnetic = NULL; GList *object_list, *iter1, *iter2; 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); minbest = min_x = min_y = 0; min_weight = 0; /* max distance of all the different reaches */ magnetic_reach = max(MAGNETIC_PIN_REACH, MAGNETIC_NET_REACH); magnetic_reach = max(magnetic_reach, MAGNETIC_BUS_REACH); object_list = g_list_append (NULL, page->connectible_list); for (iter1 = object_list; iter1 != NULL; iter1 = g_list_next(iter1)) { for (iter2 = (GList*) iter1->data; iter2 != NULL; iter2 = g_list_next(iter2)) { int left, top, right, bottom; o_current = (OBJECT*) iter2->data; if (!world_get_single_object_bounds(page->toplevel, o_current, &left, &top, &right, &bottom) || !visible (w_current, left, top, right, bottom)) continue; /* skip invisible objects */ if (o_current->type == OBJ_PIN) { min_x = o_current->line->x[o_current->whichend]; min_y = o_current->line->y[o_current->whichend]; mindist = hypot(w_x - min_x, w_y - min_y); weight = mindist / MAGNETIC_PIN_WEIGHT; } else if (o_current->type == OBJ_NET || o_current->type == OBJ_BUS) { /* we have 3 possible points to connect: 2 endpoints and 1 midpoint point */ x1 = o_current->line->x[0]; y1 = o_current->line->y[0]; x2 = o_current->line->x[1]; y2 = o_current->line->y[1]; /* endpoint tests */ dist1 = hypot(w_x - x1, w_y - y1); dist2 = hypot(w_x - x2, w_y - y2); if (dist1 < dist2) { min_x = x1; min_y = y1; mindist = dist1; } else { min_x = x2; min_y = y2; mindist = dist2; } /* midpoint tests */ if ((x1 == x2) /* vertical net */ && ((y1 >= w_y && w_y >= y2) || (y2 >= w_y && w_y >= y1))) { if (abs(w_x - x1) < mindist) { mindist = abs(w_x - x1); min_x = x1; min_y = w_y; } } if ((y1 == y2) /* horitontal net */ && ((x1 >= w_x && w_x >= x2) || (x2 >= w_x && w_x >= x1))) { if (abs(w_y - y1) < mindist) { mindist = abs(w_y - y1); min_x = w_x; min_y = y1; } } if (o_current->type == OBJ_BUS) weight = mindist / MAGNETIC_BUS_WEIGHT; else /* OBJ_NET */ weight = mindist / MAGNETIC_NET_WEIGHT; } else { /* neither pin nor net or bus */ continue; } if (o_magnetic == NULL || weight < min_weight) { minbest = mindist; min_weight = weight; o_magnetic = o_current; w_current->magnetic_wx = min_x; w_current->magnetic_wy = min_y; } } } /* check whether we found an object and if it's close enough */ if (o_magnetic != NULL) { switch (o_magnetic->type) { case (OBJ_PIN): magnetic_reach = MAGNETIC_PIN_REACH; break; case (OBJ_NET): magnetic_reach = MAGNETIC_NET_REACH; break; case (OBJ_BUS): magnetic_reach = MAGNETIC_BUS_REACH; break; } if (minbest > gschem_page_view_WORLDabs (page_view, magnetic_reach)) { w_current->magnetic_wx = -1; w_current->magnetic_wy = -1; } } else { w_current->magnetic_wx = -1; w_current->magnetic_wy = -1; } g_list_free (object_list); }
/*! \brief finish a net drawing action * \par Function Description * This function finishes the drawing of a net. If we have a visible * magnetic marker, we use that instead of the current cursor * position. * * The rubber nets are removed, the nets and cues are drawn and the * net is added to the TOPLEVEL structure. * * The function returns TRUE if it has drawn a net, FALSE otherwise. */ void o_net_end(GschemToplevel *w_current, int w_x, int w_y) { int primary_zero_length, secondary_zero_length; int found_primary_connection = FALSE; int save_wx, save_wy; GList *prev_conn_objects; OBJECT *new_net = NULL; /* Save a list of added objects to run the %add-objects-hook later */ GList *added_objects = NULL; g_assert( w_current->inside_action != 0 ); 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); o_net_invalidate_rubber (w_current); if (w_current->magnetic_wx != -1 && w_current->magnetic_wy != -1) o_net_finishmagnetic(w_current); w_current->rubber_visible = 0; /* See if either of the nets are zero length. We'll only add */ /* the non-zero ones */ primary_zero_length = (w_current->first_wx == w_current->second_wx) && (w_current->first_wy == w_current->second_wy); secondary_zero_length = (w_current->second_wx == w_current->third_wx) && (w_current->second_wy == w_current->third_wy); /* If both nets are zero length... */ /* this ends the net drawing behavior */ if ( primary_zero_length && secondary_zero_length ) { o_net_reset(w_current); return; } save_wx = w_current->third_wx; save_wy = w_current->third_wy; if (w_current->third_wx != snap_grid (w_current, w_current->third_wx) || w_current->third_wy != snap_grid (w_current, w_current->third_wy)) s_log_message(_("Warning: Ending net at off grid coordinate\n")); if (!primary_zero_length ) { /* create primary net */ new_net = o_net_new(page->toplevel, OBJ_NET, NET_COLOR, w_current->first_wx, w_current->first_wy, w_current->second_wx, w_current->second_wy); s_page_append (page->toplevel, page, new_net); added_objects = g_list_prepend (added_objects, new_net); /* conn stuff */ /* LEAK CHECK 1 */ prev_conn_objects = s_conn_return_others (NULL, new_net); o_net_add_busrippers (w_current, new_net, prev_conn_objects); g_list_free (prev_conn_objects); #if DEBUG printf("primary:\n"); s_conn_print(new_net->conn_list); #endif /* Go off and search for valid connection on this newly created net */ found_primary_connection = s_conn_net_search(new_net, 1, new_net->conn_list); if (found_primary_connection) { /* if a net connection is found, reset start point of next net */ save_wx = w_current->second_wx; save_wy = w_current->second_wy; } } /* If the second net is not zero length, add it as well */ /* Also, a valid net connection from the primary net was not found */ if (!secondary_zero_length && !found_primary_connection) { /* Add secondary net */ new_net = o_net_new(page->toplevel, OBJ_NET, NET_COLOR, w_current->second_wx, w_current->second_wy, w_current->third_wx, w_current->third_wy); s_page_append (page->toplevel, page, new_net); added_objects = g_list_prepend (added_objects, new_net); /* conn stuff */ /* LEAK CHECK 2 */ prev_conn_objects = s_conn_return_others (NULL, new_net); o_net_add_busrippers (w_current, new_net, prev_conn_objects); g_list_free (prev_conn_objects); #if DEBUG s_conn_print(new_net->conn_list); #endif } /* Call add-objects-hook */ if (added_objects != NULL) { g_run_hook_object_list (w_current, "%add-objects-hook", added_objects); g_list_free (added_objects); } w_current->first_wx = save_wx; w_current->first_wy = save_wy; gschem_toplevel_page_content_changed (w_current, page); o_undo_savestate_old(w_current, UNDO_ALL); /* Continue net drawing */ o_net_start(w_current, w_current->first_wx, w_current->first_wy); }