/*! \brief Find the closest grid coordinate. * \par Function Description * This function snaps the current input coordinate to the * closest grid coordinate. * * \param [in] w_current The GschemToplevel object. * \param [in] input The coordinate to snap. * \return The closest grid coordinate to the input. */ int snap_grid(GschemToplevel *w_current, int input) { int p, m, n; int sign, value, snap_size; SNAP_STATE snap_mode; snap_mode = gschem_options_get_snap_mode (w_current->options); snap_size = gschem_options_get_snap_size (w_current->options); if (snap_mode == SNAP_OFF) { return(input); } /* this code was inspired from killustrator, it's much simpler than mine */ sign = ( input < 0 ? -1 : 1 ); value = abs(input); p = value / snap_size; m = value % snap_size; n = p * snap_size; if (m > snap_size / 2) n += snap_size; #if DEBUG printf("p: %d\n", p); printf("m: %d\n", m); printf("m > snap_size / 2: %d\n", (m > snap_size / 2)); printf("n: %d\n", n); printf("n*s: %d\n", n*sign); #endif return(sign*n); }
/*! \private * \brief Get a property * * \param [in] object * \param [in] param_id * \param [in,out] value * \param [in] pspec */ static void get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GschemOptions *options = GSCHEM_OPTIONS (object); switch (param_id) { case PROP_GRID_MODE: g_value_set_int (value, gschem_options_get_grid_mode (options)); break; case PROP_MAGNETIC_NET_MODE: g_value_set_boolean (value, gschem_options_get_magnetic_net_mode (options)); break; case PROP_NET_RUBBER_BAND_MODE: g_value_set_boolean (value, gschem_options_get_net_rubber_band_mode (options)); break; case PROP_SNAP_MODE: g_value_set_int (value, gschem_options_get_snap_mode (options)); break; case PROP_SNAP_SIZE: g_value_set_int (value, gschem_options_get_snap_size (options)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); } }
/*! \private * \brief Update the snap mode widget with the current value * * \param [in,out] widget This widget */ static void update_snap_mode_widget (GschemOptionsWidget *widget) { g_return_if_fail (widget != NULL); if (widget->options != NULL) { int index; SNAP_STATE snap_mode; snap_mode = gschem_options_get_snap_mode (widget->options); for (index=0; index<SNAP_STATE_COUNT; index++) { g_signal_handlers_block_by_func (G_OBJECT (widget->snap_radio[index]), G_CALLBACK (update_snap_mode_model), widget); } for (index=0; index<SNAP_STATE_COUNT; index++) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget->snap_radio[index]), (snap_mode == index)); } for (index=0; index<SNAP_STATE_COUNT; index++) { g_signal_handlers_unblock_by_func (G_OBJECT (widget->snap_radio[index]), G_CALLBACK (update_snap_mode_model), widget); } } }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void x_window_create_main(GschemToplevel *w_current) { TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current); GtkPolicyType policy; GtkWidget *main_box=NULL; GtkWidget *menubar=NULL; GtkWidget *toolbar=NULL; GtkWidget *handlebox=NULL; GtkWidget *hscrollbar; GtkWidget *vscrollbar; GtkAdjustment *hadjustment; GtkAdjustment *vadjustment; char *right_button_text; /* used to signify that the window isn't mapped yet */ w_current->window = NULL; w_current->main_window = GTK_WIDGET (gschem_main_window_new ()); gtk_widget_set_name (w_current->main_window, "gschem"); gtk_window_set_policy (GTK_WINDOW (w_current->main_window), TRUE, TRUE, TRUE); /* We want the widgets to flow around the drawing area, so we don't * set a size of the main window. The drawing area's size is fixed, * see below */ /* * normally we let the window manager handle locating and sizing * the window. However, for some batch processing of schematics * (generating a pdf of all schematics for example) we want to * override this. Hence "auto_place_mode". */ if( auto_place_mode ) gtk_widget_set_uposition (w_current->main_window, 10, 10); /* this should work fine */ g_signal_connect (G_OBJECT (w_current->main_window), "delete_event", G_CALLBACK (i_callback_close_wm), w_current); /* Containers first */ main_box = gtk_vbox_new(FALSE, 1); gtk_container_border_width(GTK_CONTAINER(main_box), 0); gtk_container_add(GTK_CONTAINER(w_current->main_window), main_box); menubar = get_main_menu (w_current); if (w_current->handleboxes) { handlebox = gtk_handle_box_new (); gtk_box_pack_start(GTK_BOX(main_box), handlebox, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (handlebox), menubar); } else { gtk_box_pack_start(GTK_BOX(main_box), menubar, FALSE, FALSE, 0); } w_current->menubar = menubar; gtk_widget_realize (w_current->main_window); if (w_current->handleboxes && w_current->toolbars) { handlebox = gtk_handle_box_new (); gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0); } if (w_current->toolbars) { toolbar = gtk_toolbar_new(); gtk_toolbar_set_orientation (GTK_TOOLBAR(toolbar), GTK_ORIENTATION_HORIZONTAL); gtk_toolbar_set_style (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); if (w_current->handleboxes) { gtk_container_add (GTK_CONTAINER (handlebox), toolbar); } else { gtk_box_pack_start(GTK_BOX(main_box), toolbar, FALSE, FALSE, 0); } gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("New"), _("New file"), "toolbar/new", x_window_stock_pixmap("new", w_current), (GtkSignalFunc) i_callback_toolbar_file_new, w_current); gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Open"), _("Open file..."), "toolbar/open", x_window_stock_pixmap("open", w_current), (GtkSignalFunc) i_callback_toolbar_file_open, w_current); gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Save"), _("Save file"), "toolbar/save", x_window_stock_pixmap("save", w_current), (GtkSignalFunc) i_callback_toolbar_file_save, w_current); gtk_toolbar_append_space (GTK_TOOLBAR(toolbar)); gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Undo"), _("Undo last operation"), "toolbar/undo", x_window_stock_pixmap("undo", w_current), (GtkSignalFunc) i_callback_toolbar_edit_undo, w_current); gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Redo"), _("Redo last undo"), "toolbar/redo", x_window_stock_pixmap("redo", w_current), (GtkSignalFunc) i_callback_toolbar_edit_redo, w_current); gtk_toolbar_append_space (GTK_TOOLBAR(toolbar)); /* not part of any radio button group */ gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Component"), _("Add component...\nSelect library and component from list, move the mouse into main window, click to place\nRight mouse button to cancel"), "toolbar/component", x_window_stock_pixmap("insert-symbol", w_current), (GtkSignalFunc) i_callback_toolbar_add_component, w_current); w_current->toolbar_net = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_CHILD_RADIOBUTTON, NULL, _("Nets"), _("Add nets mode\nRight mouse button to cancel"), "toolbar/nets", x_window_stock_pixmap("insert-net", w_current), (GtkSignalFunc) i_callback_toolbar_add_net, w_current); w_current->toolbar_bus = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_CHILD_RADIOBUTTON, w_current->toolbar_net, _("Bus"), _("Add buses mode\nRight mouse button to cancel"), "toolbar/bus", x_window_stock_pixmap("insert-bus", w_current), (GtkSignalFunc) i_callback_toolbar_add_bus, w_current); /* not part of any radio button group */ gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), _("Text"), _("Add Text..."), "toolbar/text", x_window_stock_pixmap("insert-text", w_current), (GtkSignalFunc) i_callback_toolbar_add_text, w_current); gtk_toolbar_append_space (GTK_TOOLBAR(toolbar)); w_current->toolbar_select = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_CHILD_RADIOBUTTON, w_current->toolbar_bus, _("Select"), _("Select mode"), "toolbar/select", x_window_stock_pixmap("select", w_current), (GtkSignalFunc) i_callback_toolbar_edit_select, w_current); gtk_toolbar_append_space (GTK_TOOLBAR(toolbar)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_current->toolbar_select), TRUE); } /* Try to create popup menu (appears in right mouse button */ w_current->popup_menu = (GtkWidget *) get_main_popup(w_current); /* Setup a GtkScrolledWindow for the drawing area */ hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, toplevel->init_right, 100.0, 100.0, 10.0)); vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (toplevel->init_bottom, 0.0, toplevel->init_bottom, 100.0, 100.0, 10.0)); w_current->scrolled = gtk_scrolled_window_new (hadjustment, vadjustment); gtk_container_add(GTK_CONTAINER(main_box), w_current->scrolled); x_window_create_drawing(w_current->scrolled, w_current); x_window_setup_draw_events(w_current); policy = (w_current->scrollbars_flag) ? GTK_POLICY_ALWAYS : GTK_POLICY_NEVER; gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w_current->scrolled), policy, policy); hscrollbar = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW (w_current->scrolled)); gtk_range_set_update_policy (GTK_RANGE (hscrollbar), GTK_UPDATE_CONTINUOUS); vscrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (w_current->scrolled)); gtk_range_set_update_policy (GTK_RANGE (vscrollbar), GTK_UPDATE_CONTINUOUS); /* macro box */ w_current->macro_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_MACRO_WIDGET, NULL)); gtk_box_pack_start (GTK_BOX (main_box), w_current->macro_widget, FALSE, FALSE, 0); g_signal_connect (w_current->macro_widget, "response", G_CALLBACK (&x_window_invoke_macro), w_current); /* bottom box */ if (default_third_button == POPUP_ENABLED) { right_button_text = _("Menu/Cancel"); } else { right_button_text = _("Pan/Cancel"); } w_current->bottom_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_BOTTOM_WIDGET, "grid-mode", gschem_options_get_grid_mode (w_current->options), "grid-size", gschem_options_get_snap_size (w_current->options), /* x_grid_query_drawn_spacing (w_current), -- occurs before the page is set */ "left-button-text", _("Pick"), "middle-button-text", _("none"), "right-button-text", right_button_text, "snap-mode", gschem_options_get_snap_mode (w_current->options), "snap-size", gschem_options_get_snap_size (w_current->options), "status-text", _("Select Mode"), NULL)); i_update_middle_button (w_current, NULL, NULL); gtk_box_pack_start (GTK_BOX (main_box), w_current->bottom_widget, FALSE, FALSE, 0); gtk_widget_show_all (w_current->main_window); w_current->window = w_current->drawing_area->window; w_current->drawable = w_current->window; x_window_setup_gc(w_current); }
/*! \todo Finish function documentation!!! * \brief * \par Function Description * */ void o_move_motion (GschemToplevel *w_current, int w_x, int w_y) { GList *selection, *s_current; OBJECT *object; gint object_x, object_y; gboolean resnap = FALSE; SNAP_STATE snap_mode; 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); g_return_if_fail (page->place_list != NULL); snap_mode = gschem_options_get_snap_mode (w_current->options); selection = geda_list_get_glist (page->selection_list); /* realign the object if we are in resnap mode */ if ((selection != NULL) && (snap_mode == SNAP_RESNAP)) { if (g_list_length(selection) > 1) { /* find an object that is not attached to any other object */ for (s_current = selection; s_current != NULL; s_current = g_list_next(s_current)) { if (((OBJECT *) s_current->data)->attached_to == NULL) { object = (OBJECT *) s_current->data; resnap = TRUE; break; } } /* Only resnap single elements. This is also the case if the selection contains one object and all other object elements are attributes of the object element.*/ for (s_current = selection; s_current != NULL && resnap == TRUE; s_current = g_list_next(s_current)) { if (!(object == (OBJECT *) s_current->data) && !o_attrib_is_attached(page->toplevel, (OBJECT *) s_current->data, object)) { resnap = FALSE; } } } else { /* single object */ resnap = TRUE; object = (OBJECT *) selection->data; } /* manipulate w_x and w_y in a way that will lead to a position of the object that is aligned with the grid */ if (resnap) { if (o_get_position(page->toplevel, &object_x, &object_y, object)) { w_x += snap_grid (w_current, object_x) - object_x; w_y += snap_grid (w_current, object_y) - object_y; } } } o_move_invalidate_rubber (w_current, FALSE); w_current->second_wx = w_x; w_current->second_wy = w_y; o_move_invalidate_rubber (w_current, TRUE); }
/*! \brief Handle motion during a move operation, resnapping if necessary * \par Function Description * Handle movement during a move operation, by updating the global * candidate transformation parameters. The \a w_x and \b w_y * parameters are the incremental translation to be handled. * * This function mostly exists to implement the "resnapping" logic, * which destructively puts objects back onto the grid during a move * operation, as long as specific criteria are met. * * \param w_current Global gschem state structure. * \param w_x X-axis translation * \param w_y Y-axis translation */ void o_move_motion (GschemToplevel *w_current, int w_x, int w_y) { GList *selection, *s_current; OBJECT *object = NULL; gint object_x, object_y; SNAP_STATE snap_mode; 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); g_return_if_fail (page->place_list != NULL); snap_mode = gschem_options_get_snap_mode (w_current->options); selection = geda_list_get_glist (page->selection_list); /* There are three posssibilities: * * 1. There is exactly one object selected, in which case it is * snapped. * * 2. There are multiple objects selected, but there is some object * O[i] such that all other selected objects O[j!=i] are attached * as attributes of O[i]. In that case, the O[i] is snapped. * * 3. Other cases, in which case no snapping occurs. */ if (NULL == selection || SNAP_RESNAP != snap_mode) { object = NULL; } else if (1 == g_list_length (selection)) { object = (OBJECT *) selection->data; } else { /* The object that things are supposed to be attached to */ OBJECT *attached = NULL; /* Scan the selection, searching for an object that's not attached * to any other object. As we go, also check whether everything * in the list that *is* attached as an attribute is attached to * the same object. */ for (s_current = selection; NULL != s_current; s_current = g_list_next (s_current)) { OBJECT *candidate = (OBJECT *) s_current->data; if (NULL == candidate->attached_to) { /* If the object is *not* attached as an attribute, then check * whether we previously found an independent object. If we * did, we can't do snapping, so give up. */ if (NULL == object) { object = candidate; } else if (candidate != object) { break; /* Give up */ } } else { /* If the object is attached as an attribute, then check if * it's attached as an attribute of the same object as * everything else is. If not, we can't do snapping, so give * up. */ if (NULL == attached) { attached = candidate->attached_to; } else if (attached != candidate->attached_to) { break; /* Give up */ } } } if (NULL == object || (NULL != attached && object != attached)) { object = NULL; } else { g_assert (NULL != object); } } /* manipulate w_x and w_y in a way that will lead to a position of the object that is aligned with the grid */ if (NULL != object) { if (geda_object_get_position (object, &object_x, &object_y)) { w_x += snap_grid (w_current, object_x) - object_x; w_y += snap_grid (w_current, object_y) - object_y; } } o_move_invalidate_rubber (w_current, FALSE); w_current->second_wx = w_x; w_current->second_wy = w_y; o_move_invalidate_rubber (w_current, TRUE); }