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
+