From e61bac27976977b3aff76ff8ff89550a2a921f61 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 4 May 2016 03:12:06 +0000 Subject: [PATCH] Refactor file filters to not use preprocessor magic. The immediate reason for refactoring this was that the GTK port broke after 52af7256 since config.h is not included anymore, but it was a fragile piece of code I will shed no tears for. While we're at it, get rid of the mutable std::string &file to be consistent with our conventions. --- src/cocoa/cocoamain.mm | 44 +++++++++--------- src/file.cpp | 2 +- src/group.cpp | 2 +- src/gtk/gtkmain.cpp | 90 ++++++++++++++----------------------- src/solvespace.cpp | 28 ++++++------ src/solvespace.h | 100 +++++++++++++++++++---------------------- src/style.cpp | 2 +- src/win32/w32main.cpp | 46 ++++++++++++------- 8 files changed, 150 insertions(+), 164 deletions(-) diff --git a/src/cocoa/cocoamain.mm b/src/cocoa/cocoamain.mm index 9a73372a..f8c97b8b 100644 --- a/src/cocoa/cocoamain.mm +++ b/src/cocoa/cocoamain.mm @@ -732,21 +732,20 @@ bool MenuBarIsVisible(void) { /* Save/load */ -bool SolveSpace::GetOpenFile(std::string &file, const std::string &defExtension, - const char *selPattern) { +bool SolveSpace::GetOpenFile(std::string *file, const std::string &defExtension, + const FileFilter ssFilters[]) { NSOpenPanel *panel = [NSOpenPanel openPanel]; NSMutableArray *filters = [[NSMutableArray alloc] init]; - for(NSString *filter in [[NSString stringWithUTF8String:selPattern] - componentsSeparatedByString:@"\n"]) { - [filters addObjectsFromArray: - [[[filter componentsSeparatedByString:@"\t"] objectAtIndex:1] - componentsSeparatedByString:@","]]; + for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { + for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) { + [filters addObject:[NSString stringWithUTF8String:*ssPattern]]; + } } [filters removeObjectIdenticalTo:@"*"]; [panel setAllowedFileTypes:filters]; if([panel runModal] == NSFileHandlingPanelOKButton) { - file = [[NSFileManager defaultManager] + *file = [[NSFileManager defaultManager] fileSystemRepresentationWithPath:[[panel URL] path]]; return true; } else { @@ -774,8 +773,8 @@ bool SolveSpace::GetOpenFile(std::string &file, const std::string &defExtension, } @end -bool SolveSpace::GetSaveFile(std::string &file, const std::string &defExtension, - const char *selPattern) { +bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension, + const FileFilter ssFilters[]) { NSSavePanel *panel = [NSSavePanel savePanel]; SaveFormatController *controller = @@ -788,16 +787,19 @@ bool SolveSpace::GetSaveFile(std::string &file, const std::string &defExtension, NSPopUpButton *button = [controller button]; [button removeAllItems]; - for(NSString *filter in [[NSString stringWithUTF8String:selPattern] - componentsSeparatedByString:@"\n"]) { - NSArray *filterParts = [filter componentsSeparatedByString:@"\t"]; - NSString *filterName = [filterParts objectAtIndex:0]; - NSArray *filterExtensions = [[filterParts objectAtIndex:1] - componentsSeparatedByString:@","]; - [button addItemWithTitle: - [[NSString alloc] initWithFormat:@"%@ (%@)", filterName, - [filterExtensions componentsJoinedByString:@", "]]]; - [extensions addObject:[filterExtensions objectAtIndex:0]]; + for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { + std::string desc; + for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) { + if(desc == "") { + desc = *ssPattern; + } else { + desc += ", "; + desc += *ssPattern; + } + } + std::string title = std::string(ssFilter->name) + " (" + desc + ")"; + [button addItemWithTitle:[NSString stringWithUTF8String:title.c_str()]]; + [extensions addObject:[NSString stringWithUTF8String:ssFilter->patterns[0]]]; } int extensionIndex = 0; @@ -811,7 +813,7 @@ bool SolveSpace::GetSaveFile(std::string &file, const std::string &defExtension, stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; if([panel runModal] == NSFileHandlingPanelOKButton) { - file = [[NSFileManager defaultManager] + *file = [[NSFileManager defaultManager] fileSystemRepresentationWithPath:[[panel URL] path]]; return true; } else { diff --git a/src/file.cpp b/src/file.cpp index 3bbe62aa..159f0f63 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -842,7 +842,7 @@ try_load_file: switch(LocateImportedFileYesNoCancel(g->impFileRel, canCancel)) { case DIALOG_YES: { std::string oldImpFile = g->impFile; - if(!GetOpenFile(g->impFile, "", SLVS_PATTERN)) { + if(!GetOpenFile(&g->impFile, "", SlvsFileFilter)) { if(canCancel) return false; break; diff --git a/src/group.cpp b/src/group.cpp index 1e612a6c..706e5efc 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -188,7 +188,7 @@ void Group::MenuGroup(int id) { case GraphicsWindow::MNU_GROUP_IMPORT: { g.type = IMPORTED; if(g.impFile.empty()) { - if(!GetOpenFile(g.impFile, "", SLVS_PATTERN)) return; + if(!GetOpenFile(&g.impFile, "", SlvsFileFilter)) return; } // Assign the default name of the group based on the name of diff --git a/src/gtk/gtkmain.cpp b/src/gtk/gtkmain.cpp index 3d208ced..24cce5b1 100644 --- a/src/gtk/gtkmain.cpp +++ b/src/gtk/gtkmain.cpp @@ -1058,77 +1058,53 @@ void RefreshRecentMenus(void) { /* Save/load */ -static std::string FiltersFromPattern(const std::string &active, const char *patterns, - Gtk::FileChooser &chooser) { - Glib::ustring uactive = active; - Glib::ustring upatterns = patterns; - +static std::string ConvertFilters(std::string active, const FileFilter ssFilters[], + Gtk::FileChooser *chooser) { + for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { #ifdef HAVE_GTK3 - Glib::RefPtr filter = Gtk::FileFilter::create(); + Glib::RefPtr filter = Gtk::FileFilter::create(); #else - Gtk::FileFilter *filter = new Gtk::FileFilter; + Gtk::FileFilter *filter = new Gtk::FileFilter; #endif - Glib::ustring desc = ""; - bool has_name = false, is_active = false; - int last = 0; - for(int i = 0; i <= upatterns.length(); i++) { - if(upatterns[i] == '\t' || upatterns[i] == '\n' || upatterns[i] == '\0') { - Glib::ustring frag = upatterns.substr(last, i - last); - if(!has_name) { - filter->set_name(frag); - has_name = true; - } else { - filter->add_pattern(frag); - if(uactive == "") - uactive = frag.substr(2); - if("*." + uactive == frag) - is_active = true; - if(desc == "") - desc = frag; - else - desc += ", " + frag; - } - } else continue; + filter->set_name(ssFilter->name); - if(upatterns[i] == '\n' || upatterns[i] == '\0') { - filter->set_name(filter->get_name() + " (" + desc + ")"); -#ifdef HAVE_GTK3 - chooser.add_filter(filter); - if(is_active) - chooser.set_filter(filter); - - filter = Gtk::FileFilter::create(); -#else - chooser.add_filter(*filter); - if(is_active) - chooser.set_filter(*filter); - - filter = new Gtk::FileFilter(); -#endif - has_name = false; - is_active = false; - desc = ""; + bool is_active = false; + std::string desc = ""; + for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) { + std::string pattern = "*." + std::string(*ssPattern); + filter->add_pattern(pattern); + if(active == "") + active = pattern.substr(2); + if("*." + active == pattern) + is_active = true; + if(desc == "") + desc = pattern; + else + desc += ", " + pattern; } + filter->set_name(filter->get_name() + " (" + desc + ")"); - last = i + 1; + chooser->add_filter(*filter); + if(is_active) + chooser->set_filter(*filter); } - return uactive; + return active; } -bool GetOpenFile(std::string &file, const std::string &activeOrEmpty, - const char *patterns) { +bool GetOpenFile(std::string *filename, const std::string &activeOrEmpty, + const FileFilter filters[]) { Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Open File"); - chooser.set_filename(file); + chooser.set_filename(*filename); chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL); chooser.add_button("_Open", Gtk::RESPONSE_OK); chooser.set_current_folder(CnfThawString("", "FileChooserPath")); - FiltersFromPattern(activeOrEmpty, patterns, chooser); + ConvertFilters(activeOrEmpty, filters, &chooser); if(chooser.run() == Gtk::RESPONSE_OK) { CnfFreezeString(chooser.get_current_folder(), "FileChooserPath"); - file = chooser.get_filename(); + *filename = chooser.get_filename(); return true; } else { return false; @@ -1173,15 +1149,15 @@ static void ChooserFilterChanged(Gtk::FileChooserDialog *chooser) } } -bool GetSaveFile(std::string &file, const std::string &activeOrEmpty, - const char *patterns) { +bool GetSaveFile(std::string *filename, const std::string &activeOrEmpty, + const FileFilter filters[]) { Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Save File", Gtk::FILE_CHOOSER_ACTION_SAVE); chooser.set_do_overwrite_confirmation(true); chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL); chooser.add_button("_Save", Gtk::RESPONSE_OK); - std::string active = FiltersFromPattern(activeOrEmpty, patterns, chooser); + std::string active = ConvertFilters(activeOrEmpty, filters, &chooser); chooser.set_current_folder(CnfThawString("", "FileChooserPath")); chooser.set_current_name(std::string("untitled.") + active); @@ -1193,7 +1169,7 @@ bool GetSaveFile(std::string &file, const std::string &activeOrEmpty, if(chooser.run() == Gtk::RESPONSE_OK) { CnfFreezeString(chooser.get_current_folder(), "FileChooserPath"); - file = chooser.get_filename(); + *filename = chooser.get_filename(); return true; } else { return false; diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 60ec68f9..139970cd 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -374,7 +374,7 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) { std::string prevSaveFile = saveFile; if(saveAs || saveFile.empty()) { - if(!GetSaveFile(saveFile, "", SLVS_PATTERN)) return false; + if(!GetSaveFile(&saveFile, "", SlvsFileFilter)) return false; // need to get new filename directly into saveFile, since that // determines impFileRel path } @@ -457,7 +457,7 @@ void SolveSpaceUI::MenuFile(int id) { if(!SS.OkayToStartNewFile()) break; std::string newFile; - if(GetOpenFile(newFile, "", SLVS_PATTERN)) { + if(GetOpenFile(&newFile, "", SlvsFileFilter)) { SS.OpenFile(newFile); } break; @@ -473,15 +473,15 @@ void SolveSpaceUI::MenuFile(int id) { case GraphicsWindow::MNU_EXPORT_PNG: { std::string exportFile; - if(!GetSaveFile(exportFile, "", PNG_PATTERN)) break; + if(!GetSaveFile(&exportFile, "", PngFileFilter)) break; SS.ExportAsPngTo(exportFile); break; } case GraphicsWindow::MNU_EXPORT_VIEW: { std::string exportFile; - if(!GetSaveFile(exportFile, CnfThawString("", "ViewExportFormat"), - VEC_PATTERN)) break; + if(!GetSaveFile(&exportFile, CnfThawString("", "ViewExportFormat"), + VectorFileFilter)) break; CnfFreezeString(Extension(exportFile), "ViewExportFormat"); // If the user is exporting something where it would be @@ -502,8 +502,8 @@ void SolveSpaceUI::MenuFile(int id) { case GraphicsWindow::MNU_EXPORT_WIREFRAME: { std::string exportFile; - if(!GetSaveFile(exportFile, CnfThawString("", "WireframeExportFormat"), - V3D_PATTERN)) break; + if(!GetSaveFile(&exportFile, CnfThawString("", "WireframeExportFormat"), + Vector3dFileFilter)) break; CnfFreezeString(Extension(exportFile), "WireframeExportFormat"); SS.ExportViewOrWireframeTo(exportFile, true); @@ -512,8 +512,8 @@ void SolveSpaceUI::MenuFile(int id) { case GraphicsWindow::MNU_EXPORT_SECTION: { std::string exportFile; - if(!GetSaveFile(exportFile, CnfThawString("", "SectionExportFormat"), - VEC_PATTERN)) break; + if(!GetSaveFile(&exportFile, CnfThawString("", "SectionExportFormat"), + VectorFileFilter)) break; CnfFreezeString(Extension(exportFile), "SectionExportFormat"); SS.ExportSectionTo(exportFile); @@ -522,8 +522,8 @@ void SolveSpaceUI::MenuFile(int id) { case GraphicsWindow::MNU_EXPORT_MESH: { std::string exportFile; - if(!GetSaveFile(exportFile, CnfThawString("", "MeshExportFormat"), - MESH_PATTERN)) break; + if(!GetSaveFile(&exportFile, CnfThawString("", "MeshExportFormat"), + MeshFileFilter)) break; CnfFreezeString(Extension(exportFile), "MeshExportFormat"); SS.ExportMeshTo(exportFile); @@ -532,8 +532,8 @@ void SolveSpaceUI::MenuFile(int id) { case GraphicsWindow::MNU_EXPORT_SURFACES: { std::string exportFile; - if(!GetSaveFile(exportFile, CnfThawString("", "SurfacesExportFormat"), - SRF_PATTERN)) break; + if(!GetSaveFile(&exportFile, CnfThawString("", "SurfacesExportFormat"), + SurfaceFileFilter)) break; CnfFreezeString(Extension(exportFile), "SurfacesExportFormat"); StepFileWriter sfw = {}; @@ -750,7 +750,7 @@ void SolveSpaceUI::MenuAnalyze(int id) { case GraphicsWindow::MNU_STOP_TRACING: { std::string exportFile; - if(GetSaveFile(exportFile, "", CSV_PATTERN)) { + if(GetSaveFile(&exportFile, "", CsvFileFilter)) { FILE *f = ssfopen(exportFile, "wb"); if(f) { int i; diff --git a/src/solvespace.h b/src/solvespace.h index 6a12c1f1..c92b08bc 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -150,69 +150,61 @@ DialogChoice LocateImportedFileYesNoCancel(const std::string &filename, #define AUTOSAVE_SUFFIX "~" -#if defined(HAVE_GTK) - // Selection pattern format to be parsed by GTK3 glue code: - // "PNG File\t*.png\n" - // "JPEG File\t*.jpg\t*.jpeg\n" - // "All Files\t*" -# define PAT1(desc,e1) desc "\t*." e1 "\n" -# define PAT2(desc,e1,e2) desc "\t*." e1 "\t*." e2 "\n" -# define ENDPAT "All Files\t*" -#elif defined(__APPLE__) - // Selection pattern format to be parsed by Cocoa glue code: - // "PNG File\tpng\n" - // "JPEG file\tjpg,jpeg\n" - // "All Files\t*" -# define PAT1(desc,e1) desc "\t" e1 "\n" -# define PAT2(desc,e1,e2) desc "\t" e1 "," e2 "\n" -# define ENDPAT "All Files\t*" -#else - // Selection pattern format for Win32's OPENFILENAME.lpstrFilter: - // "PNG File (*.png)\0*.png\0" - // "JPEG File (*.jpg;*.jpeg)\0*.jpg;*.jpeg\0" - // "All Files (*)\0*\0\0" -# define PAT1(desc,e1) desc " (*." e1 ")\0*." e1 "\0" -# define PAT2(desc,e1,e2) desc " (*." e1 ";*." e2 ")\0*." e1 ";*." e2 "\0" -# define ENDPAT "All Files (*)\0*\0\0" -#endif +struct FileFilter { + const char *name; + const char *patterns[3]; +}; // SolveSpace native file format -#define SLVS_PATTERN PAT1("SolveSpace Models", "slvs") ENDPAT +const FileFilter SlvsFileFilter[] = { + { "SolveSpace models", { "slvs" } }, + { NULL } +}; // PNG format bitmap -#define PNG_PATTERN PAT1("PNG", "png") ENDPAT +const FileFilter PngFileFilter[] = { + { "PNG", { "png" } }, + { NULL } +}; // Triangle mesh -#define MESH_PATTERN \ - PAT1("STL Mesh", "stl") \ - PAT1("Wavefront OBJ Mesh", "obj") \ - PAT1("Three.js-compatible Mesh, with viewer", "html") \ - PAT1("Three.js-compatible Mesh, mesh only", "js") \ - ENDPAT +const FileFilter MeshFileFilter[] = { + { "STL mesh", { "stl" } }, + { "Wavefront OBJ mesh", { "obj" } }, + { "Three.js-compatible mesh, with viewer", { "html" } }, + { "Three.js-compatible mesh, mesh only", { "js" } }, + { NULL } +}; // NURBS surfaces -#define SRF_PATTERN PAT2("STEP File", "step", "stp") ENDPAT +const FileFilter SurfaceFileFilter[] = { + { "STEP file", { "step", "stp" } }, + { NULL } +}; // 2d vector (lines and curves) format -#define VEC_PATTERN \ - PAT1("PDF File", "pdf") \ - PAT2("Encapsulated PostScript", "eps", "ps") \ - PAT1("Scalable Vector Graphics", "svg") \ - PAT2("STEP File", "step", "stp") \ - PAT1("DXF File (AutoCAD 2007)", "dxf") \ - PAT2("HPGL File", "plt", "hpgl") \ - PAT1("G Code", "txt") \ - ENDPAT +const FileFilter VectorFileFilter[] = { + { "PDF file", { "pdf" } }, + { "Encapsulated PostScript", { "eps", "ps" } }, + { "Scalable Vector Graphics", { "svg" } }, + { "STEP file", { "step", "stp" } }, + { "DXF file (AutoCAD 2007)", { "dxf" } }, + { "HPGL file", { "plt", "hpgl" } }, + { "G Code", { "ngc", "txt" } }, + { NULL } +}; // 3d vector (wireframe lines and curves) format -#define V3D_PATTERN \ - PAT2("STEP File", "step", "stp") \ - PAT1("DXF File (AutoCAD 2007)", "dxf") \ - ENDPAT +const FileFilter Vector3dFileFilter[] = { + { "STEP file", { "step", "stp" } }, + { "DXF file (AutoCAD 2007)", { "dxf" } }, + { NULL } +}; // Comma-separated value, like a spreadsheet would use -#define CSV_PATTERN \ - PAT1("CSV File", "csv") \ - ENDPAT +const FileFilter CsvFileFilter[] = { + { "CSV", { "csv" } }, + { NULL } +}; -bool GetSaveFile(std::string &filename, const std::string &defExtension, - const char *selPattern); -bool GetOpenFile(std::string &filename, const std::string &defExtension, - const char *selPattern); +bool GetSaveFile(std::string *filename, const std::string &defExtension, + const FileFilter filters[]); +bool GetOpenFile(std::string *filename, const std::string &defExtension, + const FileFilter filters[]); std::vector GetFontFiles(); void OpenWebsite(const char *url); diff --git a/src/style.cpp b/src/style.cpp index c793bcef..75abffb2 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -391,7 +391,7 @@ void TextWindow::ScreenBackgroundImage(int link, uint32_t v) { png_info *info_ptr = NULL; std::string importFile; - if(!GetOpenFile(importFile, "", PNG_PATTERN)) goto err; + if(!GetOpenFile(&importFile, "", PngFileFilter)) goto err; f = ssfopen(importFile, "rb"); if(!f) goto err; diff --git a/src/win32/w32main.cpp b/src/win32/w32main.cpp index 8f9d551c..72fd2bef 100644 --- a/src/win32/w32main.cpp +++ b/src/win32/w32main.cpp @@ -987,23 +987,39 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, //----------------------------------------------------------------------------- // Common dialog routines, to open or save a file. //----------------------------------------------------------------------------- -static size_t strlen2(const char *p) { - const char *s = p; - while(*p || (!*p && *(p+1))) p++; - return p - s + 1; +static std::string ConvertFilters(const FileFilter ssFilters[]) { + std::string filter; + for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { + std::string desc, patterns; + for(const char *const *ssPattern = ssFilter->patterns; *ssPattern; ssPattern++) { + std::string pattern = "*." + std::string(*ssPattern); + if(desc == "") + desc = pattern; + else + desc += ", " + pattern; + if(patterns == "") + patterns = pattern; + else + patterns += ";" + pattern; + } + filter += std::string(ssFilter->name) + " (" + desc + ")" + '\0'; + filter += patterns + '\0'; + } + filter += '\0'; + return filter; } -static bool OpenSaveFile(bool isOpen, std::string &filename, - const std::string &defExtension, const char *selPattern) { +static bool OpenSaveFile(bool isOpen, std::string *filename, const std::string &defExtension, + const FileFilter filters[]) { // UNC paths may be as long as 32767 characters. // Unfortunately, the Get*FileName API does not provide any way to use it // except with a preallocated buffer of fixed size, so we use something // reasonably large. const int len = 32768; wchar_t filenameC[len] = {}; - wcsncpy(filenameC, Widen(filename).c_str(), len - 1); + wcsncpy(filenameC, Widen(*filename).c_str(), len - 1); - std::wstring selPatternW = Widen(std::string(selPattern, strlen2(selPattern))); + std::wstring selPatternW = Widen(ConvertFilters(filters)); std::wstring defExtensionW = Widen(defExtension); OPENFILENAME ofn = {}; @@ -1030,20 +1046,20 @@ static bool OpenSaveFile(bool isOpen, std::string &filename, EnableWindow(GraphicsWnd, true); SetForegroundWindow(GraphicsWnd); - if(r) filename = Narrow(filenameC); + if(r) *filename = Narrow(filenameC); return r ? true : false; } -bool SolveSpace::GetOpenFile(std::string &filename, - const std::string &defExtension, const char *selPattern) +bool SolveSpace::GetOpenFile(std::string *filename, const std::string &defExtension, + const FileFilter filters[]) { - return OpenSaveFile(true, filename, defExtension, selPattern); + return OpenSaveFile(true, filename, defExtension, filters); } -bool SolveSpace::GetSaveFile(std::string &filename, - const std::string &defExtension, const char *selPattern) +bool SolveSpace::GetSaveFile(std::string *filename, const std::string &defExtension, + const FileFilter filters[]) { - return OpenSaveFile(false, filename, defExtension, selPattern); + return OpenSaveFile(false, filename, defExtension, filters); } DialogChoice SolveSpace::SaveFileYesNoCancel(void)