Open/Save/SaveAs working.

This commit is contained in:
Scott Duensing 2022-12-05 21:33:26 -06:00
parent db4c7a3204
commit 116d469f5c
6 changed files with 327 additions and 25 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -58,6 +58,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">_New</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorFileNew" swapped="no"/>
</object>
</child>
<child>
@ -66,6 +67,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Open...</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorFileOpen" swapped="no"/>
</object>
</child>
<child>
@ -74,6 +76,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Save</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorFileSave" swapped="no"/>
</object>
</child>
<child>
@ -82,6 +85,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">Save _As...</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorFileSaveAs" swapped="no"/>
</object>
</child>
<child>
@ -119,6 +123,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">Cut</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorEditCut" swapped="no"/>
</object>
</child>
<child>
@ -127,6 +132,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Copy</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorEditCopy" swapped="no"/>
</object>
</child>
<child>
@ -135,6 +141,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">Paste</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorEditPaste" swapped="no"/>
</object>
</child>
<child>
@ -143,6 +150,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">Delete</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorEditDelete" swapped="no"/>
</object>
</child>
</object>
@ -165,6 +173,7 @@
<property name="can-focus">False</property>
<property name="label" translatable="yes">Vector Editor...</property>
<property name="use-underline">True</property>
<signal name="activate" handler="menuVectorHelpVector" swapped="no"/>
</object>
</child>
</object>