/* * Edit Draw * --------- * A GraphApp program for editing simple drawings. * This program written by Loki. */ #include #include #include /* * Define some data structures. */ struct Shape { int kind; /* 1=line, 2=rectangle, 3=ellipse, etc */ int width; /* line thickness, default is 1 */ int colour; /* index into colour_list (see below) */ Point p1; /* start point */ Point p2; /* end point */ }; typedef struct Shape Shape; /* * A list of shapes, implemented as an array. */ enum { MAX_SHAPES = 100 }; int num_shapes = 0; Shape shape_list[MAX_SHAPES]; /* * A global App object to connect to the windowing system. */ App *app; /* * A global Shape used in the mouse dragging functions. */ Shape shape; /* * Some lists of shape-kinds and colours, with associated string names. */ char * shape_names[] = { "Line", "Rectangle", "Box", "Round Rect", "Round Box", "Ellipse", "Oval", "Arc", "Pie" }; enum shape_types { LINE, RECTANGLE, BOX, ROUNDRECT, ROUNDBOX, ELLIPSE, OVAL, ARC, PIE }; char * colour_names[] = { "Black", "Red", "Green", "Blue", "Grey", "Magenta", "Yellow", "Cyan", "Dark Grey", "Dark Red", "Dark Green", "Dark Blue" }; Colour colour_list[] = { {0,0,0,0}, {0,255,0,0}, {0,0,255,0}, {0,0,0,255}, {0,192,192,192},{0,255,0,255}, {0,255,255,0}, {0,0,255,255}, {0,128,128,128},{0,128,0,0}, {0,0,128,0}, {0,0,0,128} }; #define NUM_SHAPE_TYPES (sizeof(shape_names)/sizeof(shape_names[0])) #define NUM_COLOURS (sizeof(colour_names)/sizeof(colour_names[0])) #define NUM_DELAYS 2 #define NUM_MODES 2 #define NUM_WIDTHS 10 /* * Some menuitems. */ MenuItem * shape_item[NUM_SHAPE_TYPES]; MenuItem * colour_item[NUM_COLOURS]; MenuItem * width_item[NUM_WIDTHS]; MenuItem * delay_item[NUM_DELAYS]; MenuItem * mode_item[NUM_MODES]; /* * Other global variables. */ int time_delay = 0; int xor_mode = 1; /* * The main drawing canvas. */ Control *canvas; /* * Help text. */ char * help_text [3] = { "The program EditDraw was written by Loki, for the " "Department of Computer Science, University of Sydney, Australia." "\n\n" "It is a demonstration of some principles involved with " "creating a drawing package.\n", "The delay option inserts a half-second delay between " "erasing the currently selected object and drawing it again." "\n\n" "Notice " "that the object is totally erased when dragging the mouse using " "the XOR drawing mode, but the Paint mode gets quite messy.", "\n\n" "By using a drawing mode which " "preserves information, we can get the picture back at any time " "by simply drawing the same object again in the same mode. " "The XOR mode preserves information, because some part of " "the destination pixels are preserved in the resulting picture.", }; /* * Maintain a list of shapes. */ void add_shape(Shape s) { if (num_shapes < MAX_SHAPES) { shape_list[num_shapes] = s; num_shapes = num_shapes + 1; } } void del_shape(int index) { while (index < num_shapes - 1) { shape_list[index] = shape_list[index+1]; index = index + 1; } num_shapes = num_shapes - 1; } /* * Functions for deciding if a point is inside a shape. */ int inside_line(Point p, Point p1, Point p2) { /* this should use Bresenham's algorithm */ /* for now we approximate by checking if we near the ends */ Rect r; r.x = p1.x-2; r.y = p1.y-2; r.width = 4; r.height = 4; if (point_in_rect(p, r)) return 1; r.x = p2.x-2; r.y = p2.y-2; if (point_in_rect(p, r)) return 1; return 0; } int inside_shape(Point p, Shape s) { Rect r; r = rect(s.p1.x, s.p1.y, s.p2.x - s.p1.x, s.p2.y - s.p1.y); r = rect_abs(r); if (s.kind == LINE) { return inside_line(p, s.p1, s.p2); } else if (s.kind == RECTANGLE) { return point_in_rect(p,r); } else if (s.kind == BOX) { return (point_in_rect(p,r) && (! point_in_rect(p,inset_rect(r,2)))); } else if (s.kind == ROUNDRECT) { return point_in_rect(p,r); } else if (s.kind == ROUNDBOX) { return point_in_rect(p,r); } else if (s.kind == ELLIPSE) { return point_in_rect(p,r); /* this is a poor guess since ellipses are round */ } else if (s.kind == OVAL) { return (point_in_rect(p,r) && (! point_in_rect(p,inset_rect(r,2)))); /* this is a poor guess since ellipses are round */ } else if (s.kind == ARC) { return (point_in_rect(p,r) && (! point_in_rect(p,inset_rect(r,2)))); /* this is a poor guess since ellipses are round */ } else if (s.kind == PIE) { return (point_in_rect(p,r) && (! point_in_rect(p,inset_rect(r,2)))); /* this is a poor guess since ellipses are round */ } return 0; } /* * Given a point where the user has clicked, find the nearby shape. * Search from the end of the list backwards, to find the frontmost shape. */ int find_shape(Point p) { int i; for (i = num_shapes - 1; i >= 0; i=i-1) { if (inside_shape(p, shape_list[i])) { return i; } } return -1; /* not found */ } /* * Draw a single shape. */ void draw_shape(Graphics *g, Shape s) { Rect r; r = rect(s.p1.x, s.p1.y, s.p2.x - s.p1.x, s.p2.y - s.p1.y); r = rect_abs(r); set_colour(g, colour_list[s.colour]); set_line_width(g, s.width); if (s.kind == LINE) { draw_line(g, s.p1, s.p2); } else if (s.kind == RECTANGLE) { fill_rect(g, r); } else if (s.kind == BOX) { draw_rect(g, r); } else if (s.kind == ROUNDRECT) { draw_round_rect(g, r); } else if (s.kind == ROUNDBOX) { fill_round_rect(g, r); } else if (s.kind == ELLIPSE) { fill_ellipse(g, r); } else if (s.kind == OVAL) { draw_ellipse(g, r); } else if (s.kind == ARC) { draw_arc(g, r, 45, -45); } else if (s.kind == PIE) { fill_arc(g, r, 45, -45); } } /* * Draw the entire list of shapes. */ void draw_all_shapes(Graphics *g) { int i; set_paint_mode(g); for (i=0; i < num_shapes; i=i+1) { draw_shape(g, shape_list[i]); } } void draw_canvas(Control *c, Graphics *g) { Rect r = get_control_area(c); draw_all_shapes(g); set_colour(g, rgb(0,0,0)); set_line_width(g, 1); draw_rect(g, r); } void resize_canvas(Window *w) { Graphics *g; Rect r = get_window_area(w); r.y += 24; r.height -= 24; r = inset_rect(r, 10); set_control_area(canvas, r); g = get_control_graphics(canvas); draw_canvas(canvas, g); del_graphics(g); } /* * Create a shape by dragging the mouse. */ void start_drag(Control *c, int buttons, Point p) { Graphics *g = get_control_graphics(c); Colour bg = get_control_background(c); shape.p1 = p; /* set start-point of the shape */ shape.p2 = p; /* set end-point to be the same */ if (xor_mode) set_xor_mode(g, bg);/* set drawing mode to XOR */ draw_shape(g, shape); /* draw new shape */ del_graphics(g); } void start_window_drag(Window *w, int buttons, Point p) { Control *c = get_window_data(w); /* convert to canvas co-ordinates */ p.x -= c->area.x; p.y -= c->area.y; start_drag(c, buttons, p); } void end_drag(Control *c, int buttons, Point p) { Graphics *g = get_control_graphics(c); shape.p2 = p; /* set end-point of this shape */ set_paint_mode(g); /* set drawing mode to normal */ draw_shape(g, shape); /* draw new shape */ add_shape(shape); /* add the shape to the list */ del_graphics(g); } void end_window_drag(Window *w, int buttons, Point p) { Control *c = get_window_data(w); /* convert to canvas co-ordinates */ p.x -= c->area.x; p.y -= c->area.y; end_drag(c, buttons, p); } void drag_shape(Control *c, int buttons, Point p) { Graphics *g = get_control_graphics(c); Colour bg = get_control_background(c); if (xor_mode) set_xor_mode(g, bg);/* set drawing mode to XOR */ delay(app, time_delay); /* to see what's happening */ draw_all(app); draw_shape(g, shape); /* cancel old shape */ draw_all(app); /* make changes visible now */ delay(app, time_delay); /* to see what's happening */ draw_all(app); shape.p2 = p; /* move end-point of this shape */ draw_shape(g, shape); /* draw new shape */ del_graphics(g); } void window_drag_shape(Window *w, int buttons, Point p) { Control *c = get_window_data(w); /* convert to canvas co-ordinates */ p.x -= c->area.x; p.y -= c->area.y; drag_shape(c, buttons, p); } /* * Menu call-back functions below. */ void select_shape(MenuItem *me) { uncheck_menu_item(shape_item[shape.kind]); shape.kind = me->value; check_menu_item(me); } void select_colour(MenuItem *me) { uncheck_menu_item(colour_item[shape.colour]); shape.colour = me->value; check_menu_item(me); } void select_width(MenuItem *me) { uncheck_menu_item(width_item[shape.width]); shape.width = me->value; check_menu_item(me); } void select_delay(MenuItem *me) { int i; time_delay = me->value; for (i=0; i < NUM_DELAYS; i++) { uncheck_menu_item(delay_item[i]); if (me == delay_item[i]) check_menu_item(me); } } void select_mode(MenuItem *me) { int i; xor_mode = me->value; for (i=0; i < NUM_MODES; i++) { uncheck_menu_item(mode_item[i]); if (me == mode_item[i]) check_menu_item(me); } } void about_this_program(MenuItem *me) { ask_ok(app, me->text, help_text[0]); } void help_on_delays(MenuItem *me) { ask_ok(app, me->text, help_text[1]); } void help_on_modes(MenuItem *me) { ask_ok(app, me->text, help_text[2]); } void quit(MenuItem *me) { exit(0); } /* * Initialise the global variable 'shape'. */ void init_shape(void) { shape.kind = LINE; shape.width = 3; shape.colour = 1; shape.p1 = pt(0,0); shape.p2 = pt(0,0); } /* * The main function. */ int main(int argc, char *argv[]) { Window *w; MenuBar *mb; Menu *m; MenuItem *mi; int i; char name[32]; app = new_app(argc, argv); w = new_window(app, rect(0,0,500,500), "Edit Draw", STANDARD_WINDOW); on_window_resize(w, resize_canvas); init_shape(); mb = new_menu_bar(w); m = new_menu(mb, "File"); mi = new_menu_item(m, "Quit", 'Q', quit); m = new_menu(mb, "Shape"); for (i=0; i < NUM_SHAPE_TYPES; i=i+1) { shape_item[i] = new_menu_item(m, shape_names[i], 0, select_shape); shape_item[i]->value = i; } select_shape(shape_item[0]); m = new_menu(mb, "Colour"); for (i=0; i < NUM_COLOURS; i=i+1) { colour_item[i] = new_menu_item(m, colour_names[i], 0, select_colour); colour_item[i]->value = i; } select_colour(colour_item[1]); m = new_menu(mb, "Width"); for (i=0; i < NUM_WIDTHS; i=i+1) { sprintf(name, "%d", i); width_item[i] = new_menu_item(m, name, 0, select_width); width_item[i]->value = i; } select_width(width_item[1]); m = new_menu(mb, "Options"); delay_item[0] = new_menu_item(m, "No delay", 0, select_delay); delay_item[1] = new_menu_item(m, "Use delay", 0, select_delay); delay_item[1]->value = 50; select_delay(delay_item[0]); mi = new_menu_item(m, "-", 0, NULL); mode_item[0] = new_menu_item(m, "Use XOR mode", 0, select_mode); mode_item[1] = new_menu_item(m, "Use paint mode", 0, select_mode); mode_item[0]->value = 1; mode_item[1]->value = 0; select_mode(mode_item[0]); m = new_menu(mb, "Help"); mi = new_menu_item(m, "About EditDraw", 0, about_this_program); mi = new_menu_item(m, "-", 0, NULL); mi = new_menu_item(m, "Use of Delays", 0, help_on_delays); mi = new_menu_item(m, "Drawing Modes", 0, help_on_modes); canvas = new_control(w, rect(10,30,480,480)); on_control_redraw(canvas, draw_canvas); set_control_background(canvas, rgb(240,240,240)); on_control_mouse_down(canvas, start_drag); on_control_mouse_up(canvas, end_drag); on_control_mouse_drag(canvas, drag_shape); set_window_data(w, canvas); on_window_mouse_down(w, start_window_drag); on_window_mouse_up(w, end_window_drag); on_window_mouse_drag(w, window_drag_shape); show_window(w); main_loop(app); del_app(app); return 0; }