#include #include #include #include /*********************************** Circle List ****************************************/ #define DEF_RADIUS 30 #define MAX_RADIUS 100 #define MAX_CIRCLES 80 typedef struct _Circle { int x; int y; int r; int id; } Circle; static Circle circlesList[MAX_CIRCLES]; static int circlesCount = 0; static int circles_add(int x, int y) { static int lastId = 0; if (circlesCount == MAX_CIRCLES) return -1; circlesList[circlesCount].x = x; circlesList[circlesCount].y = y; circlesList[circlesCount].r = DEF_RADIUS; circlesList[circlesCount].id = lastId; /* each circle has an unique id */ circlesCount++; lastId++; return lastId - 1; } static int circles_insert(Circle circle) { if (circlesCount == MAX_CIRCLES) return -1; circlesList[circlesCount] = circle; circlesCount++; return 1; } static void circles_remove(Circle circle) { int i, j; for (i = 0; i < circlesCount; i++) { if (circlesList[i].id == circle.id) break; } for (j = i; j < circlesCount - 1; j++) circlesList[j] = circlesList[j + 1]; circlesCount--; } static void circles_update(Circle circle) { int i; for (i = 0; i < circlesCount; i++) { if (circlesList[i].id == circle.id) break; } circlesList[i].r = circle.r; } static int circles_pick(int x, int y) { int i; int min_r = MAX_RADIUS; int id = -1; for (i = 0; i < circlesCount; i++) { int dx = circlesList[i].x - x; int dy = circlesList[i].y - y; int r = dx*dx + dy*dy; r = (int)sqrt((double)r); if (r < circlesList[i].r) { if (id < 0) { id = circlesList[i].id; min_r = r; } else if (r < min_r) id = circlesList[i].id; } } return id; } static Circle circles_find(int id) { int i; for (i = 0; i < circlesCount; i++) { if (circlesList[i].id == id) break; } return circlesList[i]; } /************************************* Operation List ************************************************/ #define MAX_OPERATIONS 200 enum { OP_CREATE, OP_REMOVE, OP_UPDATE }; typedef struct _Operation { int type; Circle circle; } Operation; static Operation operationsList[MAX_OPERATIONS]; static int operationsCount = 0; static int currentOperation = -1; static void op_add(int type, Circle circle) { if (operationsCount == MAX_OPERATIONS) return; if (currentOperation < operationsCount - 1) operationsCount = currentOperation + 1; operationsList[operationsCount].type = type; operationsList[operationsCount].circle = circle; currentOperation = operationsCount; operationsCount++; } static void op_undo(void) { Operation op; if (currentOperation < 0) return; op = operationsList[currentOperation]; currentOperation--; switch (op.type) { case OP_CREATE: circles_remove(op.circle); break; case OP_REMOVE: circles_insert(op.circle); break; case OP_UPDATE: op = operationsList[currentOperation]; currentOperation--; /* return 2 to get the old value */ circles_update(op.circle); break; } } static void op_redo(void) { Operation op; if (currentOperation + 1 == operationsCount) return; currentOperation++; op = operationsList[currentOperation]; switch (op.type) { case OP_CREATE: circles_insert(op.circle); break; case OP_REMOVE: circles_remove(op.circle); break; case OP_UPDATE: currentOperation++; /* increment 2 to get the new value */ op = operationsList[currentOperation]; circles_update(op.circle); break; } } static int op_has_undo(void) { if (currentOperation < 0) return 0; return 1; } static int op_has_redo(void) { if (currentOperation + 1 == operationsCount) return 0; return 1; } /*********************************************** Interface ****************************************/ void update_buttons(Ihandle* ih) { Ihandle *undoButton = IupGetDialogChild(ih, "UNDO"); Ihandle *redoButton = IupGetDialogChild(ih, "REDO"); if (op_has_undo()) IupSetAttribute(undoButton, "ACTIVE", "YES"); else IupSetAttribute(undoButton, "ACTIVE", "NO"); if (op_has_redo()) IupSetAttribute(redoButton, "ACTIVE", "YES"); else IupSetAttribute(redoButton, "ACTIVE", "NO"); } int bt_undo_cb(Ihandle *self) { Ihandle *canvas = IupGetDialogChild(self, "CANVAS"); op_undo(); update_buttons(self); IupUpdate(canvas); return IUP_DEFAULT; } int bt_redo_cb(Ihandle *self) { Ihandle *canvas = IupGetDialogChild(self, "CANVAS"); op_redo(); update_buttons(self); IupUpdate(canvas); return IUP_DEFAULT; } static int canvas_button_cb(Ihandle *ih, int but, int pressed, int x, int y, char* status) { Ihandle *dlg = IupGetDialog(ih); if (but == IUP_BUTTON1 && pressed) { int id = circles_pick(x, y); if (id >= 0) IupSetInt(ih, "HIGHLIGHTEDCIRCLE", id); else { id = circles_add(x, y); op_add(OP_CREATE, circles_find(id)); update_buttons(ih); } IupUpdate(ih); } else if (but == IUP_BUTTON3 && pressed) { int highlightedCircle = IupGetInt(ih, "HIGHLIGHTEDCIRCLE"); int id = circles_pick(x, y); if (id == highlightedCircle) { Circle circle = circles_find(id); int new_r, old_r = circle.r; Ihandle *configDialog = (Ihandle *)IupGetAttribute(dlg, "CONFIGDIALOG"); Ihandle *val = IupGetDialogChild(configDialog, "VAL"); Ihandle *lbl = IupGetDialogChild(configDialog, "LBL"); IupSetStrf(lbl, "TITLE", "Adjust the diameter of the circle at (%d, %d)", circle.x, circle.y); IupSetInt(val, "VALUE", old_r); IupSetInt(configDialog, "CIRCLEID", id); IupPopup(configDialog, IUP_CENTERPARENT, IUP_CENTERPARENT); new_r = IupGetInt(val, "VALUE"); if (new_r != old_r) { op_add(OP_UPDATE, circle); /* add both old and new */ circle.r = new_r; op_add(OP_UPDATE, circle); update_buttons(ih); } } } (void)status; return IUP_DEFAULT; } static int canvas_action(Ihandle *ih) { int i, w, h, highlightedCircle; IupDrawBegin(ih); IupDrawGetSize(ih, &w, &h); IupSetAttribute(ih, "DRAWCOLOR", "255 255 255"); IupSetAttribute(ih, "DRAWSTYLE", "FILL"); IupDrawRectangle(ih, 0, 0, w - 1, h - 1); IupSetAttribute(ih, "DRAWCOLOR", "0 0 0"); IupSetAttribute(ih, "DRAWSTYLE", "STROKE"); highlightedCircle = IupGetInt(ih, "HIGHLIGHTEDCIRCLE"); for (i = 0; i < circlesCount; i++) { Circle circle = circlesList[i]; if (highlightedCircle == circle.id) { IupSetAttribute(ih, "DRAWCOLOR", "128 128 128"); IupSetAttribute(ih, "DRAWSTYLE", "FILL"); } else { IupSetAttribute(ih, "DRAWCOLOR", "0 0 0"); IupSetAttribute(ih, "DRAWSTYLE", "STROKE"); } IupDrawArc(ih, circle.x - circle.r, circle.y - circle.r, circle.x + circle.r, circle.y + circle.r, 0., 360.); } IupDrawEnd(ih); return IUP_DEFAULT; } int val_valuechanged_cb(Ihandle *self) { Ihandle* dlg = IupGetDialog(self); Ihandle* canvas = (Ihandle *)IupGetAttribute(self, "CANVAS"); int circleId = IupGetInt(dlg, "CIRCLEID"); int r = IupGetInt(self, "VALUE"); Circle circle = circles_find(circleId); circle.r = r; circles_update(circle); IupUpdate(canvas); return IUP_DEFAULT; } Ihandle *createCircleConfigDialog(Ihandle* parent_dlg) { Ihandle *dlg, *val, *lbl, *box; lbl = IupLabel(NULL); IupSetAttribute(lbl, "EXPAND", "HORIZONTAL"); IupSetAttribute(lbl, "TITLE", "Adjust the diameter of the circle at (100, 100)"); IupSetAttribute(lbl, "NAME", "LBL"); val = IupVal("HORIZONTAL"); IupSetAttribute(val, "EXPAND", "HORIZONTAL"); IupSetAttribute(val, "MIN", "0"); IupSetInt(val, "MAX", MAX_RADIUS); IupSetAttribute(val, "NAME", "VAL"); box = IupVbox(IupFill(), lbl, val, IupFill(), NULL); IupSetAttribute(box, "ALIGMENT", "ACENTER"); IupSetAttribute(box, "NMARGIN", "10x10"); dlg = IupDialog(box); IupSetAttribute(dlg, "TITLE", "Circle Config"); IupSetAttributeHandle(dlg, "PARENTDIALOG", parent_dlg); IupSetCallback(val, "VALUECHANGED_CB", (Icallback)val_valuechanged_cb); return dlg; } int main(int argc, char **argv) { Ihandle *dlg, *hbox, *vbox, *configDialog; Ihandle *undoButton, *redoButton, *canvas; IupOpen(&argc, &argv); undoButton = IupButton("Undo", NULL); IupSetAttribute(undoButton, "NAME", "UNDO"); IupSetAttribute(undoButton, "SIZE", "60"); redoButton = IupButton("Redo", NULL); IupSetAttribute(redoButton, "NAME", "REDO"); IupSetAttribute(redoButton, "SIZE", "60"); IupSetAttribute(redoButton, "ACTIVE", "NO"); IupSetAttribute(undoButton, "ACTIVE", "NO"); canvas = IupCanvas(NULL); IupSetAttribute(canvas, "NAME", "CANVAS"); IupSetAttribute(canvas, "EXPAND", "YES"); IupSetInt(canvas, "HIGHLIGHTEDCIRCLE", -1); hbox = IupHbox(IupFill(), undoButton, redoButton, IupFill(), NULL); vbox = IupVbox(hbox, canvas, NULL); IupSetAttribute(vbox, "NMARGIN", "10x10"); IupSetAttribute(vbox, "GAP", "10"); dlg = IupDialog(vbox); IupSetAttribute(dlg, "TITLE", "Circle Drawer"); IupSetAttribute(dlg, "SIZE", "300x150"); /* Registers callbacks */ IupSetCallback(undoButton, "ACTION", (Icallback)bt_undo_cb); IupSetCallback(redoButton, "ACTION", (Icallback)bt_redo_cb); IupSetCallback(canvas, "ACTION", (Icallback)canvas_action); IupSetCallback(canvas, "BUTTON_CB", (Icallback)canvas_button_cb); configDialog = createCircleConfigDialog(dlg); IupSetAttribute(configDialog, "CANVAS", (char *)canvas); IupSetAttribute(dlg, "CONFIGDIALOG", (char *)configDialog); IupShowXY(dlg, IUP_CENTER, IUP_CENTER); IupMainLoop(); IupClose(); return EXIT_SUCCESS; }