static void update_tux(gint direction) { gint rotation = 0; GooCanvasBounds bounds; gdouble scale; /* Our svg image of tux is faced south */ switch(direction) { case EAST: rotation = -90; break; case WEST: rotation = 90; break; case NORTH: rotation = 180; break; case SOUTH: rotation = 0; break; } goo_canvas_item_set_transform(tuxitem, NULL); goo_canvas_item_get_bounds(tuxitem, &bounds); scale = (gdouble) cellsize / (bounds.x2 - bounds.x1); goo_canvas_item_scale(tuxitem, scale, scale); goo_canvas_item_rotate( tuxitem, rotation, (bounds.x2-bounds.x1)/2, (bounds.y2-bounds.y1)/2); // update the running shoes if(run_fast_possible && run_fast) { goo_canvas_item_set_transform(tuxshoes, NULL); scale = (gdouble) cellsize / (bounds.x2 - bounds.x1); goo_canvas_item_scale(tuxshoes, scale, scale); goo_canvas_item_rotate( tuxshoes, rotation, (bounds.x2-bounds.x1)/2, (bounds.y2-bounds.y1)/2); } }
/* * Same as draw rect but for an image */ static void move_image(GooCanvasItem *group, int x, int y, GooCanvasItem *item) { int x1,y1; y1=cellsize*(y)-hoogte + board_border_y; x1=cellsize*(x)-breedte + board_border_x; goo_canvas_item_set_transform(item, NULL); goo_canvas_item_translate(item, x1, y1); goo_canvas_item_raise(item, NULL); }
/** Setting the bar location * @param[in] x the bar x coordinate, -1 to set the default * @param[in] y the bar y coordinate, -1 to set the default * @param[in] zoom the bar zoom factor, -1 to set the default */ static void bar_location (int x, int y, double zoom) { // Make the y coord be assigned at its bottom int ny = (y == -1 ? _default_y : y); ny += BARHEIGHT - (zoom == -1 ? _default_zoom : zoom) * BARHEIGHT; goo_canvas_item_set_transform(rootitem, NULL); GooCanvasBounds bounds; goo_canvas_item_get_bounds(rootitem, &bounds); int nx = (x == -1 ? (BOARDWIDTH - (bounds.x2 - bounds.x1))/2 : x); goo_canvas_item_translate(rootitem, nx, ny); goo_canvas_item_scale(rootitem, (zoom == -1 ? _default_zoom : zoom), (zoom == -1 ? _default_zoom : zoom)); //#endif }
static void update_preview (Browser *br) { LibraryPart *library_part; gdouble new_x, new_y, x1, y1, x2, y2; gdouble text_width; gdouble width, height; GooCanvasBounds bounds; gdouble scale; cairo_matrix_t transf, affine; gchar *part_name; char *description; GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *selection; // Get the current selected row selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (br->list)); if (!GTK_IS_TREE_SELECTION (selection)) return; if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { return; } gtk_tree_model_get (model, &iter, 0, &part_name, -1); library_part = library_get_part (br->library, part_name); // If there is already a preview part-item, destroy its group and create a // new one. if (br->preview != NULL) { goo_canvas_item_remove (GOO_CANVAS_ITEM (br->preview)); } br->preview = GOO_CANVAS_GROUP (goo_canvas_group_new ( goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), NULL)); goo_canvas_set_bounds (GOO_CANVAS (br->canvas), 0.0, 0.0, 250.0, 110.0); g_object_get (br->preview, "width", &width, "height", &height, NULL); if (!library_part) return; part_item_create_canvas_items_for_preview (br->preview, library_part); // Unconstraint the canvas width & height to adjust for the part description g_object_set (br->preview, "width", -1.0, "height", -1.0, NULL); // Get the coordonates */ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (br->preview), &bounds); x1 = bounds.x1; x2 = bounds.x2; y1 = bounds.y1; y2 = bounds.y2; // Translate in such a way that the canvas centre remains in (0, 0) cairo_matrix_init_translate (&transf, -(x2 + x1) / 2.0f + PREVIEW_WIDTH / 2, -(y2 + y1) / 2.0f + PREVIEW_HEIGHT / 2); // Compute the scale of the widget if ((x2 - x1 != 0) || (y2 - y1 != 0)) { if ((x2 - x1) < (y2 - y1)) scale = 0.60f * PREVIEW_HEIGHT / (y2 - y1); else scale = 0.60f * PREVIEW_WIDTH / (x2 - x1); } else scale = 5; cairo_matrix_init_scale (&affine, scale, scale); cairo_matrix_multiply (&transf, &transf, &affine); // Apply the transformation goo_canvas_item_set_transform (GOO_CANVAS_ITEM (br->preview), &transf); // Compute the motion to centre the Preview widget new_x = 100 + (PREVIEW_WIDTH - x1 - x2) / 2; new_y = (PREVIEW_HEIGHT - y1 - y2) / 2 - 10; // Apply the transformation if (scale > 5.0) scale = 3.0; goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->preview), new_x, new_y, scale, 0.0); description = g_strdup (library_part->description); wrap_string (description, 20); g_object_set (br->description, "text", description, NULL); g_free (description); g_object_get (G_OBJECT (br->description), "width", &text_width, NULL); goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->description), 50.0, -20.0, 1.0, 0.0); g_free (part_name); }
/* Setting list of available icons in the control bar */ static void bar_set (const GComprisBarFlags flags) { // Always reset the zoom factor or the calculation // will be wrong goo_canvas_item_set_transform(rootitem, NULL); _hidden = FALSE; goo_canvas_item_raise(rootitem, NULL); /* Non yet initialized : Something Wrong */ if(get_item(GC_BAR_LEVEL) == NULL) { g_message("in bar_set_level, level_item uninitialized : should not happen\n"); return; } current_flags = flags; if(gc_help_has_board(gc_board_get_current())) current_flags |= GC_BAR_HELP; if(flags&GC_BAR_ABOUT) current_flags |= GC_BAR_ABOUT; if(flags&GC_BAR_CONFIG) current_flags |= GC_BAR_CONFIG; if(flags&GC_BAR_REPEAT_ICON) current_flags |= GC_BAR_REPEAT_ICON; if(flags&GC_BAR_REPEAT) current_flags |= GC_BAR_REPEAT; update_exit_button(); GSList *list; double x = 0; for (list = buttons; list != NULL; list = list->next) { GooCanvasItem *item = (GooCanvasItem *)list->data; GComprisBarFlags flag = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT (item), "flag")); if (flag & current_flags) { GooCanvasBounds bounds; SET_ITEM_LOCATION(item, x, -20); goo_canvas_item_get_bounds(item, &bounds); gc_item_focus_init(item, NULL); x += bounds.x2 - bounds.x1 + BAR_GAP; g_object_set (item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); } else g_object_set (item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } /* Scale the bar back to fit the buttons, no more */ SET_ITEM_LOCATION(bar_item, 0, 0); GooCanvasBounds bounds; goo_canvas_item_get_bounds(bar_item, &bounds); goo_canvas_item_scale(bar_item, x / (bounds.x2 - bounds.x1), 1); // Always center the bar with its new bounds //SET_ITEM_LOCATION(rootitem, 0, _default_y); bar_location (-1, -1, -1); }
// Event handler for a SheetItem gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_target_item, GdkEvent *event, Sheet *sheet) { // Remember the last position of the mouse cursor. static double last_x, last_y; GooCanvas *canvas; SheetPriv *priv; GList *list; // Mouse cursor position in window coordinates, snapped to the grid spacing. double snapped_x, snapped_y; // Move the selected item(s) by this movement. double dx, dy; g_return_val_if_fail (sheet_item != NULL, FALSE); g_return_val_if_fail (sheet != NULL, FALSE); priv = sheet->priv; canvas = GOO_CANVAS (sheet); switch (event->type) { case GDK_BUTTON_PRESS: // Grab focus to sheet for correct use of events gtk_widget_grab_focus (GTK_WIDGET (sheet)); switch (event->button.button) { case 1: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); sheet->state = SHEET_STATE_DRAG_START; sheet_get_pointer (sheet, &last_x, &last_y); break; case 3: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); if (sheet->state != SHEET_STATE_NONE) return TRUE; // Bring up a context menu for right button clicks. if (!SHEET_ITEM (sheet_item)->priv->selected && !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) sheet_select_all (sheet, FALSE); sheet_item_select (SHEET_ITEM (sheet_item), TRUE); sheet_item_run_menu (SHEET_ITEM (sheet_item), sheet, (GdkEventButton *) event); break; default: return FALSE; } break; case GDK_2BUTTON_PRESS: // Do not interfere with object dragging. if (sheet->state == SHEET_STATE_DRAG) return FALSE; switch (event->button.button) { case 1: if (sheet->state == SHEET_STATE_DRAG_START) sheet->state = SHEET_STATE_NONE; g_signal_stop_emission_by_name (sheet_item, "button_press_event"); g_signal_emit_by_name (sheet_item, "double_clicked"); break; default: return FALSE; } break; case GDK_3BUTTON_PRESS: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); return TRUE; case GDK_BUTTON_RELEASE: switch (event->button.button) { case 1: if (sheet->state != SHEET_STATE_DRAG && sheet->state != SHEET_STATE_DRAG_START) return TRUE; g_signal_stop_emission_by_name (sheet_item, "button-release-event"); if (sheet->state == SHEET_STATE_DRAG_START) { sheet->state = SHEET_STATE_NONE; if (!(event->button.state & GDK_SHIFT_MASK)) sheet_select_all (sheet, FALSE); if (IS_SHEET_ITEM (sheet_item)) sheet_item_select (SHEET_ITEM (sheet_item), TRUE); return TRUE; } // Get the mouse motion sheet_get_pointer (sheet, &snapped_x, &snapped_y); snapped_x -= last_x; snapped_y -= last_y; sheet->state = SHEET_STATE_NONE; goo_canvas_pointer_ungrab (canvas, GOO_CANVAS_ITEM (sheet_item), event->button.time); // Reparent the selected objects to the normal group // to have correct behaviour for (list = priv->selected_objects; list; list = list->next) { sheet_item_reparent (SHEET_ITEM (list->data), sheet->object_group); } for (list = priv->selected_objects; list; list = list->next) { ItemData *item_data; Coords pos; item_data = SHEET_ITEM (list->data)->priv->data; pos.x = snapped_x; pos.y = snapped_y; item_data_move (item_data, &pos); item_data_register (item_data); } g_list_free_full (list, g_object_unref); break; } case GDK_KEY_PRESS: switch (event->key.keyval) { case GDK_KEY_r: sheet_rotate_selection (sheet); { gdouble x, y; GooCanvasBounds bounds; sheet_get_pointer (sheet, &x, &y); // Center the objects around the mouse pointer. goo_canvas_item_get_bounds ( GOO_CANVAS_ITEM (priv->floating_group), &bounds); dx = x - (bounds.x1 + bounds.x2) / 2; dy = y - (bounds.y1 + bounds.y2) / 2; snap_to_grid (sheet->grid, &dx, &dy); goo_canvas_item_translate ( GOO_CANVAS_ITEM (priv->floating_group), dx, dy); last_x = snapped_x; last_y = snapped_y; } break; default: return FALSE; } return TRUE; case GDK_MOTION_NOTIFY: if (sheet->state != SHEET_STATE_DRAG && sheet->state != SHEET_STATE_DRAG_START) return FALSE; if (sheet->state == SHEET_STATE_DRAG_START) { sheet->state = SHEET_STATE_DRAG; // Update the selection if needed. if (IS_SHEET_ITEM (sheet_item) && (!SHEET_ITEM (sheet_item)->priv->selected)) { if (!(event->button.state & GDK_SHIFT_MASK)) { sheet_select_all (sheet, FALSE); } sheet_item_select (SHEET_ITEM (sheet_item), TRUE); } // Reparent the selected objects so that we can move them // efficiently. for (list = priv->selected_objects; list; list = list->next) { ItemData *item_data; item_data = SHEET_ITEM (list->data)->priv->data; item_data_unregister (item_data); sheet_item_reparent (SHEET_ITEM (list->data), priv->selected_group); } goo_canvas_pointer_grab (canvas, GOO_CANVAS_ITEM (sheet_item), GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, event->button.time); } // Set last_x & last_y to the pointer position sheet_get_pointer (sheet, &snapped_x, &snapped_y); dx = snapped_x - last_x; dy = snapped_y - last_y; // Check that we don't move outside the sheet... // Horizontally: /* if (cx1 <= 0) { // leftmost edge dx = dx - x1; snap_to_grid (sheet->grid, &dx, NULL); snapped_x = last_x + dx; } else if (cx2 >= sheet_width) { // rightmost edge dx = dx - (x2 - sheet_width / priv->zoom); snap_to_grid (sheet->grid, &dx, NULL); snapped_x = last_x + dx; } // And vertically: if (cy1 <= 0) { // upper edge dy = dy - y1; snap_to_grid (sheet->grid, NULL, &dy); snapped_y = last_y + dy; } else if (cy2 >= sheet_height) { // lower edge dy = dy - (y2 - sheet_height / priv->zoom); snap_to_grid (sheet->grid, NULL, &dy); snapped_y = last_y + dy; } //last_x = snapped_x; //last_y = snapped_y; */ goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->selected_group), NULL); goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), dx, dy); return TRUE; default: return FALSE; } return TRUE; }
// Event handler for a "floating" group of objects. int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) { SheetPriv *priv; GList *list; static gboolean keep = FALSE; // Remember the start position of the mouse cursor. static Coords last = {0., 0.}; // Mouse cursor position in window coordinates, snapped to the grid spacing. static Coords snapped = {0., 0.}; // Move the selected item(s) by this movement. Coords delta = {0., 0.}; g_return_val_if_fail (sheet != NULL, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); g_return_val_if_fail (sheet->priv->floating_objects != NULL, FALSE); priv = sheet->priv; switch (event->type) { case GDK_BUTTON_RELEASE: g_signal_stop_emission_by_name (sheet, "event"); break; case GDK_BUTTON_PRESS: if (sheet->state != SHEET_STATE_FLOAT) return TRUE; switch (event->button.button) { case 2: case 4: case 5: return FALSE; case 1: // do not free the floating items, but use them like a stamp keep = event->button.state & GDK_CONTROL_MASK; // Continue adding if CTRL is pressed if (!keep) { sheet->state = SHEET_STATE_NONE; g_signal_stop_emission_by_name (sheet, "event"); if (g_signal_handler_is_connected (sheet, sheet->priv->float_handler_id)) g_signal_handler_disconnect (sheet, sheet->priv->float_handler_id); sheet->priv->float_handler_id = 0; } // FIXME assert that `Coords current` has been set by now! for (list = priv->floating_objects; list; list = list->next) { SheetItem *floating_item; ItemData *floating_data; // Create a real item. floating_item = list->data; if (!keep) { floating_data = sheet_item_get_data (floating_item); g_object_set (floating_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } else { // FIXME the bounding box of the clone is wrong floating_data = item_data_clone (sheet_item_get_data (floating_item)); } g_object_ref (G_OBJECT (floating_data)); NG_DEBUG ("Item Data Pos will be %lf %lf", snapped.x, snapped.y) item_data_set_pos (floating_data, &snapped); item_data_snap (floating_data, sheet->grid); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), floating_data); if (!keep) g_object_unref (G_OBJECT (floating_item)); } if (keep) { g_object_set (G_OBJECT (priv->floating_group), "x", snapped.x, "y", snapped.y, NULL); } else { g_list_free (priv->floating_objects); priv->floating_objects = NULL; } break; case 3: // Cancel the "float-placement" for button-3 clicks. g_signal_stop_emission_by_name (sheet, "event"); sheet_item_cancel_floating (sheet); break; } break; case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: g_signal_stop_emission_by_name (sheet, "event"); return TRUE; case GDK_MOTION_NOTIFY: // keep track of the position, as `sheet_get_pointer*()` does not work // in other events than MOTION_NOTIFY #if 0 { Coords tmp; last = current; if (sheet_get_pointer (sheet, &tmp.x, &tmp.y)) { snapped_current = current = tmp; snap_to_grid (sheet->grid, &snapped_current.x, &snapped_current.y); } } #endif if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START) return FALSE; g_signal_stop_emission_by_name (sheet, "event"); // Get pointer position independantly of the zoom if (sheet->state == SHEET_STATE_FLOAT_START) { sheet->state = SHEET_STATE_FLOAT; last.x = last.y = 0.; // Reparent the selected objects so that we can move them // efficiently. for (list = priv->floating_objects; list; list = list->next) { sheet_item_reparent (SHEET_ITEM (list->data), priv->floating_group); // Set the floating item visible g_object_set (G_OBJECT (list->data), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); } #if 0 GooCanvasBounds box; goo_canvas_item_get_bounds (priv->floating_group, &box); #endif NG_DEBUG ("\n\n\nFLOAT ### START\n\n\n\n"); } sheet_get_pointer_snapped (sheet, &snapped.x, &snapped.y); delta = coords_sub (&snapped, &last); NG_DEBUG ("drag floating current sx=%lf sy=%lf \n", snapped.x, snapped.y); NG_DEBUG ("drag floating last lx=%lf ly=%lf \n", last.x, last.y); NG_DEBUG ("drag floating delta -> dx=%lf dy=%lf \n", delta.x, delta.y); #if !FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK last = snapped; #else goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->floating_group), NULL); #endif goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), delta.x, delta.y); break; case GDK_KEY_PRESS: switch (event->key.keyval) { case GDK_KEY_r: case GDK_KEY_R: { Coords bbdelta; GooCanvasBounds bounds; // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); bbdelta.x = (bounds.x2 - bounds.x1) / 2.; bbdelta.y = (bounds.y2 - bounds.y1) / 2.; sheet_rotate_ghosts (sheet); // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); bbdelta.x -= (bounds.x2 - bounds.x1) / 2.; bbdelta.y -= (bounds.y2 - bounds.y1) / 2.; snap_to_grid (sheet->grid, &bbdelta.x, &bbdelta.y); goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), bbdelta.x, bbdelta.y); } break; default: return FALSE; } default: return FALSE; } return TRUE; }
/** * whenever the model changes, this one gets called to update the view representation * @attention this recalculates the matrix every time, this makes sure no errors stack up * @attention further reading on matrix manipulations * @attention http://www.cairographics.org/matrix_transform/ * @param data the model item, a bare C struct derived from ItemData * @param sheet_item the view item, derived from goo_canvas_group/item */ static void part_changed_callback (ItemData *data, SheetItem *sheet_item) { //TODO add static vars in order to skip the redraw if nothing changed //TODO may happen once in a while and the check is really cheap GSList *iter; GooCanvasAnchorType anchor; GooCanvasGroup *group; GooCanvasItem *canvas_item; PartItem *item; PartItemPriv *priv; Part *part; int index = 0; Coords pos; double scale_h, scale_v; // states int rotation; IDFlip flip; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_PART_ITEM (sheet_item)); item = PART_ITEM (sheet_item); group = GOO_CANVAS_GROUP (item); part = PART (data); priv = item->priv; // init the states flip = part_get_flip (part); rotation = part_get_rotation (part); DEGSANITY (rotation); scale_h = (flip & ID_FLIP_HORIZ) ? -1. : 1.; scale_v = (flip & ID_FLIP_VERT) ? -1. : 1.; item_data_get_pos (data, &pos); // Move the canvas item and invalidate the bbox cache. goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (sheet_item), pos.x, pos.y, 1.0, 0.0); cairo_matrix_t morph, inv; cairo_status_t done; cairo_matrix_init_rotate (&morph, DEG2RAD (rotation)); cairo_matrix_scale (&morph, scale_h, scale_v); inv = morph; done = cairo_matrix_invert (&inv); if (done != CAIRO_STATUS_SUCCESS) { g_warning ("Failed to invert matrix. This should never happen. Never!"); return; } // rotate all items in the canvas group for (index = 0; index < group->items->len; index++) { canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), &morph); } // revert the rotation of all labels and change their anchor to not overlap too badly // this assures that the text is always horizontal and properly aligned anchor = angle_to_anchor (rotation); for (iter = priv->label_items; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // same for label nodes for (iter = priv->label_nodes; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // Invalidate the bounding box cache. priv->cache_valid = FALSE; }