From 2512b6b7c1a6e34e10426b09d4f24d1ab3ab51a3 Mon Sep 17 00:00:00 2001 From: Milan Prochac Date: Sun, 10 Jan 2016 12:09:06 +0100 Subject: [PATCH] Modular file formats --- src/action.c | 35 ++++-- src/buffer.c | 6 +- src/dolists.h | 2 + src/file.c | 294 ++++++++++++++++++++++++++++++++++++++++++---- src/file.h | 3 + src/global.h | 1 + src/hid.h | 37 ++++++ src/hid/gtk/gtkhid-main.c | 11 +- src/hid/gtk/gui-config.c | 2 +- src/hid/gtk/gui-dialog.c | 54 +++++++-- src/hid/gtk/gui.h | 2 +- 11 files changed, 402 insertions(+), 45 deletions(-) diff --git a/src/action.c b/src/action.c index 0be31ab..2cfd6a9 100644 --- a/src/action.c +++ b/src/action.c @@ -5706,7 +5706,7 @@ ActionUnselect (int argc, char **argv, Coord x, Coord y) /* --------------------------------------------------------------------------- */ static const char saveto_syntax[] = - N_("SaveTo(Layout|LayoutAs,filename)\n" + N_("SaveTo(Layout|LayoutAs,filename,format)\n" "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n" "SaveTo(PasteBuffer,filename)"); @@ -5738,35 +5738,52 @@ Save the content of the active Buffer to a file. This is the graphical way to cr %end-doc */ +extern int SavePCBWithFormat (PCBType *pcb, char *filename, char *fileformat); + static int ActionSaveTo (int argc, char **argv, Coord x, Coord y) { char *function; char *name; + char *format; function = ARG (0); if ( ! function || strcasecmp (function, "Layout") == 0) { - if (SavePCB (PCB->Filename) == 0) + if (SavePCBWithFormat (PCB, PCB->Filename, PCB->Fileformat) == 0) SetChangedFlag (false); return 0; } - if (argc != 2) + if (argc < 2 ) AFAIL (saveto); name = argv[1]; + if (argc == 3) { + format = argv[2]; + } else { + format = (PCB->Fileformat)?PCB->Fileformat:hid_get_default_format_id (); + } if (strcasecmp (function, "LayoutAs") == 0) { - if (SavePCB (name) == 0) + if (SavePCBWithFormat (PCB, name, format) == 0) { - SetChangedFlag (false); - free (PCB->Filename); - PCB->Filename = strdup (name); - if (gui->notify_filename_changed != NULL) - gui->notify_filename_changed (); + if (hid_file_format_loadable(format)) + { + SetChangedFlag (false); + free (PCB->Filename); + PCB->Filename = strdup (name); + /* change only if new format was specified */ + if (argc == 3) + { + free (PCB->Fileformat); + PCB->Fileformat = strdup (format); + } + if (gui->notify_filename_changed != NULL) + gui->notify_filename_changed (); + } } return 0; } diff --git a/src/buffer.c b/src/buffer.c index c58100b..1a280d1 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -47,6 +47,7 @@ #include "crosshair.h" #include "data.h" #include "error.h" +#include "file.h" #include "mymem.h" #include "mirror.h" #include "misc.h" @@ -1141,10 +1142,11 @@ ConvertBufferToElement (BufferType *Buffer) bool LoadLayoutToBuffer (BufferType *Buffer, char *Filename) { - PCBType *newPCB = CreateNewPCB (); + PCBType *newPCB = NULL; // = CreateNewPCB (); + char *dummy; /* new data isn't added to the undo list */ - if (!ParsePCB (newPCB, Filename)) + if (!LoadPCBWithFormat (&newPCB, Filename, NULL, &dummy)) { /* clear data area and replace pointer */ ClearBuffer (Buffer); diff --git a/src/dolists.h b/src/dolists.h index 3b7c663..714c520 100644 --- a/src/dolists.h +++ b/src/dolists.h @@ -1,7 +1,9 @@ #undef REGISTER_ACTIONS #undef REGISTER_ATTRIBUTES #undef REGISTER_FLAGS +#undef REGISTER_FORMATS #define REGISTER_ACTIONS(a) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();} #define REGISTER_ATTRIBUTES(a) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();} #define REGISTER_FLAGS(a) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();} +#define REGISTER_FORMATS(a) {extern void HIDCONCAT(register_,a)();HIDCONCAT(register_,a)();} diff --git a/src/file.c b/src/file.c index c3ed5eb..5af7624 100644 --- a/src/file.c +++ b/src/file.c @@ -127,6 +127,9 @@ static int WritePipe (char *, bool); static int ParseLibraryTree (void); static int LoadNewlibFootprintsFromDir(char *path, char *toppath, bool recursive); +//extern int SavePCBWithFormat (PCBType *pcb, char *filename, char *fileformat); +//extern int LoadPCBWithFormat (PCBType **pcb, char *filename, char *fileformat, char **new_format); + /* --------------------------------------------------------------------------- * Flag helper functions */ @@ -350,16 +353,7 @@ SaveBufferElements (char *Filename) int SavePCB (char *file) { - int retcode; - - if (gui->notify_save_pcb == NULL) return WritePipe (file, true); - - gui->notify_save_pcb (file, false); - retcode = WritePipe (file, true); - gui->notify_save_pcb (file, true); - - return retcode; } /*! @@ -389,11 +383,11 @@ set_some_route_style () * PCBChanged action. */ static int -real_load_pcb (char *Filename, bool revert) +real_load_pcb (char *Filename, char *Format, bool revert) { const char *unit_suffix, *grid_size; - char *new_filename; - PCBType *newPCB = CreateNewPCB (); + char *new_filename, *new_format; + PCBType *newPCB = NULL; /* = CreateNewPCB (); */ PCBType *oldPCB; #ifdef DEBUG double elapsed; @@ -405,13 +399,9 @@ real_load_pcb (char *Filename, bool revert) new_filename = strdup (Filename); oldPCB = PCB; - PCB = newPCB; - - /* mark the default font invalid to know if the file has one */ - newPCB->Font.Valid = false; /* new data isn't added to the undo list */ - if (!ParsePCB (PCB, new_filename)) + if (!LoadPCBWithFormat (&PCB, new_filename, Format, &new_format)) { RemovePCB (oldPCB); @@ -438,6 +428,7 @@ real_load_pcb (char *Filename, bool revert) /* clear 'changed flag' */ SetChangedFlag (false); PCB->Filename = new_filename; + PCB->Fileformat = strdup(new_format); /* just in case a bad file saved file is loaded */ /* Use attribute PCB::grid::unit as unit, if we can */ @@ -474,7 +465,10 @@ real_load_pcb (char *Filename, bool revert) return (0); } + + newPCB = PCB; PCB = oldPCB; + hid_action ("PCBChanged"); /* release unused memory */ @@ -488,7 +482,7 @@ real_load_pcb (char *Filename, bool revert) int LoadPCB (char *file) { - return real_load_pcb (file, false); + return real_load_pcb (file, NULL, false); } /*! @@ -497,7 +491,7 @@ LoadPCB (char *file) int RevertPCB (void) { - return real_load_pcb (PCB->Filename, true); + return real_load_pcb (PCB->Filename, PCB->Fileformat, true); } /*! @@ -1073,6 +1067,8 @@ void Backup (void) { char *filename = NULL; + char *fileformat; + char *save_savecommand; if( PCB && PCB->Filename ) { @@ -1083,6 +1079,8 @@ Backup (void) exit (1); } sprintf (filename, "%s~", PCB->Filename); + fileformat = PCB->Fileformat; + printf("B: %s\n",fileformat); } else { @@ -1094,9 +1092,16 @@ Backup (void) exit (1); } sprintf (filename, BACKUP_NAME, (int) getpid ()); + fileformat = hid_get_default_format_id (); } - WritePCBFile (filename); + save_savecommand = Settings.SaveCommand; + Settings.SaveCommand = NULL; + + SavePCBWithFormat (PCB, filename, fileformat); + + Settings.SaveCommand = save_savecommand; + free (filename); } @@ -1112,8 +1117,16 @@ Backup (void) void SaveTMPData (void) { + char *save_savecommand; + sprintf (TMPFilename, EMERGENCY_NAME, (int) getpid ()); + + save_savecommand = Settings.SaveCommand; + Settings.SaveCommand = NULL; + WritePCBFile (TMPFilename); + + Settings.SaveCommand = save_savecommand; } /*! @@ -1667,3 +1680,244 @@ static int ReadEdifNetlist (char *filename) return 0; } + +/****************************************************************************************************/ + +static int n_formats = 0; +static HID_Format **all_formats = 0; + +/* + load_save 0: load format, 1: save format + return: + 0: no more data + 1: data OK + id == NULL -> function not implemented for specific format +*/ +int +hid_get_file_format(int idx, int load_save, char **id, char **name, char **mime, char ***patterns) +{ + if (idx >= n_formats) + return 0; + if (((load_save == 0) && (all_formats[idx]->load_function == NULL)) || ((load_save != 0) && (all_formats[idx]->save_function == NULL))) + { + *id=NULL; + return 1; + } + + *id = all_formats[idx]->id; + *name = all_formats[idx]->description; + *mime = all_formats[idx]->mimetype; + *patterns = all_formats[idx]->patterns; + + return 1; +} + +void +hid_register_formats (HID_Format * a, int n) +{ + int i, count = 0; + + all_formats = (HID_Format **)realloc (all_formats, + (n_formats + n) * sizeof (HID_Format*)); + for (i = 0; i < n; i++) + { +#if 0 + printf("Registering %s (%s)\n",a[i].description,a[i].id); +#endif + all_formats[n_formats + count++] = a + i; + } + n_formats += count; +} + +char * +hid_get_format_id_by_desc (char *desc) +{ + int i; + + for (i = 0; i < n_formats; i++) + { + if (strcmp (all_formats[i]->description, desc) == 0) + return all_formats[i]->id; + } + return NULL; +} + +char * +hid_get_format_id_by_idx (int idx) +{ + if (idx < n_formats) + { + return all_formats[idx]->id; + } + return NULL; +} + +char * +hid_get_default_format_id (char *desc) +{ + int i; + + for (i = 0; i < n_formats; i++) + { + if (all_formats[i]->default_format) + return all_formats[i]->id; + } + return NULL; +} + +extern int hid_file_format_loadable(char *id) +{ + int i; + + for (i = 0; i < n_formats; i++) + { + if (strcmp (all_formats[i]->id, id) == 0) + return (all_formats[i]->load_function == NULL)?0:1; + } + + return 0; +} + + +int +SavePCBWithFormat (PCBType *pcb, char *filename, char *fileformat) +{ + int i; + int result; + + Message(_("Saving file %s as %s\n"), filename, fileformat); + + for (i = 0; i < n_formats ; i++ ) { + if ((strcmp (all_formats[i]->id, fileformat) == 0) && (all_formats[i]->save_function != NULL )) + { + if ((all_formats[i]->check_version != NULL) && (*(int (*)(unsigned long, unsigned long))all_formats[i]->check_version)(PCB_FILE_VERSION, PCBFileVersionNeeded()) != 0 ) + { + gui->report_dialog(_("Incompatible file format"),_("The selected file format does not support current data structures")); + Message(_("Selected format \"%s\" does not support data structures version %ul:\n"), fileformat, PCB_FILE_VERSION); + return 1; + } + if (gui->notify_save_pcb != NULL) + gui->notify_save_pcb (filename, false); + + result = (*(int (*)(PCBType *, char *))all_formats[i]->save_function)(pcb, filename); + + if (gui->notify_save_pcb != NULL) + gui->notify_save_pcb (filename, true); + + return result; + } + } + Message (_("No suitable module for format \"%s\"\n"), fileformat); + return 1; +} + +int +LoadPCBWithFormat (PCBType **pcb, char *filename, char *fileformat, char **new_format) +{ + int i; + int result; + + *pcb = CreateNewPCB (); + /* mark the default font invalid to know if the file has one */ + (*pcb)->Font.Valid = false; + + if (fileformat) + { + Message(_("Loading file %s as %s\n"), filename, fileformat); + + for (i = 0; i < n_formats ; i++ ) + { + if ((strcmp (all_formats[i]->id, fileformat) == 0) && (all_formats[i]->load_function != NULL )) + { + *new_format = all_formats[i]->id; + return (*(int (*)(PCBType *, char *))all_formats[i]->load_function)(*pcb, filename); + } + } + Message (_("No suitable module for format \"%s\"\n"), fileformat); + } else { + Message(_("Loading file %s with autodetection.\n"), filename); + for (i = 0; i < n_formats ; i++ ) + { + if (all_formats[i]->load_function != NULL) + { + Message(_(" Probing format %s\n"), all_formats[i]->id); + if (all_formats[i]->check_function != NULL ) + { + /* If check function is available and return value is OK (0), the file is loaded and no other formats are tested */ + if (((*(int (*)(char *))all_formats[i]->check_function)(filename)) == 0) + { + *new_format = all_formats[i]->id; + return (*(int (*)(PCBType *, char *))all_formats[i]->load_function)(*pcb, filename); + } + } else { + /* If check function is not available, the file is loaded; if fail, next format is tried */ + result = (*(int (*)(PCBType *, char *))all_formats[i]->load_function)(*pcb, filename); + if (result == 0 ) + { + *new_format = all_formats[i]->id; + return result; + } else { + /* Cleanup after unsuccessful load */ + RemovePCB(*pcb); + *pcb = CreateNewPCB (); + /* mark the default font invalid to know if the file has one */ + (*pcb)->Font.Valid = false; + } + } + } + } + } + + Message (_("No suitable module found for file \"%s\"\n"), filename); + return 1; +} + +/****************************************************************************************************/ + +int +SavePCB2(PCBType *pcb, char *filename) +{ + return SavePCB(filename); +} + +int +CheckPCB(char *filename) +{ + FILE *f; + int i; + char buf[512]; + + f=fopen(filename, "r"); + + if (!f) + return 1; + + for ( i = 0; i < 10; i++ ) + { + fgets(buf,sizeof(buf),f); + if (strstr(buf,"FileVersion[") != 0) + { + fclose(f); + return 0; + } + } + fclose(f); + return 1; +} + +#define PCB_FILE_VERSION_IMPLEMENTED 20110603 + +int +CheckPCBVersion(unsigned long current, unsigned long minimal) +{ + return (PCB_FILE_VERSION_IMPLEMENTED >= minimal)?0:1; +} + +static char *pcb_format_list_patterns[]={"*.pcb", "*.PCB", 0}; + +static HID_Format pcb_format_list[]={ + {"pcb","Legacy PCB", pcb_format_list_patterns, "application/x-pcb-layout", 1, (void*)CheckPCBVersion, (void*)CheckPCB, (void*)ParsePCB, (void*)SavePCB2,0,0}, +}; + +REGISTER_FORMATS (pcb_format_list) + diff --git a/src/file.h b/src/file.h index 0515882..36e138f 100644 --- a/src/file.h +++ b/src/file.h @@ -51,6 +51,9 @@ int ImportNetlist (char *); int SaveBufferElements (char *); void sort_netlist (void); +extern int LoadPCBWithFormat (PCBType **pcb, char *filename, char *fileformat, char **new_format); +extern int SavePCBWithFormat (PCBType *pcb, char *filename, char *fileformat); + int PCBFileVersionNeeded (void); /*!< This is the version needed by the file we're saving. */ diff --git a/src/global.h b/src/global.h index 880d768..2b4870a 100644 --- a/src/global.h +++ b/src/global.h @@ -571,6 +571,7 @@ typedef struct PCBType FlagType Flags; char *Name, /*!< Name of board. */ *Filename, /*!< Name of file (from load). */ + *Fileformat, /*!< type of file (from load or last save). */ *PrintFilename, /*!< From print dialog. */ *Netlistname, /*!< Name of netlist file. */ ThermStyle; /*!< Type of thermal to place with thermal tool. */ diff --git a/src/hid.h b/src/hid.h index 69e4541..d8553a3 100644 --- a/src/hid.h +++ b/src/hid.h @@ -264,6 +264,43 @@ extern "C" #define REGISTER_ATTRIBUTES(a) HIDCONCAT(void register_,a) ()\ { hid_register_attributes(a, sizeof(a)/sizeof(a[0])); } + +/* values returned by check_function */ +#define HID_FFORMAT_UNKNOWN 0 +#define HID_FFORMAT_ACCEPT 1 +#define HID_FFORMAT_REJECT 2 + + typedef struct + { + /* Name of the file format */ + char *id; + char *description; + /* default extension of the file format */ + char **patterns; + char *mimetype; + int default_format; + /* check for versions supported by file format: current version (recommended), minimal */ + int (*check_version) (unsigned long, unsigned long); + /* Functions to call to procass file. */ + int (*check_function) (char *); + int (*load_function) (void *, char *); + int (*save_function) (void *, char *); + int (*load_elemet_function) (void *, char *); + int (*save_element_function) (void *, char *); + } HID_Format; + + extern void hid_register_formats (HID_Format *, int); +#define REGISTER_FORMATS(a) HIDCONCAT(void register_,a) ()\ +{ hid_register_formats(a, sizeof(a)/sizeof(a[0])); } + +/* some format-specific functions. */ +extern char * hid_get_format_id_by_desc (char *desc); +extern char * hid_get_format_id_by_idx (int); +extern char * hid_get_default_format_id (); +extern int hid_get_file_format(int idx, int load_save, char **id, char **name, char **mime, char ***patterns); +extern int hid_file_format_loadable(char *id); + + /* These three are set by hid_parse_command_line(). */ extern char *program_name; extern char *program_directory; diff --git a/src/hid/gtk/gtkhid-main.c b/src/hid/gtk/gtkhid-main.c index 1e210ab..35fe03a 100644 --- a/src/hid/gtk/gtkhid-main.c +++ b/src/hid/gtk/gtkhid-main.c @@ -1313,6 +1313,7 @@ Save (int argc, char **argv, Coord x, Coord y) char *function; char *name; char *prompt; + gchar *current_format=NULL; static gchar *current_dir = NULL; @@ -1323,15 +1324,15 @@ Save (int argc, char **argv, Coord x, Coord y) if (strcasecmp (function, "Layout") == 0) if (PCB->Filename) - return hid_actionl ("SaveTo", "Layout", PCB->Filename, NULL); + return hid_actionl ("SaveTo", "Layout", PCB->Filename, PCB->Fileformat, NULL); if (strcasecmp (function, "PasteBuffer") == 0) prompt = _("Save element as"); else prompt = _("Save layout as"); - + name = ghid_dialog_file_select_save (prompt, - ¤t_dir, + ¤t_dir, ¤t_format, PCB->Filename, Settings.FilePath); if (name) @@ -1351,9 +1352,9 @@ Save (int argc, char **argv, Coord x, Coord y) * just obtained. */ if (strcasecmp (function, "Layout") == 0) - hid_actionl ("SaveTo", "LayoutAs", name, NULL); + hid_actionl ("SaveTo", "LayoutAs", name, hid_get_format_id_by_desc(current_format), NULL); else - hid_actionl ("SaveTo", function, name, NULL); + hid_actionl ("SaveTo", function, name, hid_get_format_id_by_desc(current_format), NULL); } g_free (name); } diff --git a/src/hid/gtk/gui-config.c b/src/hid/gtk/gui-config.c index aa91dbf..e772037 100644 --- a/src/hid/gtk/gui-config.c +++ b/src/hid/gtk/gui-config.c @@ -1874,7 +1874,7 @@ config_color_save_cb (gpointer data) gchar *name, *path, *dir = g_strdup (color_dir); path = - ghid_dialog_file_select_save (_("Save Color File"), &dir, NULL, NULL); + ghid_dialog_file_select_save (_("Save Color File"), &dir, NULL, NULL, NULL); if (path) { name = g_path_get_basename (path); diff --git a/src/hid/gtk/gui-dialog.c b/src/hid/gtk/gui-dialog.c index 4ef77ff..9c57cb2 100644 --- a/src/hid/gtk/gui-dialog.c +++ b/src/hid/gtk/gui-dialog.c @@ -307,12 +307,23 @@ ghid_dialog_file_select_open (gchar * title, gchar ** path, gchar * shortcuts) { /* add a filter for layout files */ GtkFileFilter *pcb_filter; - pcb_filter = gtk_file_filter_new (); - gtk_file_filter_set_name (pcb_filter, "pcb"); - gtk_file_filter_add_mime_type (pcb_filter, "application/x-pcb-layout"); - gtk_file_filter_add_pattern (pcb_filter, "*.pcb"); - gtk_file_filter_add_pattern (pcb_filter, "*.PCB"); - gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), pcb_filter); + int idx = 0, pi; + char *filter_id, *filter_name, *filter_mime, **filter_patterns; + + while (hid_get_file_format(idx, 0, &filter_id, &filter_name, &filter_mime, &filter_patterns)) { + if (filter_id != NULL ) { + pcb_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (pcb_filter, filter_name); + gtk_file_filter_add_mime_type (pcb_filter, filter_mime); + pi = 0; + while (filter_patterns[pi] != 0 ) { + gtk_file_filter_add_pattern (pcb_filter, filter_patterns[pi]); + pi++; + } + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), pcb_filter); + } + idx++; + } } /* in case we have a dialog for loading a netlist file */ @@ -455,7 +466,7 @@ ghid_dialog_file_select_multiple(gchar * title, gchar ** path, gchar * shortcuts /* ---------------------------------------------- */ /* Caller must g_free() the returned filename. */ gchar * -ghid_dialog_file_select_save (gchar * title, gchar ** path, gchar * file, +ghid_dialog_file_select_save (gchar * title, gchar ** path, gchar ** format, gchar * file, gchar * shortcuts) { GtkWidget *dialog; @@ -491,6 +502,27 @@ ghid_dialog_file_select_save (gchar * title, gchar ** path, gchar * file, g_path_get_dirname (file)); } + if (format != NULL ) { + GtkFileFilter *pcb_filter; + int idx = 0, pi; + char *filter_id, *filter_name, *filter_mime, **filter_patterns; + + while (hid_get_file_format(idx, 0, &filter_id, &filter_name, &filter_mime, &filter_patterns)) { + if (filter_id != NULL ) { + pcb_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (pcb_filter, filter_name); + gtk_file_filter_add_mime_type (pcb_filter, filter_mime); + pi = 0; + while (filter_patterns[pi] != 0 ) { + gtk_file_filter_add_pattern (pcb_filter, filter_patterns[pi]); + pi++; + } + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), pcb_filter); + } + idx++; + } + } + if (shortcuts && *shortcuts) { folder = g_strdup (shortcuts); @@ -512,6 +544,14 @@ ghid_dialog_file_select_save (gchar * title, gchar ** path, gchar * file, { dup_string (path, folder); g_free (folder); + + if (format) + { + GtkFileFilter *fp_filter; + + fp_filter=gtk_file_chooser_get_filter(GTK_FILE_CHOOSER (dialog)); + dup_string (format, (gchar*)gtk_file_filter_get_name(fp_filter)); + } } } gtk_widget_destroy (dialog); diff --git a/src/hid/gtk/gui.h b/src/hid/gtk/gui.h index 4b36da9..47f62f7 100644 --- a/src/hid/gtk/gui.h +++ b/src/hid/gtk/gui.h @@ -315,7 +315,7 @@ gchar *ghid_dialog_file_select_open (gchar * title, gchar ** path, gchar * shortcuts); GSList *ghid_dialog_file_select_multiple (gchar * title, gchar ** path, gchar * shortcuts); -gchar *ghid_dialog_file_select_save (gchar * title, gchar ** path, +gchar *ghid_dialog_file_select_save (gchar * title, gchar ** path, gchar ** format, gchar * file, gchar * shortcuts); void ghid_dialog_message (gchar * message); gboolean ghid_dialog_confirm (gchar * message, gchar *cancelmsg, gchar *okmsg); -- 1.8.3.1