diff --git a/include/utils.h b/include/utils.h index 894072c..f640ebc 100644 --- a/include/utils.h +++ b/include/utils.h @@ -27,6 +27,9 @@ #include "common.h" +char *utilCreateString(char *format, ...); +char *utilCreateStringVArgs(char *format, va_list args); +gboolean utilFileExists(char *filename); GdkPixbuf *utilGetPixbufFromMemory(char *data, unsigned int len); WindowDataT *utilGetWindowData(GtkWidget *window); gboolean utilGetWidgetsFromMemory(char *name[], GtkWidget **widgets[], char *data, unsigned int len, gpointer userData); diff --git a/include/vecparse.h b/include/vecparse.h index be4c4f1..da85267 100644 --- a/include/vecparse.h +++ b/include/vecparse.h @@ -40,7 +40,6 @@ enum ParserKeywordE { PARSE_PALETTE, PARSE_PLOT, PARSE_RECTANGLE, - PARSE_RESET, PARSE_RETURN, // After this are keywords for things that aren't commands but still need to emit bytecode. PARSE_MATH, diff --git a/src/utils.c b/src/utils.c index 57f6235..beb6931 100644 --- a/src/utils.c +++ b/src/utils.c @@ -32,6 +32,48 @@ typedef struct WindowListS { static WindowListT *_windowList = NULL; +char *utilCreateString(char *format, ...) { + va_list args; + char *string; + + va_start(args, format); + string = utilCreateStringVArgs(format, args); + va_end(args); + + return string; +} + + +__attribute__((__format__(__printf__, 1, 0))) +char *utilCreateStringVArgs(char *format, va_list args) { + va_list argsCopy; + int32_t size = 0; + char *buffer = NULL; + + va_copy(argsCopy, args); + size = vsnprintf(NULL, 0, format, argsCopy) + 1; + va_end(argsCopy); + buffer = calloc(1, (size_t)size); + if (buffer) { + vsnprintf(buffer, (size_t)size, format, args); + } + + return buffer; +} + + +gboolean utilFileExists(char *filename) { + FILE *f = fopen(filename, "rb"); + + if (f) { + fclose(f); + return TRUE; + } + + return FALSE; +} + + GdkPixbuf *utilGetPixbufFromMemory(char *data, unsigned int len) { GdkPixbufLoader *loader; GdkPixbuf *pixbuf; diff --git a/src/vecparse.c b/src/vecparse.c index cae5c61..6ba89d8 100644 --- a/src/vecparse.c +++ b/src/vecparse.c @@ -318,7 +318,6 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { { "PALETTE", PARSE_PALETTE }, { "PLOT", PARSE_PLOT }, { "RECTANGLE", PARSE_RECTANGLE }, - { "RESET", PARSE_RESET }, { "RETURN", PARSE_RETURN }, { NULL, PARSE_NONE } }; @@ -657,11 +656,6 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) { lineOkay = TRUE; break; - case PARSE_RESET: - outputByte(bytecode, PARSE_RESET); - lineOkay = TRUE; - break; - case PARSE_RETURN: // Return outputByte(bytecode, PARSE_RETURN); diff --git a/src/vector.c b/src/vector.c index fb482e4..48a0170 100644 --- a/src/vector.c +++ b/src/vector.c @@ -37,6 +37,8 @@ #include "vecparse.h" +#define VICTOR_VERSION "1.00" + #define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l) #define MARGIN_SCRIPT_FOLD_INDEX 1 #define MARKER_ERROR_ARROW 0 @@ -50,6 +52,7 @@ typedef struct VectorDataS { GtkWidget *drawVectorImage; GtkWidget *boxVectorForEditor; GtkWidget *editor; + GtkWidget *fileVectorTraceImage; ScintillaObject *sci; void *pLexer; int id; @@ -60,6 +63,9 @@ typedef struct VectorDataS { jlContextT *jlc; double traceImagePercent; float *variables; + char *filename; + char *title; + char *tracename; } VectorDataT; @@ -72,9 +78,20 @@ EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, g 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 loadTraceImage(VectorDataT *self, char *filename); +EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData); +EVENT void menuVectorEditCut(GtkWidget *object, gpointer userData); +EVENT void menuVectorEditDelete(GtkWidget *object, gpointer userData); +EVENT void menuVectorEditPaste(GtkWidget *object, gpointer userData); EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData); +EVENT void menuVectorFileNew(GtkWidget *object, gpointer userData); +EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData); +EVENT void menuVectorFileSave(GtkWidget *object, gpointer userData); +EVENT void menuVectorFileSaveAs(GtkWidget *object, gpointer userData); +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); static float variable(VectorDataT *self, unsigned char byte); static int word(VectorDataT *self, unsigned short word); EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData); @@ -212,7 +229,7 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi // Release code. DEL(code); // Mark text dirty. SCN_SAVEPOINTLEFT isn't being reliable. - self->windowData.isDirty = TRUE; + setDirty(self, TRUE); } break; @@ -240,8 +257,19 @@ static int getWord(VecByteCodeT *bytecode, int *index) { EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { - VectorDataT *self = (VectorDataT *)userData; - char *filename; + VectorDataT *self = (VectorDataT *)userData; + char *temp = NULL; + + printf("fileVectorTraceImageFileSet fired\n"); + temp = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object)); + loadTraceImage(self, temp); + DEL(temp); + + setDirty(self, TRUE); +} + + +static void loadTraceImage(VectorDataT *self, char *filename) { int x; int y; int n; @@ -251,9 +279,11 @@ EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { cairo_surface_t *temp; cairo_t *cr; - filename = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object)); - image = stbi_load(filename, &x, &y, &n, 4); // Cairo is always 32 bit even with no alpha. Derp. - DEL(filename); + if (self->tracename != NULL) DEL(self->tracename); + self->tracename = strdup(filename); + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->fileVectorTraceImage), filename); + + image = stbi_load(self->tracename, &x, &y, &n, 4); // Cairo is always 32 bit even with no alpha. Derp. if (image != NULL) { // Create Cairo surface the same size as the loaded image. @@ -296,6 +326,26 @@ EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { } +EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData) { + +} + + +EVENT void menuVectorEditCut(GtkWidget *object, gpointer userData) { + +} + + +EVENT void menuVectorEditDelete(GtkWidget *object, gpointer userData) { + +} + + +EVENT void menuVectorEditPaste(GtkWidget *object, gpointer userData) { + +} + + EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData) { VectorDataT *self = (VectorDataT *)userData; @@ -305,6 +355,183 @@ EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData) { } +EVENT void menuVectorFileNew(GtkWidget *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)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); + } + } +} + + +EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + GtkWidget *dialog; + GtkFileFilter *filter; + FILE *in; + char *line = NULL; + size_t len = 0; + size_t count = 0; + + if (self->windowData.isDirty) { + if (!utilQuestionDialog(self->windowData.window, "Open", "You have unsaved changes. Open different file?")) { + return; + } + } + + dialog = gtk_file_chooser_dialog_new("Open", + GTK_WINDOW(self->windowData.window), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.vic"); + 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); + + in = fopen(self->filename, "rt"); + if (in != NULL) { + while (getline(&line, &len, in) != -1) { + switch (count) { + case 0: // Version Number + break; + + case 1: // Trace image name + line[strlen(line) - 1] = 0; + if (utilFileExists(line)) { + loadTraceImage(self, line); + } + break; + + case 2: // Separator line + break; + + default: // Code for editor + SSM(SCI_ADDTEXT, strlen(line), (sptr_t)line); + break; + } + count++; + } + fclose(in); + if (line != NULL) DEL(line); + setDirty(self, FALSE); // Do again - loading text marks us dirty. + } else { + //***TODO*** Something bad happened. + } + } + + gtk_widget_destroy(dialog); +} + + +EVENT void menuVectorFileSave(GtkWidget *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + int length = SSM(SCI_GETLENGTH, 0, 0); + FILE *out = NULL; + char *code = NULL; + + // Do we need to save? + if (self->windowData.isDirty == TRUE) { + + // Do we have a filename? If not, kick 'em to SaveAs. + if (self->filename == NULL) { + menuVectorFileSaveAs(object, userData); + return; + } + + // Allocate space to fetch code from editor. + code = (char *)malloc(length + 1); + if (!code) { + //***TODO*** Something bad happened. + return; + } + + // Fetch code. + SSM(SCI_GETTEXT, length, (sptr_t)code); + + out = fopen(self->filename, "wt"); + if (out != NULL) { + // Save! + fprintf(out, "%s\n", VICTOR_VERSION); + fprintf(out, "%s\n", (self->tracename != NULL ? self->tracename : "")); + fprintf(out, "------------------------------------------------------------------------------\n"); + fprintf(out, "%s\n", code); + fclose(out); + // Release code. + DEL(code); + // We're clean now. + setDirty(self, FALSE); + } else { + // Release code. + DEL(code); + + //***TODO*** Something bad happened. + } + } +} + + +EVENT void menuVectorFileSaveAs(GtkWidget *object, gpointer userData) { + VectorDataT *self = (VectorDataT *)userData; + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save As", + GTK_WINDOW(self->windowData.window), + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + + 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)); + menuVectorFileSave(object, self); + } + + gtk_widget_destroy(dialog); +} + + +EVENT void menuVectorHelpVector(GtkWidget *object, gpointer userData) { + +} + + static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { int x1; int y1; @@ -324,6 +551,12 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { self->variables = NULL; + // Reset JoeyLib draw state. + jlPaletteDefault(self->jlc); + jlDrawColorSet(self->jlc, 0); + jlDrawClear(self->jlc); + jlDrawColorSet(self->jlc, 15); + while (index < bytecode->length) { switch (bytecode->bytes[index++]) { @@ -575,14 +808,6 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { jlDrawBoxFilled(self->jlc, x1, y1, x2, y2); break; - case PARSE_RESET: - printf("Reset\n"); - jlPaletteDefault(self->jlc); - jlDrawColorSet(self->jlc, 0); - jlDrawClear(self->jlc); - jlDrawColorSet(self->jlc, 15); - break; - case PARSE_RETURN: index = arrpop(stack); printf("Return %d\n", index); @@ -618,6 +843,28 @@ EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userDat } +static void setDirty(VectorDataT *self, gboolean dirty) { + char *title; + + self->windowData.isDirty = dirty; + if (dirty) { + if (self->filename) { + title = utilCreateString("%s - %s *", self->title, self->filename); + } else { + title = utilCreateString("%s - (no name) *", self->title); + } + } else { + if (self->filename) { + title = utilCreateString("%s - %s", self->title, self->filename); + } else { + title = utilCreateString("%s", self->title); + } + } + gtk_window_set_title(GTK_WINDOW(self->windowData.window), title); + DEL(title); +} + + static float variable(VectorDataT *self, unsigned char byte) { int value; @@ -676,8 +923,8 @@ EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) { void winVectorCreate(void) { VectorDataT *self; - char *widgetNames[] = { "winVector", "boxVectorForEditor", "drawVectorImage", NULL }; - GtkWidget **widgets[] = { NULL, NULL, NULL }; + char *widgetNames[] = { "winVector", "boxVectorForEditor", "drawVectorImage", "fileVectorTraceImage", NULL }; + GtkWidget **widgets[] = { NULL, NULL, NULL, NULL }; // Set up instance data. self = NEW(VectorDataT); @@ -687,8 +934,12 @@ void winVectorCreate(void) { widgets[0] = &self->windowData.window; widgets[1] = &self->boxVectorForEditor; widgets[2] = &self->drawVectorImage; + widgets[3] = &self->fileVectorTraceImage; utilGetWidgetsFromMemory(widgetNames, widgets, EMBEDDED(___ui_Vector_glade), self); + // Grab title. + self->title = strdup(gtk_window_get_title(GTK_WINDOW(self->windowData.window))); + // Add missing event to drawVectorImage gtk_widget_add_events(self->drawVectorImage, GDK_BUTTON_PRESS_MASK); g_signal_connect(G_OBJECT(self->drawVectorImage), "button-press-event", G_CALLBACK(drawVectorImageClick), self); @@ -748,8 +999,8 @@ void winVectorCreate(void) { SSM(SCI_MARKERSETBACK, MARKER_ERROR_HIGHLIGHT, 127 | (0 << 8) | (0 << 16)); // RGB // Debug + /* SSM(SCI_INSERTTEXT, 0, (sptr_t) - "reset\n\n" "goto red\n\n" "yellow:\n" "\t%yellow = %red\n" @@ -767,6 +1018,7 @@ void winVectorCreate(void) { "\tgoto yellow\n\n" "end:\n" ); + */ // Connect editor to our code. g_signal_connect(G_OBJECT(self->editor), "sci-notify", G_CALLBACK(editorVectorNotify), self); @@ -797,7 +1049,10 @@ static void winVectorDelete(gpointer userData) { cairo_surface_destroy(self->surface); cairo_surface_destroy(self->scaled); cairo_surface_destroy(self->target); - if (self->trace != NULL) cairo_surface_destroy(self->trace); + if (self->trace != NULL) cairo_surface_destroy(self->trace); + if (self->filename != NULL) DEL(self->filename); + if (self->title != NULL) DEL(self->title); + if (self->tracename != NULL) DEL(self->tracename); DEL(self); } diff --git a/ui/Vector.glade b/ui/Vector.glade index 53e5f0c..c72621e 100644 --- a/ui/Vector.glade +++ b/ui/Vector.glade @@ -58,6 +58,7 @@ False _New True + @@ -66,6 +67,7 @@ False _Open... True + @@ -74,6 +76,7 @@ False _Save True + @@ -82,6 +85,7 @@ False Save _As... True + @@ -119,6 +123,7 @@ False Cut True + @@ -127,6 +132,7 @@ False _Copy True + @@ -135,6 +141,7 @@ False Paste True + @@ -143,6 +150,7 @@ False Delete True + @@ -165,6 +173,7 @@ False Vector Editor... True +