From 02103f36efbc630b1ebd6e72ab0cf032551b49e1 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 18 Dec 2022 21:05:10 -0600 Subject: [PATCH] Project loading mostly implemented. --- include/common.h | 2 + include/utils.h | 2 + include/vector.h | 2 +- src/draw.c | 5 -- src/joeydev.c | 2 +- src/palette.c | 6 +- src/project.c | 175 +++++++++++++++++++++++++++++++++++++++-------- src/utils.c | 75 ++++++++++++++++++++ src/vector.c | 170 +++++++++++++++++---------------------------- 9 files changed, 295 insertions(+), 144 deletions(-) diff --git a/include/common.h b/include/common.h index 8568cbd..6b07be7 100644 --- a/include/common.h +++ b/include/common.h @@ -51,6 +51,8 @@ typedef struct WindowDataS { GtkWidget *window; gboolean (*closeWindow)(GtkWidget* widget, gpointer data); gboolean isDirty; + char *title; + char *filename; } WindowDataT; diff --git a/include/utils.h b/include/utils.h index 03e822a..097c977 100644 --- a/include/utils.h +++ b/include/utils.h @@ -31,9 +31,11 @@ char *utilCreateString(char *format, ...); char *utilCreateStringVArgs(char *format, va_list args); void utilEnsureBufferSize(unsigned char **buffer, int *length, int wanted); gboolean utilFileExists(char *filename); +gboolean utilFileOpen(WindowDataT *self, char *extension, char *what); WindowDataT *utilGetWindowData(GtkWidget *window); gboolean utilGetWidgetsFromMemory(char *resource, char *name[], GtkWidget **widgets[], gpointer userData); gboolean utilQuestionDialog(GtkWidget *parent, char *title, char *question); +void utilSetDirty(WindowDataT *self, gboolean dirty); void utilWindowRegister(gpointer windowData); int utilWindowsCloseAll(void); int utilWindowsOpen(void); diff --git a/include/vector.h b/include/vector.h index 9163cd0..676a3eb 100644 --- a/include/vector.h +++ b/include/vector.h @@ -24,7 +24,7 @@ #define VECTOR_H -void winVectorCreate(void); +void winVectorCreate(char *filename); #endif // VECTOR_H diff --git a/src/draw.c b/src/draw.c index 1d8fcf5..47b7018 100644 --- a/src/draw.c +++ b/src/draw.c @@ -28,11 +28,6 @@ #include "draw.h" -//***TODO*** This entire thing should render to a 4 bit buffer and only -// convert to truecolor when needed. That way we can implement palette -// side effects. - - typedef struct { jint16 StartX; jint16 EndX; diff --git a/src/joeydev.c b/src/joeydev.c index d78f451..717edac 100644 --- a/src/joeydev.c +++ b/src/joeydev.c @@ -81,7 +81,7 @@ EVENT void toolJoeyDevVectorClicked(GtkWidget *widget, gpointer userData) { (void)widget; (void)userData; - winVectorCreate(); + winVectorCreate(NULL); } diff --git a/src/palette.c b/src/palette.c index 62227ef..afee1cd 100644 --- a/src/palette.c +++ b/src/palette.c @@ -104,15 +104,15 @@ GtkWidget *palette_dialog_new(GtkWidget *parent, char *title, unsigned char r, u self = NEW(PaletteDialogDataT); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_spacing(vbox, 10); + gtk_box_set_spacing(GTK_BOX(vbox), 10); self->drawingArea = gtk_drawing_area_new(); gtk_widget_set_size_request(self->drawingArea, 128, 64); g_signal_connect(G_OBJECT(self->drawingArea), "draw", G_CALLBACK(paletteDialogDraw), self); grid = gtk_grid_new(); - gtk_grid_set_column_spacing(grid, 10); - gtk_grid_set_row_spacing(grid, 10); + gtk_grid_set_column_spacing(GTK_GRID(grid), 10); + gtk_grid_set_row_spacing(GTK_GRID(grid), 10); labelRed = gtk_label_new("Red:"); labelGreen = gtk_label_new("Green:"); diff --git a/src/project.c b/src/project.c index b8ab90e..d704be8 100644 --- a/src/project.c +++ b/src/project.c @@ -20,27 +20,54 @@ */ +#pragma clang diagnostic push +#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" + + #include "common.h" #include "project.h" #include "utils.h" +enum ProjectSectionTypeE { + SECTION_HEADER = 0, + SECTION_CODE, + SECTION_BITMAP, + SECTION_STENCIL, + SECTION_VECTOR, + SECTION_SOUND, + SECTION_MUSIC, + SECTION_RAW_DATA, + SECTION_COOKED_DATA, + SECTION_COUNT +}; +typedef enum ProjectSectionTypeE ProjectSectionTypeT; + + typedef struct ProjectDataS { - WindowDataT windowData; - GtkWidget *treeProject; - GtkTreeStore *storeProject; - GtkTreeIter iterTop; - GtkTreeIter iterHeaders; - GtkTreeIter iterCode; - GtkTreeIter iterBitmaps; - GtkTreeIter iterVectors; - GtkTreeIter iterSounds; - GtkTreeIter iterMusic; - GtkTreeIter iterData; - char *title; + WindowDataT windowData; + GtkWidget *treeProject; + GtkTreeStore *storeProject; + char *title; + char *filename; } ProjectDataT; +static char *_SectionName[] = { + "Header", + "Code", + "Bitmap", + "Stencil", + "Vector", + "Sound", + "Music", + "Raw Data", + "Cooked Data", + NULL +}; + + +static void loadProject(ProjectDataT *self); EVENT void menuProjectFileNew(GtkWidget *object, gpointer userData); EVENT void menuProjectFileOpen(GtkWidget *object, gpointer userData); EVENT void menuProjectFileSave(GtkWidget *object, gpointer userData); @@ -56,13 +83,104 @@ EVENT gboolean winProjectClose(GtkWidget *object, gpointer userData); static void winProjectDelete(gpointer userData); +static void loadProject(ProjectDataT *self) { + FILE *in = NULL; + char *line = NULL; + size_t len = 0; + size_t count = 0; + int16_t extension; + int end; + char temp[4]; + GtkTreeIter iterParent; + GtkTreeIter iter; + ProjectSectionTypeT section; + + in = fopen(self->windowData.filename, "rt"); + if (in != NULL) { + while (getline(&line, &len, in) != -1) { + if (strlen(line) > 0) line[strlen(line) - 1] = 0; + switch (count) { + case 0: // Version Number + case 1: // Separator line + break; + + default: // Project Data + // Are we reading single filenames? + // Is it long enough? + if (strlen(line) > 5) { + // Put last three bytes - the extension - into a short int so we can use it with switch. + end = (int)strlen(line) - 1; + extension = (line[end - 2] << 16) + (line[end - 1] << 8) + line[end]; + //debug("%s - %d\n", line, extension); + switch (extension) { + case 11880: // h + section = SECTION_HEADER; + break; + + case 11875: // c + section = SECTION_CODE; + break; + + case 28007: // img + section = SECTION_BITMAP; + break; + + case 29806: // stn + section = SECTION_STENCIL; + break; + + case 26979: // vic + section = SECTION_VECTOR; + break; + + case 28260: // snd + section = SECTION_SOUND; + break; + + case 28516: // mod + section = SECTION_MUSIC; + break; + + case 24951: // raw + section = SECTION_RAW_DATA; + break; + + default: + section = SECTION_COUNT; + break; + } + } + if (section < SECTION_COUNT) { + snprintf(temp, 4, "%d", section); + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(self->storeProject), &iterParent, temp); + gtk_tree_store_append(self->storeProject, &iter, &iterParent); + gtk_tree_store_set(self->storeProject, &iter, 0, line, -1); + } + break; + } + count++; + } + fclose(in); + DEL(line); + gtk_tree_view_expand_all(GTK_TREE_VIEW(self->treeProject)); + utilSetDirty((WindowDataT *)self, FALSE); // Do again - loading text marks us dirty. + } else { + //***TODO*** Something bad happened. + } +} + + EVENT void menuProjectFileNew(GtkWidget *object, gpointer userData) { } EVENT void menuProjectFileOpen(GtkWidget *object, gpointer userData) { + ProjectDataT *self = (ProjectDataT *)userData; + if (utilFileOpen((WindowDataT *)userData, "*.joe", "Project")) { + loadProject(self); + } } @@ -149,8 +267,10 @@ void winProjectCreate(void) { NULL, NULL }; - GtkTreeViewColumn *col; - GtkCellRenderer *renderer; + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + GtkTreeIter iter; + ProjectSectionTypeT i; // Set up instance data. self = NEW(ProjectDataT); @@ -168,20 +288,12 @@ void winProjectCreate(void) { gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", 0); self->storeProject = gtk_tree_store_new(1, G_TYPE_STRING); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Headers", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Code", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Bitmaps", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Vectors", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Sounds", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Music", -1); - gtk_tree_store_append(self->storeProject, &self->iterTop, NULL); - gtk_tree_store_set(self->storeProject, &self->iterTop, 0, "Data", -1); + + for (i=0; istoreProject, &iter, NULL); + gtk_tree_store_set(self->storeProject, &iter, 0, _SectionName[i], -1); + } + gtk_tree_view_set_model(GTK_TREE_VIEW(self->treeProject), GTK_TREE_MODEL(self->storeProject)); g_object_unref(self->storeProject); @@ -191,6 +303,10 @@ void winProjectCreate(void) { // Register window & show it. utilWindowRegister(self); gtk_widget_show_all(self->windowData.window); + + //***DEBUG*** + self->windowData.filename = strdup("/home/scott/code/joeydev/cmake-build-debug/test.joe"); + loadProject(self); } @@ -201,3 +317,6 @@ static void winProjectDelete(gpointer userData) { DEL(self); } + + +#pragma clang diagnostic pop diff --git a/src/utils.c b/src/utils.c index 7225621..63b0abf 100644 --- a/src/utils.c +++ b/src/utils.c @@ -89,6 +89,52 @@ gboolean utilFileExists(char *filename) { } +gboolean utilFileOpen(WindowDataT *self, char *extension, char *what) { + GtkWidget *dialog; + GtkFileFilter *filter; + char *title = utilCreateString("Open %s", what); + char *warning = utilCreateString("You have unsaved changes. Open different %s?", what); + char *files = utilCreateString("%s Files", what); + gboolean result = TRUE; + + if (self->isDirty) { + if (!utilQuestionDialog(self->window, title, warning)) { + result = FALSE; + } + } + + if (result == TRUE) { + dialog = gtk_file_chooser_dialog_new(title, + GTK_WINDOW(self->window), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL + ); + filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, files); + gtk_file_filter_add_pattern(filter, extension); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + utilSetDirty((WindowDataT *)self, FALSE); + DEL(self->filename); + self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } else { + result = FALSE; + } + + gtk_widget_destroy(dialog); + } + + DEL(files); + DEL(warning); + DEL(title); + + return result; +} + + WindowDataT *utilGetWindowData(GtkWidget *window) { return hmget(_windowList, window); } @@ -135,8 +181,34 @@ gboolean utilQuestionDialog(GtkWidget *parent, char *title, char *question) { } +void utilSetDirty(WindowDataT *self, gboolean dirty) { + char *title; + + self->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->window), title); + DEL(title); +} + + void utilWindowRegister(gpointer windowData) { WindowDataT *w = (WindowDataT *)windowData; + + // Grab title. + w->title = strdup(gtk_window_get_title(GTK_WINDOW(w->window))); + hmput(_windowList, w->window, windowData); } @@ -170,5 +242,8 @@ gboolean utilWindowUnRegister(gpointer windowData) { WindowDataT *w = (WindowDataT *)windowData; result = hmdel(_windowList, w->window); + DEL(w->filename); + DEL(w->title); + return (result == 1) ? TRUE : FALSE; } diff --git a/src/vector.c b/src/vector.c index 29d88e4..ff0ff64 100644 --- a/src/vector.c +++ b/src/vector.c @@ -87,8 +87,6 @@ typedef struct VectorDataS { jlContextT *jlc; double traceImagePercent; float *variables; - char *filename; - char *title; char *tracename; char *buffer; int bufferLength; @@ -113,6 +111,7 @@ 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); +static void loadVectorImage(VectorDataT *self); EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData); EVENT void menuVectorEditCut(GtkWidget *object, gpointer userData); EVENT void menuVectorEditDelete(GtkWidget *object, gpointer userData); @@ -126,7 +125,6 @@ EVENT void menuVectorHelpVector(GtkWidget *object, gpointer userData); static void releasePointList(VectorDataT *self); static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self); EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData); -static void setDirty(VectorDataT *self, gboolean dirty); static void sortCoordinates(int *x1, int *y1, int *x2, int *y2); static void status(VectorDataT *self, char *message); EVENT void toolBoxClicked(GtkToolButton *object, gpointer userData); @@ -425,7 +423,7 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi // Release bytecode. DEL(byteCode.bytes); // Mark text dirty. SCN_SAVEPOINTLEFT isn't being reliable. - setDirty(self, TRUE); + utilSetDirty((WindowDataT *)self, TRUE); } break; @@ -450,7 +448,7 @@ EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { status(self, "Trace image loaded."); - setDirty(self, TRUE); + utilSetDirty((WindowDataT *)self, TRUE); } @@ -606,6 +604,50 @@ static void loadTraceImage(VectorDataT *self, char *filename) { } +static void loadVectorImage(VectorDataT *self) { + FILE *in = NULL; + char *line = NULL; + size_t len = 0; + size_t count = 0; + + in = fopen(self->windowData.filename, "rt"); + if (in != NULL) { + self->buffer[0] = 0; + 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 + utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, strlen(self->buffer) + strlen(line)); + strcat(self->buffer, line); + break; + } + count++; + } + fclose(in); + DEL(line); + SSM(SCI_ADDTEXT, strlen(self->buffer), (sptr_t)self->buffer); + //SSM(SCI_CONVERTEOLS, SC_EOL_CR, 0); + status(self, "Image loaded."); + utilSetDirty((WindowDataT *)self, FALSE); // Do again - loading text marks us dirty. + } else { + //***TODO*** Something bad happened. + } + +} + + EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData) { VectorDataT *self = (VectorDataT *)userData; @@ -680,84 +722,22 @@ EVENT void menuVectorFileNew(GtkWidget *object, gpointer userData) { gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->fileVectorTraceImage)); } // Clear any filename. - DEL(self->filename); + DEL(self->windowData.filename); // Refresh widget. gtk_widget_queue_draw(self->drawVectorImage); // Mark clean. - setDirty(self, FALSE); + utilSetDirty((WindowDataT *)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; - } + (void)object; + + if (utilFileOpen((WindowDataT *)userData, "*.vic", "Image")) { + loadVectorImage(self); } - - 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) { - // Use our New code to reset the editor state before we load. - setDirty(self, FALSE); - menuVectorFileNew(object, userData); - - 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; - 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 - utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, strlen(self->buffer) + strlen(line)); - strcat(self->buffer, line); - break; - } - count++; - } - fclose(in); - DEL(line); - SSM(SCI_ADDTEXT, strlen(self->buffer), (sptr_t)self->buffer); - //SSM(SCI_CONVERTEOLS, SC_EOL_CR, 0); - status(self, "Image loaded."); - setDirty(self, FALSE); // Do again - loading text marks us dirty. - } else { - //***TODO*** Something bad happened. - } - } - - gtk_widget_destroy(dialog); } @@ -771,7 +751,7 @@ EVENT void menuVectorFileSave(GtkWidget *object, gpointer userData) { if (self->windowData.isDirty == TRUE) { // Do we have a filename? If not, kick 'em to SaveAs. - if (self->filename == NULL) { + if (self->windowData.filename == NULL) { menuVectorFileSaveAs(object, userData); return; } @@ -786,7 +766,7 @@ EVENT void menuVectorFileSave(GtkWidget *object, gpointer userData) { // Fetch code. SSM(SCI_GETTEXT, length, (sptr_t)code); - out = fopen(self->filename, "wt"); + out = fopen(self->windowData.filename, "wt"); if (out != NULL) { // Save! fprintf(out, "%s\n", VICTOR_VERSION); @@ -796,7 +776,7 @@ EVENT void menuVectorFileSave(GtkWidget *object, gpointer userData) { fclose(out); status(self, "Saved."); // We're clean now. - setDirty(self, FALSE); + utilSetDirty((WindowDataT *)self, FALSE); } else { //***TODO*** Something bad happened. } @@ -821,8 +801,8 @@ EVENT void menuVectorFileSaveAs(GtkWidget *object, gpointer userData) { gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - DEL(self->filename); - self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + DEL(self->windowData.filename); + self->windowData.filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); menuVectorFileSave(object, self); } @@ -1200,28 +1180,6 @@ 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 void sortCoordinates(int *x1, int *y1, int *x2, int *y2) { int temp; @@ -1430,7 +1388,7 @@ EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) { } -void winVectorCreate(void) { +void winVectorCreate(char *filename) { VectorDataT *self; char *widgetNames[] = { "winVector", @@ -1464,9 +1422,6 @@ void winVectorCreate(void) { widgets[4] = &self->statusBar; utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Vector.glade", widgetNames, widgets, self); - // Grab title. - self->title = strdup(gtk_window_get_title(GTK_WINDOW(self->windowData.window))); - // Get status bar context ID. self->statusBarId = gtk_statusbar_get_context_id(GTK_STATUSBAR(self->statusBar), "JoeyDev"); @@ -1569,6 +1524,11 @@ void winVectorCreate(void) { gtk_widget_show_all(self->windowData.window); status(self, "Welcome to the Victor Vector Editor!"); + + if (filename != NULL) { + self->windowData.filename = strdup(filename); + loadVectorImage(self); + } } @@ -1586,8 +1546,6 @@ static void winVectorDelete(gpointer userData) { cairo_surface_destroy(self->target); if (self->trace != NULL) cairo_surface_destroy(self->trace); releasePointList(self); - DEL(self->filename); - DEL(self->title); DEL(self->tracename); DEL(self->buffer);