From 12b0d090a7ed25b2380c847d4be4d79cd8275654 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 11 Dec 2022 20:41:05 -0600 Subject: [PATCH] Toolbar drawing is working! CIRCLE syntax changed. There are issues in JoeyLib with drawing clipped circles. --- src/draw.c | 2 + src/vecparse.c | 6 +- src/vector.c | 404 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 309 insertions(+), 103 deletions(-) diff --git a/src/draw.c b/src/draw.c index 1f55594..0b80f8c 100644 --- a/src/draw.c +++ b/src/draw.c @@ -89,6 +89,8 @@ void _jlDrawCircleClipped(jlContextT *c, jint16 x0, jint16 y0, jint16 radius) { jint16 dy = 1; jint16 err = dx - (jint16)(radius << 1); + //***TODO*** This produces negative Y values for large circles. + while (x >= y) { if ((x0 + x < 320) && (y0 + y < 200)) jlDrawPixelSet(c, x0 + x, y0 + y); if ((x0 + y < 320) && (y0 + x < 200)) jlDrawPixelSet(c, x0 + y, y0 + x); diff --git a/src/vecparse.c b/src/vecparse.c index 4cbcc0c..fc70800 100644 --- a/src/vecparse.c +++ b/src/vecparse.c @@ -450,10 +450,10 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { break; case PARSE_CIRCLE: - // Circle (value) at (value),(value) - if (!parserGetX(&tokenEnd, &variables, &y2)) break; - if (parserGetWord("AT", &tokenEnd) < 0) break; + // Circle (value),(value) radius (value) if (!parserGetXY(&tokenEnd, &variables, &x1, &y1)) break; + if (parserGetWord("RADIUS", &tokenEnd) < 0) break; + if (!parserGetX(&tokenEnd, &variables, &y2)) break; if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break; if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break; outputByte(bytecode, PARSE_CIRCLE); diff --git a/src/vector.c b/src/vector.c index 87eb6b8..e2fb431 100644 --- a/src/vector.c +++ b/src/vector.c @@ -47,6 +47,24 @@ #define PREVIEW_HEIGHT 400 +enum ClickStateE { + CLICK_NONE = 0, + CLICK_BOX_1, + CLICK_BOX_2, + CLICK_CIRCLE_1, + CLICK_CIRCLE_2, + CLICK_ELLIPSE_1, + CLICK_ELLIPSE_2, + CLICK_FILL_1, + CLICK_LINE_1, + CLICK_LINE_2, + CLICK_LINE_X, + CLICK_PLOT_1, + CLICK_RECTANGLE_1, + CLICK_RECTANGLE_2 +}; +typedef enum ClickStateE ClickStateT; + typedef struct VectorDataS { WindowDataT windowData; GtkWidget *drawVectorImage; @@ -69,6 +87,9 @@ typedef struct VectorDataS { char *buffer; int bufferLength; PointT **pointList; + gboolean allowRedraw; + ClickStateT clickState; + PointT clickTempPoint; } VectorDataT; @@ -82,6 +103,8 @@ EVENT gboolean drawVectorImageMotionEvent(GtkWidget *widget, GdkEventMotion *ev EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData); EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData); static int getWord(VecByteCodeT *bytecode, int *index); +static void insertCommand(VectorDataT *self, char *command); +static void insertText(VectorDataT *self, char *text); static void loadTraceImage(VectorDataT *self, char *filename); EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData); EVENT void menuVectorEditCut(GtkWidget *object, gpointer userData); @@ -96,13 +119,13 @@ EVENT void menuVectorHelpVector(GtkWidget *object, gpointer userData); static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self); EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData); static void setDirty(VectorDataT *self, gboolean dirty); -EVENT void toolBoxClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolCircleClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolEllipseClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolFillClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolLineClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolPlotClicked(GtkToolButton *object, gpointer user_data); -EVENT void toolRectangleClicked(GtkToolButton *object, gpointer user_data); +EVENT void toolBoxClicked(GtkToolButton *object, gpointer userData); +EVENT void toolCircleClicked(GtkToolButton *object, gpointer userData); +EVENT void toolEllipseClicked(GtkToolButton *object, gpointer userData); +EVENT void toolFillClicked(GtkToolButton *object, gpointer userData); +EVENT void toolLineClicked(GtkToolButton *object, gpointer userData); +EVENT void toolPlotClicked(GtkToolButton *object, gpointer userData); +EVENT void toolRectangleClicked(GtkToolButton *object, gpointer userData); static float variable(VectorDataT *self, unsigned char byte); static int word(VectorDataT *self, unsigned short word); EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData); @@ -131,11 +154,104 @@ EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpoint (void)object; + if (event->type == GDK_BUTTON_PRESS) { + + // Clamp coordinates, just in case. + if (x < 0) x = 0; + if (x > 319) x = 319; + if (y < 0) y = 0; + if (y > 199) y = 199; + snprintf(temp, 8, "%d,%d", x, y); + + switch (self->clickState) { + case CLICK_NONE: + // Do nothing. + break; + + case CLICK_BOX_1: + insertText(self, temp); + insertText(self, " TO "); + self->clickState++; + break; + + case CLICK_BOX_2: + insertText(self, temp); + self->clickState = CLICK_NONE; + break; + + case CLICK_CIRCLE_1: + self->clickTempPoint.x = x; + self->clickTempPoint.y = y; + insertText(self, temp); + insertText(self, " RADIUS "); + self->clickState++; + break; + + case CLICK_CIRCLE_2: + x = sqrt(pow(x - self->clickTempPoint.x, 2) + pow(y - self->clickTempPoint.y, 2)); + snprintf(temp, 8, "%d", x); + insertText(self, temp); + self->clickState = CLICK_NONE; + break; + + case CLICK_ELLIPSE_1: + insertText(self, temp); + insertText(self, " TO "); + self->clickState++; + break; + + case CLICK_ELLIPSE_2: + insertText(self, temp); + self->clickState = CLICK_NONE; + break; + + case CLICK_FILL_1: + insertText(self, temp); + self->clickState = CLICK_NONE; + break; + + case CLICK_LINE_1: + insertText(self, temp); + insertText(self, " TO "); + self->clickState++; + break; + + case CLICK_LINE_2: + insertText(self, temp); + self->clickState++; + break; + + case CLICK_LINE_X: + insertText(self, " TO "); + insertText(self, temp); + break; + + case CLICK_PLOT_1: + insertText(self, temp); + insertText(self, " TO "); + self->clickState = CLICK_NONE; + break; + + case CLICK_RECTANGLE_1: + insertText(self, temp); + insertText(self, " TO "); + self->clickState++; + break; + + case CLICK_RECTANGLE_2: + insertText(self, temp); + self->clickState = CLICK_NONE; + break; + } + } + + /* if (event->type == GDK_DOUBLE_BUTTON_PRESS) { snprintf(temp, 8, "%d,%d", x, y); SSM(SCI_ADDTEXT, strlen(temp), (sptr_t)temp); SSM(SCI_GRABFOCUS, 0, 0); } + */ } @@ -196,41 +312,45 @@ EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer user EVENT gboolean drawVectorImageMotionEvent(GtkWidget *widget, GdkEventMotion *event, gpointer userData) { - VectorDataT *self = (VectorDataT *)userData; - int closest = -1; - int distance = INT_MAX; - int x = (int)(event->x * 0.5); - int y = (int)(event->y * 0.5); - int temp; - int i; - char buffer[8]; + VectorDataT *self = (VectorDataT *)userData; + int closest = -1; + int distance = INT_MAX; + int x = (int)(event->x * 0.5); + int y = (int)(event->y * 0.5); + int temp; + int i; + char buffer[8]; - // Find closest point to mouse pointer. - for (i=0; ipointList); i++) { - temp = sqrt(pow(x - self->pointList[i]->x, 2) + pow(y - self->pointList[i]->y, 2)); - if (temp < distance) { - closest = i; - distance = temp; + // If we're in a drawing action, bail out of this. + if (self->clickState != CLICK_NONE) return FALSE; + + // If they're holding the button down, update the point. + if (event->state & GDK_BUTTON1_MASK) { + + // Find closest point to mouse pointer. + for (i = 0; i < arrlen(self->pointList); i++) { + temp = sqrt(pow(x - self->pointList[i]->x, 2) + pow(y - self->pointList[i]->y, 2)); + if (temp < distance) { + closest = i; + distance = temp; + } } - } - // Did we find a point to edit? - if (closest >= 0) { - // Select first occurrence of it in the text editor. - snprintf(buffer, 8, "%d,%d", self->pointList[closest]->x, self->pointList[closest]->y); - SSM(SCI_TARGETWHOLEDOCUMENT, 0, 0); - SSM(SCI_SETSEARCHFLAGS, SCFIND_NONE, 0); - temp = SSM(SCI_SEARCHINTARGET, strlen(buffer), (sptr_t)buffer); - // Did we find it in the text? - if (temp >= 0) { - // Select the text. - SSM(SCI_SETSEL, temp, temp + strlen(buffer)); - // If they're holding the button down, update the point. - if (event->state & GDK_BUTTON1_MASK) { + // Did we find a point to edit? + if (closest >= 0) { + // Select first occurrence of it in the text editor. + snprintf(buffer, 8, "%d,%d", self->pointList[closest]->x, self->pointList[closest]->y); + SSM(SCI_TARGETWHOLEDOCUMENT, 0, 0); + SSM(SCI_SETSEARCHFLAGS, SCFIND_NONE, 0); + temp = SSM(SCI_SEARCHINTARGET, strlen(buffer), (sptr_t)buffer); + // Did we find it in the text? + if (temp >= 0) { + // Select the text. + SSM(SCI_SETSEL, temp, temp + strlen(buffer)); // Are these values sane? - if (x < 0) x = 0; + if (x < 0) x = 0; if (x > 319) x = 319; - if (y < 0) y = 0; + if (y < 0) y = 0; if (y > 199) y = 199; snprintf(buffer, 8, "%d,%d", x, y); SSM(SCI_REPLACETARGET, -1, (sptr_t)buffer); @@ -257,30 +377,35 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi switch (notifyData->nmhdr.code) { case SCN_MODIFIED: if (notifyData->modificationType & SC_MOD_INSERTTEXT || notifyData->modificationType & SC_MOD_DELETETEXT) { - // Allocate space to fetch code from editor. - utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, length); - // Clear error markers. - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); - // Fetch code. - SSM(SCI_GETTEXT, length, (sptr_t)self->buffer); - // Parse code. - byteCode.bytes = NULL; - byteCode.length = 0; - byteCode.bufferSize = 0; - lineNumber = vecparser(self->buffer, &byteCode); - if (lineNumber >= 0) { - // Mark lines that fail to parse. - SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); - SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); - } else { + // Cancel any in-progress drawing action. + self->clickState = CLICK_NONE; + // Is it safe to repaint now? + if (self->allowRedraw) { + // Allocate space to fetch code from editor. + utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, length); + // Clear error markers. + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); + // Fetch code. + SSM(SCI_GETTEXT, length, (sptr_t)self->buffer); + // Parse code. + byteCode.bytes = NULL; + byteCode.length = 0; + byteCode.bufferSize = 0; + lineNumber = vecparser(self->buffer, &byteCode); + if (lineNumber >= 0) { + // Mark lines that fail to parse. + SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); + SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); + } else { #ifdef DEBUG - FILE *out = fopen("bytecode.bin", "wb"); - fwrite(byteCode.bytes, byteCode.length, 1, out); - fclose(out); + FILE *out = fopen("bytecode.bin", "wb"); + fwrite(byteCode.bytes, byteCode.length, 1, out); + fclose(out); #endif - // All good! - renderBytecode(&byteCode, self); + // All good! + renderBytecode(&byteCode, self); + } } // Release bytecode. @@ -301,6 +426,19 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi } +EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + char *temp = NULL; + + debug("fileVectorTraceImageFileSet fired\n"); + temp = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object)); + loadTraceImage(self, temp); + DEL(temp); + + setDirty(self, TRUE); +} + + static int getWord(VecByteCodeT *bytecode, int *index) { int word; @@ -313,16 +451,56 @@ static int getWord(VecByteCodeT *bytecode, int *index) { } -EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { - VectorDataT *self = (VectorDataT *)userData; - char *temp = NULL; +static void insertCommand(VectorDataT *self, char *command) { + int pos; + int line; + int len; + char *newLine = "\n\0"; - debug("fileVectorTraceImageFileSet fired\n"); - temp = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object)); - loadTraceImage(self, temp); - DEL(temp); + // Position Scintilla cursor at the start of the current line. + pos = SSM(SCI_GETCURRENTPOS, 0, 0); + line = SSM(SCI_LINEFROMPOSITION, pos, 0); + SSM(SCI_GOTOLINE, line, 0); - setDirty(self, TRUE); + // I'm using 'buffer' and don't want a redraw until I'm done. + self->allowRedraw = FALSE; + + // Collect leading whitespace on this line. + len = SSM(SCI_GETLINE, line, NULL); + utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, len + 1); + SSM(SCI_GETLINE, line, (sptr_t)self->buffer); + self->buffer[len + 1] = 0; + pos = 0; + while (self->buffer[pos] == ' ' || self->buffer[pos] == '\t') { + pos++; + if (pos > len - 1) { + pos = 0; + break; + } + } + self->buffer[pos] = 0; + + // Insert CR on next line and leading whitespace of previous line. + line++; + SSM(SCI_GOTOLINE, line, 0); + insertText(self, "\n"); + SSM(SCI_GOTOLINE, line, 0); + insertText(self, self->buffer); + insertText(self, command); + insertText(self, " "); + + // Drawing is OK again. + self->allowRedraw = TRUE; +} + + +static void insertText(VectorDataT *self, char *text) { + int pos; + + pos = SSM(SCI_GETCURRENTPOS, 0, 0); + SSM(SCI_INSERTTEXT, -1, (sptr_t)text); + pos += strlen(text); + SSM(SCI_GOTOPOS, pos, 0); } @@ -434,31 +612,33 @@ EVENT void menuVectorFileNew(GtkWidget *object, gpointer userData) { (void)object; if (self->windowData.isDirty == TRUE) { - if (utilQuestionDialog(self->windowData.window, "New", "You have unsaved changes. Start new?")) { - // Clear editor. - SSM(SCI_CLEARALL, 0, 0); - // Clear error markers. - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); - SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); - // Reset JoeyLib drawing state. - jlPaletteDefault(self->jlc); - jlDrawColorSet(self->jlc, 0); - jlDrawClear(self->jlc); - jlDrawColorSet(self->jlc, 15); - // Destroy trace image, if any. - if (self->trace != NULL) { - cairo_surface_destroy(self->trace); - self->trace = NULL; - gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->fileVectorTraceImage)); - } - // Clear any filename. - if (self->filename != NULL) DEL(self->filename); - // Refresh widget. - gtk_widget_queue_draw(self->drawVectorImage); - // Mark clean. - setDirty(self, FALSE); + if (!utilQuestionDialog(self->windowData.window, "New", "You have unsaved changes. Start new?")) { + return; } } + + // Clear editor. + SSM(SCI_CLEARALL, 0, 0); + // Clear error markers. + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0); + SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0); + // Reset JoeyLib drawing state. + jlPaletteDefault(self->jlc); + jlDrawColorSet(self->jlc, 0); + jlDrawClear(self->jlc); + jlDrawColorSet(self->jlc, 15); + // Destroy trace image, if any. + if (self->trace != NULL) { + cairo_surface_destroy(self->trace); + self->trace = NULL; + gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->fileVectorTraceImage)); + } + // Clear any filename. + if (self->filename != NULL) DEL(self->filename); + // Refresh widget. + gtk_widget_queue_draw(self->drawVectorImage); + // Mark clean. + setDirty(self, FALSE); } @@ -488,13 +668,13 @@ EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData) { gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - if (self->filename != NULL) DEL(self->filename); - self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - // Use our New code to reset the editor state before we load. setDirty(self, FALSE); menuVectorFileNew(object, userData); + if (self->filename != NULL) DEL(self->filename); + self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + in = fopen(self->filename, "rt"); if (in != NULL) { self->buffer[0] = 0; @@ -523,6 +703,7 @@ EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData) { fclose(in); if (line != NULL) DEL(line); SSM(SCI_ADDTEXT, strlen(self->buffer), (sptr_t)self->buffer); + //SSM(SCI_CONVERTEOLS, SC_EOL_CR, 0); setDirty(self, FALSE); // Do again - loading text marks us dirty. } else { //***TODO*** Something bad happened. @@ -980,38 +1161,59 @@ static void setDirty(VectorDataT *self, gboolean dirty) { } -EVENT void toolBoxClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolBoxClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "BOX"); + self->clickState = CLICK_BOX_1; } -EVENT void toolCircleClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolCircleClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "CIRCLE"); + self->clickState = CLICK_CIRCLE_1; } -EVENT void toolEllipseClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolEllipseClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "ELLIPSE"); + self->clickState = CLICK_ELLIPSE_1; } -EVENT void toolFillClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolFillClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "FILL"); + self->clickState = CLICK_FILL_1; } -EVENT void toolLineClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolLineClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "LINE"); + self->clickState = CLICK_LINE_1; } -EVENT void toolPlotClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolPlotClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "PLOT"); + self->clickState = CLICK_PLOT_1; } -EVENT void toolRectangleClicked(GtkToolButton *object, gpointer user_data) { +EVENT void toolRectangleClicked(GtkToolButton *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + insertCommand(self, "RECTANGLE"); + self->clickState = CLICK_RECTANGLE_1; } @@ -1090,6 +1292,7 @@ void winVectorCreate(void) { // Set up instance data. self = NEW(VectorDataT); self->windowData.closeWindow = winVectorClose; + self->allowRedraw = TRUE; // Set up working buffer. utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, 1024); @@ -1134,6 +1337,7 @@ void winVectorCreate(void) { SSM(SCI_STYLESETBACK, STYLE_DEFAULT, 0); SSM(SCI_STYLECLEARALL, 0, 0); SSM(SCI_SETTABWIDTH, 3, 0); + //SSM(SCI_SETEOLMODE, SC_EOL_CR, 0); SSM(SCI_SETMARGINWIDTHN, 0, (int)SSM(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"_99999")); SSM(SCI_SETMARGINWIDTHN, 1, 16); SSM(SCI_SETWRAPMODE, SC_WRAP_NONE, 0);