diff --git a/res/locales/en_US.po b/res/locales/en_US.po index 45db4fd1..5545ee6a 100644 --- a/res/locales/en_US.po +++ b/res/locales/en_US.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: SolveSpace 3.0\n" "Report-Msgid-Bugs-To: whitequark@whitequark.org\n" -"POT-Creation-Date: 2017-01-07 06:44+0000\n" +"POT-Creation-Date: 2017-01-11 03:01+0000\n" "PO-Revision-Date: 2017-01-05 10:30+0000\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -1398,6 +1398,191 @@ msgstr "click to place bottom left of text" msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT" msgstr "NEW COMMENT -- DOUBLE-CLICK TO EDIT" +#: platform/cocoamain.mm:481 platform/gtkmain.cpp:582 platform/w32main.cpp:448 +#: platform/w32main.cpp:1373 +msgctxt "title" +msgid "(new sketch)" +msgstr "(new sketch)" + +#: platform/cocoamain.mm:710 platform/gtkmain.cpp:896 platform/w32main.cpp:1292 +msgid "(no recent files)" +msgstr "(no recent files)" + +#: platform/cocoamain.mm:828 platform/gtkmain.cpp:1010 +msgid "untitled" +msgstr "untitled" + +#: platform/cocoamain.mm:859 +msgid "Do you want to save the changes you made to the new sketch?" +msgstr "Do you want to save the changes you made to the new sketch?" + +#: platform/cocoamain.mm:861 +msgid "Your changes will be lost if you don't save them." +msgstr "Your changes will be lost if you don't save them." + +#: platform/cocoamain.mm:862 +msgctxt "button" +msgid "Save" +msgstr "Save" + +#: platform/cocoamain.mm:863 platform/cocoamain.mm:904 +msgctxt "button" +msgid "Cancel" +msgstr "Cancel" + +#: platform/cocoamain.mm:864 +msgctxt "button" +msgid "Don't Save" +msgstr "Don't Save" + +#: platform/cocoamain.mm:879 +msgid "An autosave file is availible for this project." +msgstr "An autosave file is availible for this project." + +#: platform/cocoamain.mm:881 +msgid "Do you want to load the autosave file instead?" +msgstr "Do you want to load the autosave file instead?" + +#: platform/cocoamain.mm:882 +msgctxt "button" +msgid "Load" +msgstr "Load" + +#: platform/cocoamain.mm:883 +msgctxt "button" +msgid "Don't Load" +msgstr "Don't Load" + +#: platform/cocoamain.mm:899 +msgid "" +"Do you want to locate it manually?\n" +"If you select “No”, any geometry that depends on the missing file will be " +"removed." +msgstr "" +"Do you want to locate it manually?\n" +"If you select “No”, any geometry that depends on the missing file will be " +"removed." + +#: platform/cocoamain.mm:902 +msgctxt "button" +msgid "Yes" +msgstr "Yes" + +#: platform/cocoamain.mm:905 +msgctxt "button" +msgid "No" +msgstr "No" + +#: platform/cocoamain.mm:1125 platform/w32main.cpp:180 +msgctxt "button" +msgid "OK" +msgstr "OK" + +#: platform/cocoamain.mm:1208 platform/gtkmain.cpp:1377 +#: platform/w32main.cpp:1395 platform/w32main.cpp:1435 +msgctxt "title" +msgid "Property Browser" +msgstr "Property Browser" + +#: platform/gtkmain.cpp:952 +msgctxt "title" +msgid "Open File" +msgstr "Open File" + +#: platform/gtkmain.cpp:954 +msgid "_Cancel" +msgstr "_Cancel" + +#: platform/gtkmain.cpp:955 +msgid "_Open" +msgstr "_Open" + +#: platform/gtkmain.cpp:1000 +msgctxt "title" +msgid "Save File" +msgstr "Save File" + +#: platform/gtkmain.cpp:1003 platform/gtkmain.cpp:1040 +#: platform/gtkmain.cpp:1088 +msgctxt "button" +msgid "_Cancel" +msgstr "_Cancel" + +#: platform/gtkmain.cpp:1004 platform/gtkmain.cpp:1038 +msgctxt "button" +msgid "_Save" +msgstr "_Save" + +#: platform/gtkmain.cpp:1033 platform/w32main.cpp:1153 +msgid "" +"The file has changed since it was last saved.\n" +"\n" +"Do you want to save the changes?" +msgstr "" +"The file has changed since it was last saved.\n" +"\n" +"Do you want to save the changes?" + +#: platform/gtkmain.cpp:1037 platform/w32main.cpp:1155 +msgctxt "title" +msgid "Modified File" +msgstr "Modified File" + +#: platform/gtkmain.cpp:1039 +msgctxt "button" +msgid "Do_n't Save" +msgstr "Do_n't Save" + +#: platform/gtkmain.cpp:1057 platform/w32main.cpp:1179 +msgid "" +"An autosave file is availible for this project.\n" +"\n" +"Do you want to load the autosave file instead?" +msgstr "" +"An autosave file is availible for this project.\n" +"\n" +"Do you want to load the autosave file instead?" + +#: platform/gtkmain.cpp:1061 platform/w32main.cpp:1181 +msgctxt "title" +msgid "Autosave Available" +msgstr "Autosave Available" + +#: platform/gtkmain.cpp:1062 +msgctxt "button" +msgid "_Load autosave" +msgstr "_Load autosave" + +#: platform/gtkmain.cpp:1063 +msgctxt "button" +msgid "Do_n't Load" +msgstr "Do_n't Load" + +#: platform/gtkmain.cpp:1084 platform/w32main.cpp:1209 +msgctxt "title" +msgid "Missing File" +msgstr "Missing File" + +#: platform/gtkmain.cpp:1085 +msgctxt "button" +msgid "_Yes" +msgstr "_Yes" + +#: platform/gtkmain.cpp:1086 +msgctxt "button" +msgid "_No" +msgstr "_No" + +#: platform/gtkmain.cpp:1300 platform/w32main.cpp:176 +msgctxt "title" +msgid "Error" +msgstr "Error" + +#: platform/gtkmain.cpp:1300 platform/w32main.cpp:176 +msgctxt "title" +msgid "Message" +msgstr "Message" + #: style.cpp:161 msgid "" "Can't assign style to an entity that's derived from another entity; try " @@ -1558,6 +1743,66 @@ msgstr "Nearest isometric view" msgid "Align view to active workplane" msgstr "Align view to active workplane" +#: ui.h:69 +msgid "SolveSpace models" +msgstr "SolveSpace models" + +#: ui.h:74 +msgid "PNG file" +msgstr "PNG file" + +#: ui.h:79 +msgid "STL mesh" +msgstr "STL mesh" + +#: ui.h:80 +msgid "Wavefront OBJ mesh" +msgstr "Wavefront OBJ mesh" + +#: ui.h:81 +msgid "Three.js-compatible mesh, with viewer" +msgstr "Three.js-compatible mesh, with viewer" + +#: ui.h:82 +msgid "Three.js-compatible mesh, mesh only" +msgstr "Three.js-compatible mesh, mesh only" + +#: ui.h:87 ui.h:95 ui.h:103 +msgid "STEP file" +msgstr "STEP file" + +#: ui.h:92 +msgid "PDF file" +msgstr "PDF file" + +#: ui.h:93 +msgid "Encapsulated PostScript" +msgstr "Encapsulated PostScript" + +#: ui.h:94 +msgid "Scalable Vector Graphics" +msgstr "Scalable Vector Graphics" + +#: ui.h:96 ui.h:104 +msgid "DXF file (AutoCAD 2007)" +msgstr "DXF file (AutoCAD 2007)" + +#: ui.h:97 +msgid "HPGL file" +msgstr "HPGL file" + +#: ui.h:98 +msgid "G Code" +msgstr "G Code" + +#: ui.h:109 +msgid "AutoCAD DXF and DWG files" +msgstr "AutoCAD DXF and DWG files" + +#: ui.h:114 +msgid "Comma-separated values" +msgstr "Comma-separated values" + #: view.cpp:78 msgid "Scale cannot be zero or negative." msgstr "Scale cannot be zero or negative." diff --git a/res/messages.pot b/res/messages.pot index d0c96606..69530cb2 100644 --- a/res/messages.pot +++ b/res/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: SolveSpace 3.0\n" "Report-Msgid-Bugs-To: whitequark@whitequark.org\n" -"POT-Creation-Date: 2017-01-07 06:44+0000\n" +"POT-Creation-Date: 2017-01-11 03:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1212,6 +1212,180 @@ msgstr "" msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT" msgstr "" +#: platform/cocoamain.mm:481 platform/gtkmain.cpp:582 platform/w32main.cpp:448 +#: platform/w32main.cpp:1373 +msgctxt "title" +msgid "(new sketch)" +msgstr "" + +#: platform/cocoamain.mm:710 platform/gtkmain.cpp:896 platform/w32main.cpp:1292 +msgid "(no recent files)" +msgstr "" + +#: platform/cocoamain.mm:828 platform/gtkmain.cpp:1010 +msgid "untitled" +msgstr "" + +#: platform/cocoamain.mm:859 +msgid "Do you want to save the changes you made to the new sketch?" +msgstr "" + +#: platform/cocoamain.mm:861 +msgid "Your changes will be lost if you don't save them." +msgstr "" + +#: platform/cocoamain.mm:862 +msgctxt "button" +msgid "Save" +msgstr "" + +#: platform/cocoamain.mm:863 platform/cocoamain.mm:904 +msgctxt "button" +msgid "Cancel" +msgstr "" + +#: platform/cocoamain.mm:864 +msgctxt "button" +msgid "Don't Save" +msgstr "" + +#: platform/cocoamain.mm:879 +msgid "An autosave file is availible for this project." +msgstr "" + +#: platform/cocoamain.mm:881 +msgid "Do you want to load the autosave file instead?" +msgstr "" + +#: platform/cocoamain.mm:882 +msgctxt "button" +msgid "Load" +msgstr "" + +#: platform/cocoamain.mm:883 +msgctxt "button" +msgid "Don't Load" +msgstr "" + +#: platform/cocoamain.mm:899 +msgid "" +"Do you want to locate it manually?\n" +"If you select “No”, any geometry that depends on the missing file will be removed." +msgstr "" + +#: platform/cocoamain.mm:902 +msgctxt "button" +msgid "Yes" +msgstr "" + +#: platform/cocoamain.mm:905 +msgctxt "button" +msgid "No" +msgstr "" + +#: platform/cocoamain.mm:1125 platform/w32main.cpp:180 +msgctxt "button" +msgid "OK" +msgstr "" + +#: platform/cocoamain.mm:1208 platform/gtkmain.cpp:1377 platform/w32main.cpp:1395 +#: platform/w32main.cpp:1435 +msgctxt "title" +msgid "Property Browser" +msgstr "" + +#: platform/gtkmain.cpp:952 +msgctxt "title" +msgid "Open File" +msgstr "" + +#: platform/gtkmain.cpp:954 +msgid "_Cancel" +msgstr "" + +#: platform/gtkmain.cpp:955 +msgid "_Open" +msgstr "" + +#: platform/gtkmain.cpp:1000 +msgctxt "title" +msgid "Save File" +msgstr "" + +#: platform/gtkmain.cpp:1003 platform/gtkmain.cpp:1040 platform/gtkmain.cpp:1088 +msgctxt "button" +msgid "_Cancel" +msgstr "" + +#: platform/gtkmain.cpp:1004 platform/gtkmain.cpp:1038 +msgctxt "button" +msgid "_Save" +msgstr "" + +#: platform/gtkmain.cpp:1033 platform/w32main.cpp:1153 +msgid "" +"The file has changed since it was last saved.\n" +"\n" +"Do you want to save the changes?" +msgstr "" + +#: platform/gtkmain.cpp:1037 platform/w32main.cpp:1155 +msgctxt "title" +msgid "Modified File" +msgstr "" + +#: platform/gtkmain.cpp:1039 +msgctxt "button" +msgid "Do_n't Save" +msgstr "" + +#: platform/gtkmain.cpp:1057 platform/w32main.cpp:1179 +msgid "" +"An autosave file is availible for this project.\n" +"\n" +"Do you want to load the autosave file instead?" +msgstr "" + +#: platform/gtkmain.cpp:1061 platform/w32main.cpp:1181 +msgctxt "title" +msgid "Autosave Available" +msgstr "" + +#: platform/gtkmain.cpp:1062 +msgctxt "button" +msgid "_Load autosave" +msgstr "" + +#: platform/gtkmain.cpp:1063 +msgctxt "button" +msgid "Do_n't Load" +msgstr "" + +#: platform/gtkmain.cpp:1084 platform/w32main.cpp:1209 +msgctxt "title" +msgid "Missing File" +msgstr "" + +#: platform/gtkmain.cpp:1085 +msgctxt "button" +msgid "_Yes" +msgstr "" + +#: platform/gtkmain.cpp:1086 +msgctxt "button" +msgid "_No" +msgstr "" + +#: platform/gtkmain.cpp:1300 platform/w32main.cpp:176 +msgctxt "title" +msgid "Error" +msgstr "" + +#: platform/gtkmain.cpp:1300 platform/w32main.cpp:176 +msgctxt "title" +msgid "Message" +msgstr "" + #: style.cpp:161 msgid "" "Can't assign style to an entity that's derived from another entity; try assigning a style to this " @@ -1370,6 +1544,66 @@ msgstr "" msgid "Align view to active workplane" msgstr "" +#: ui.h:69 +msgid "SolveSpace models" +msgstr "" + +#: ui.h:74 +msgid "PNG file" +msgstr "" + +#: ui.h:79 +msgid "STL mesh" +msgstr "" + +#: ui.h:80 +msgid "Wavefront OBJ mesh" +msgstr "" + +#: ui.h:81 +msgid "Three.js-compatible mesh, with viewer" +msgstr "" + +#: ui.h:82 +msgid "Three.js-compatible mesh, mesh only" +msgstr "" + +#: ui.h:87 ui.h:95 ui.h:103 +msgid "STEP file" +msgstr "" + +#: ui.h:92 +msgid "PDF file" +msgstr "" + +#: ui.h:93 +msgid "Encapsulated PostScript" +msgstr "" + +#: ui.h:94 +msgid "Scalable Vector Graphics" +msgstr "" + +#: ui.h:96 ui.h:104 +msgid "DXF file (AutoCAD 2007)" +msgstr "" + +#: ui.h:97 +msgid "HPGL file" +msgstr "" + +#: ui.h:98 +msgid "G Code" +msgstr "" + +#: ui.h:109 +msgid "AutoCAD DXF and DWG files" +msgstr "" + +#: ui.h:114 +msgid "Comma-separated values" +msgstr "" + #: view.cpp:78 msgid "Scale cannot be zero or negative." msgstr "" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8309e49c..d8f2b0b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -125,10 +125,14 @@ else() endforeach() endif() +set(every_platform_SOURCES + platform/w32main.cpp + platform/gtkmain.cpp + platform/cocoamain.mm) + # solvespace library set(solvespace_core_HEADERS - config.h dsc.h expr.h polygon.h @@ -212,11 +216,15 @@ if(HAVE_GETTEXT) set(output_pot ${CMAKE_CURRENT_SOURCE_DIR}/../res/messages.pot) set(templ_po ${CMAKE_CURRENT_BINARY_DIR}/messages.po) set(output_po ${CMAKE_CURRENT_SOURCE_DIR}/../res/locales/en_US.po) - set(inputs ${solvespace_core_SOURCES}) + set(inputs + ${solvespace_core_SOURCES} + ${solvespace_core_HEADERS} + ${every_platform_SOURCES}) add_custom_command( OUTPUT ${output_pot} COMMAND ${XGETTEXT} + --language=C++ --keyword --keyword=_ --keyword=N_ --keyword=C_:2,1c --keyword=CN_:2,1c --force-po --width=100 --sort-by-file --package-name=SolveSpace diff --git a/src/platform/cocoamain.mm b/src/platform/cocoamain.mm index f0839cc8..b8699949 100644 --- a/src/platform/cocoamain.mm +++ b/src/platform/cocoamain.mm @@ -13,16 +13,22 @@ using SolveSpace::dbp; +/* Utility functions */ + +static NSString* Wrap(const std::string &s) { + return [NSString stringWithUTF8String:s.c_str()]; +} + /* Settings */ namespace SolveSpace { void CnfFreezeInt(uint32_t val, const std::string &key) { [[NSUserDefaults standardUserDefaults] - setInteger:val forKey:[NSString stringWithUTF8String:key.c_str()]]; + setInteger:val forKey:Wrap(key)]; } uint32_t CnfThawInt(uint32_t val, const std::string &key) { - NSString *nsKey = [NSString stringWithUTF8String:key.c_str()]; + NSString *nsKey = Wrap(key); if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) return [[NSUserDefaults standardUserDefaults] integerForKey:nsKey]; return val; @@ -30,11 +36,11 @@ uint32_t CnfThawInt(uint32_t val, const std::string &key) { void CnfFreezeFloat(float val, const std::string &key) { [[NSUserDefaults standardUserDefaults] - setFloat:val forKey:[NSString stringWithUTF8String:key.c_str()]]; + setFloat:val forKey:Wrap(key)]; } float CnfThawFloat(float val, const std::string &key) { - NSString *nsKey = [NSString stringWithUTF8String:key.c_str()]; + NSString *nsKey = Wrap(key); if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) return [[NSUserDefaults standardUserDefaults] floatForKey:nsKey]; return val; @@ -42,12 +48,12 @@ float CnfThawFloat(float val, const std::string &key) { void CnfFreezeString(const std::string &val, const std::string &key) { [[NSUserDefaults standardUserDefaults] - setObject:[NSString stringWithUTF8String:val.c_str()] - forKey:[NSString stringWithUTF8String:key.c_str()]]; + setObject:Wrap(val) + forKey:Wrap(key)]; } std::string CnfThawString(const std::string &val, const std::string &key) { - NSString *nsKey = [NSString stringWithUTF8String:key.c_str()]; + NSString *nsKey = Wrap(key); if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) { NSString *nsNewVal = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey]; return [nsNewVal UTF8String]; @@ -459,45 +465,45 @@ void GetGraphicsWindowSize(int *w, int *h) { *h = (int)size.height; } -void InvalidateGraphics(void) { +void InvalidateGraphics() { [GWView setNeedsDisplay:YES]; } -void PaintGraphics(void) { +void PaintGraphics() { [GWView setNeedsDisplay:YES]; CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES); } void SetCurrentFilename(const std::string &filename) { if(!filename.empty()) { - [GW setTitleWithRepresentedFilename:[NSString stringWithUTF8String:filename.c_str()]]; + [GW setTitleWithRepresentedFilename:Wrap(filename)]; } else { - [GW setTitle:@"(new sketch)"]; + [GW setTitle:Wrap(C_("title", "(new sketch)"))]; [GW setRepresentedFilename:@""]; } } -void ToggleFullScreen(void) { +void ToggleFullScreen() { [GW toggleFullScreen:nil]; } -bool FullScreenIsActive(void) { +bool FullScreenIsActive() { return [GWDelegate isFullscreen]; } void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, const std::string &str) { - [GWView startEditing:[NSString stringWithUTF8String:str.c_str()] + [GWView startEditing:Wrap(str) at:(NSPoint){(CGFloat)x, (CGFloat)y} withHeight:fontHeight withMinWidthInChars:minWidthChars]; } -void HideGraphicsEditControl(void) { +void HideGraphicsEditControl() { [GWView stopEditing]; } -bool GraphicsEditControlIsVisible(void) { +bool GraphicsEditControlIsVisible() { return [GWView isEditing]; } } @@ -547,13 +553,13 @@ void AddContextMenuItem(const char *label, ContextCommand cmd) { } } -void CreateContextSubmenu(void) { +void CreateContextSubmenu() { ssassert(!contextSubmenu, "Unexpected nested submenu"); contextSubmenu = [[NSMenu alloc] initWithTitle:@""]; } -ContextCommand ShowContextMenu(void) { +ContextCommand ShowContextMenu() { if(!contextMenu) return ContextCommand::CANCELLED; @@ -629,7 +635,7 @@ void InitMainMenu(NSMenu *mainMenu) { current_level = entry->level; if(entry->label) { - label = [NSString stringWithUTF8String:Translate(entry->label).c_str()]; + label = Wrap(Translate(entry->label)); /* OS X does not support mnemonics */ label = [label stringByReplacingOccurrencesOfString:@"&" withString:@""]; @@ -668,7 +674,7 @@ void InitMainMenu(NSMenu *mainMenu) { for(auto locale : Locales()) { NSMenuItem *localeMenuItem = [localeMenu addItemWithTitle: - [NSString stringWithUTF8String:locale.displayName.c_str()] + Wrap(locale.displayName) action:NULL keyEquivalent:@""]; [localeMenuItem setTag:(NSInteger)i++]; [localeMenuItem setTarget:[MainMenuResponder class]]; @@ -701,7 +707,7 @@ static void RefreshRecentMenu(SolveSpace::Command cmd, SolveSpace::Command base) if(std::string(RecentFile[0]).empty()) { NSMenuItem *placeholder = [[NSMenuItem alloc] - initWithTitle:@"(no recent files)" action:nil keyEquivalent:@""]; + initWithTitle:Wrap(_("(no recent files)")) action:nil keyEquivalent:@""]; [placeholder setEnabled:NO]; [menu addItem:placeholder]; } else { @@ -710,7 +716,7 @@ static void RefreshRecentMenu(SolveSpace::Command cmd, SolveSpace::Command base) break; NSMenuItem *item = [[NSMenuItem alloc] - initWithTitle:[[NSString stringWithUTF8String:RecentFile[i].c_str()] + initWithTitle:[Wrap(RecentFile[i]) stringByAbbreviatingWithTildeInPath] action:nil keyEquivalent:@""]; [item setTag:((uint32_t)base + i)]; @@ -721,16 +727,16 @@ static void RefreshRecentMenu(SolveSpace::Command cmd, SolveSpace::Command base) } } -void RefreshRecentMenus(void) { +void RefreshRecentMenus() { RefreshRecentMenu(Command::OPEN_RECENT, Command::RECENT_OPEN); RefreshRecentMenu(Command::GROUP_RECENT, Command::RECENT_LINK); } -void ToggleMenuBar(void) { +void ToggleMenuBar() { [NSMenu setMenuBarVisible:![NSMenu menuBarVisible]]; } -bool MenuBarIsVisible(void) { +bool MenuBarIsVisible() { return [NSMenu menuBarVisible]; } } @@ -801,8 +807,8 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension, desc += *ssPattern; } } - std::string title = std::string(ssFilter->name) + " (" + desc + ")"; - [button addItemWithTitle:[NSString stringWithUTF8String:title.c_str()]]; + std::string title = Translate(ssFilter->name) + " (" + desc + ")"; + [button addItemWithTitle:Wrap(title)]; [extensions addObject:[NSString stringWithUTF8String:ssFilter->patterns[0]]]; } [panel setAllowedFileTypes:extensions]; @@ -810,8 +816,7 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension, int extensionIndex = 0; if(defExtension != "") { - extensionIndex = [extensions indexOfObject: - [NSString stringWithUTF8String:defExtension.c_str()]]; + extensionIndex = [extensions indexOfObject:Wrap(defExtension)]; if(extensionIndex == -1) { extensionIndex = 0; } @@ -819,14 +824,15 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension, [button selectItemAtIndex:extensionIndex]; if(file->empty()) { - [panel setNameFieldStringValue:[@"untitled" - stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; + [panel setNameFieldStringValue: + [Wrap(_("untitled")) + stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; } else { [panel setDirectoryURL: - [NSURL fileURLWithPath:[NSString stringWithUTF8String:Dirname(*file).c_str()] + [NSURL fileURLWithPath:Wrap(Dirname(*file)) isDirectory:NO]]; [panel setNameFieldStringValue: - [[NSString stringWithUTF8String:Basename(*file, /*stripExtension=*/true).c_str()] + [Wrap(Basename(*file, /*stripExtension=*/true)) stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; } @@ -839,22 +845,23 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension, } } -SolveSpace::DialogChoice SolveSpace::SaveFileYesNoCancel(void) { +SolveSpace::DialogChoice SolveSpace::SaveFileYesNoCancel() { NSAlert *alert = [[NSAlert alloc] init]; if(!std::string(SolveSpace::SS.saveFile).empty()) { [alert setMessageText: [[@"Do you want to save the changes you made to the sketch “" stringByAppendingString: - [[NSString stringWithUTF8String:SolveSpace::SS.saveFile.c_str()] + [Wrap(SolveSpace::SS.saveFile) stringByAbbreviatingWithTildeInPath]] stringByAppendingString:@"”?"]]; } else { - [alert setMessageText:@"Do you want to save the changes you made to the new sketch?"]; + [alert setMessageText: + Wrap(_("Do you want to save the changes you made to the new sketch?"))]; } - [alert setInformativeText:@"Your changes will be lost if you don't save them."]; - [alert addButtonWithTitle:@"Save"]; - [alert addButtonWithTitle:@"Cancel"]; - [alert addButtonWithTitle:@"Don't Save"]; + [alert setInformativeText:Wrap(_("Your changes will be lost if you don't save them."))]; + [alert addButtonWithTitle:Wrap(C_("button", "Save"))]; + [alert addButtonWithTitle:Wrap(C_("button", "Cancel"))]; + [alert addButtonWithTitle:Wrap(C_("button", "Don't Save"))]; switch([alert runModal]) { case NSAlertFirstButtonReturn: return DIALOG_YES; @@ -866,14 +873,14 @@ SolveSpace::DialogChoice SolveSpace::SaveFileYesNoCancel(void) { } } -SolveSpace::DialogChoice SolveSpace::LoadAutosaveYesNo(void) { +SolveSpace::DialogChoice SolveSpace::LoadAutosaveYesNo() { NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText: - @"An autosave file is availible for this project."]; + Wrap(_("An autosave file is availible for this project."))]; [alert setInformativeText: - @"Do you want to load the autosave file instead?"]; - [alert addButtonWithTitle:@"Load"]; - [alert addButtonWithTitle:@"Don't Load"]; + Wrap(_("Do you want to load the autosave file instead?"))]; + [alert addButtonWithTitle:Wrap(C_("button", "Load"))]; + [alert addButtonWithTitle:Wrap(C_("button", "Don't Load"))]; switch([alert runModal]) { case NSAlertFirstButtonReturn: return DIALOG_YES; @@ -886,16 +893,16 @@ SolveSpace::DialogChoice SolveSpace::LoadAutosaveYesNo(void) { SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel( const std::string &filename, bool canCancel) { NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:[NSString stringWithUTF8String: - ("The linked file " + filename + " is not present.").c_str()]]; + [alert setMessageText: + Wrap("The linked file “" + filename + "” is not present.")]; [alert setInformativeText: - @"Do you want to locate it manually?\n" - "If you select \"No\", any geometry that depends on " - "the missing file will be removed."]; - [alert addButtonWithTitle:@"Yes"]; + Wrap(_("Do you want to locate it manually?\n" + "If you select “No”, any geometry that depends on " + "the missing file will be removed."))]; + [alert addButtonWithTitle:Wrap(C_("button", "Yes"))]; if(canCancel) - [alert addButtonWithTitle:@"Cancel"]; - [alert addButtonWithTitle:@"No"]; + [alert addButtonWithTitle:Wrap(C_("button", "Cancel"))]; + [alert addButtonWithTitle:Wrap(C_("button", "No"))]; switch([alert runModal]) { case NSAlertFirstButtonReturn: return DIALOG_YES; @@ -1039,7 +1046,6 @@ void InitTextWindow() { NSUtilityWindowMask)]; [[TW standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; [[TW standardWindowButton:NSWindowZoomButton] setHidden:YES]; - [TW setTitle:@"Property Browser"]; [TW setFrameAutosaveName:@"TextWindow"]; [TW setFloatingPanel:YES]; [TW setBecomesKeyOnlyIfNeeded:YES]; @@ -1082,7 +1088,7 @@ double GetScreenDpi() { return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f; } -void InvalidateText(void) { +void InvalidateText() { NSSize size = [TWView convertSizeToBacking:[TWView frame].size]; size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2; [TWView setFrameSize:[TWView convertSizeFromBacking:size]]; @@ -1098,15 +1104,15 @@ void SetMousePointerToHand(bool is_hand) { } void ShowTextEditControl(int x, int y, const std::string &str) { - return [TWView startEditing:[NSString stringWithUTF8String:str.c_str()] + return [TWView startEditing:Wrap(str) at:(NSPoint){(CGFloat)x, (CGFloat)y}]; } -void HideTextEditControl(void) { +void HideTextEditControl() { return [TWView stopEditing]; } -bool TextEditControlIsVisible(void) { +bool TextEditControlIsVisible() { return [TWView isEditing]; } }; @@ -1116,10 +1122,10 @@ bool TextEditControlIsVisible(void) { void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error) { NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:(error ? NSWarningAlertStyle : NSInformationalAlertStyle)]; - [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:Wrap(C_("button", "OK"))]; - /* do some additional formatting of the message these are - heuristics, but they are made failsafe and lead to nice results. */ + /* do some additional formatting of the message; + these are heuristics, but they are made failsafe and lead to nice results. */ NSString *input = [NSString stringWithUTF8String:str]; NSRange dot = [input rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString:@".:"]]; @@ -1195,10 +1201,14 @@ std::vector SolveSpace::GetFontFiles() { @end void SolveSpace::RefreshLocale() { + SS.UpdateWindowTitle(); SolveSpace::InitMainMenu([NSApp mainMenu]); + RefreshRecentMenus(); + + [TW setTitle:Wrap(C_("title", "Property Browser"))]; } -void SolveSpace::ExitNow(void) { +void SolveSpace::ExitNow() { [NSApp stop:nil]; } diff --git a/src/platform/gtkmain.cpp b/src/platform/gtkmain.cpp index 698a3e75..93e6c1db 100644 --- a/src/platform/gtkmain.cpp +++ b/src/platform/gtkmain.cpp @@ -10,8 +10,6 @@ #include #include -#include - #include #include @@ -53,6 +51,11 @@ #endif namespace SolveSpace { +/* Utility functions */ +std::string Title(const std::string &s) { + return "SolveSpace - " + s; +} + /* Settings */ /* Why not just use GSettings? Two reasons. It doesn't allow to easily see @@ -576,11 +579,7 @@ void PaintGraphics(void) { } void SetCurrentFilename(const std::string &filename) { - if(!filename.empty()) { - GW->set_title("SolveSpace - " + filename); - } else { - GW->set_title("SolveSpace - (not yet saved)"); - } + GW->set_title(Title(filename.empty() ? C_("title", "(new sketch)") : filename.c_str())); } void ToggleFullScreen(void) { @@ -893,13 +892,13 @@ static void RefreshRecentMenu(Command cmd, Command base) { Gtk::Menu *menu = new Gtk::Menu; recent->set_submenu(*menu); - if(std::string(RecentFile[0]).empty()) { - Gtk::MenuItem *placeholder = new Gtk::MenuItem("(no recent files)"); + if(RecentFile[0].empty()) { + Gtk::MenuItem *placeholder = new Gtk::MenuItem(_("(no recent files)")); placeholder->set_sensitive(false); menu->append(*placeholder); } else { for(size_t i = 0; i < MAX_RECENT; i++) { - if(std::string(RecentFile[i]).empty()) + if(RecentFile[i].empty()) break; RecentMenuItem *item = new RecentMenuItem(RecentFile[i], (uint32_t)base + i); @@ -921,7 +920,7 @@ static std::string ConvertFilters(std::string active, const FileFilter ssFilters Gtk::FileChooser *chooser) { for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { Glib::RefPtr filter = Gtk::FileFilter::create(); - filter->set_name(ssFilter->name); + filter->set_name(Translate(ssFilter->name)); bool is_active = false; std::string desc = ""; @@ -950,10 +949,10 @@ static std::string ConvertFilters(std::string active, const FileFilter ssFilters bool GetOpenFile(std::string *filename, const std::string &activeOrEmpty, const FileFilter filters[]) { - Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Open File"); + Gtk::FileChooserDialog chooser(*GW, Title(C_("title", "Open File"))); chooser.set_filename(*filename); - chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL); - chooser.add_button("_Open", Gtk::RESPONSE_OK); + chooser.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + chooser.add_button(_("_Open"), Gtk::RESPONSE_OK); chooser.set_current_folder(CnfThawString("", "FileChooserPath")); ConvertFilters(activeOrEmpty, filters, &chooser); @@ -998,17 +997,17 @@ static void ChooserFilterChanged(Gtk::FileChooserDialog *chooser) bool GetSaveFile(std::string *filename, const std::string &defExtension, const FileFilter filters[]) { - Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Save File", + Gtk::FileChooserDialog chooser(*GW, Title(C_("title", "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); + chooser.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); + chooser.add_button(C_("button", "_Save"), Gtk::RESPONSE_OK); std::string activeExtension = ConvertFilters(defExtension, filters, &chooser); if(filename->empty()) { chooser.set_current_folder(CnfThawString("", "FileChooserPath")); - chooser.set_current_name("untitled." + activeExtension); + chooser.set_current_name(std::string(_("untitled")) + "." + activeExtension); } else { chooser.set_current_folder(Dirname(*filename)); chooser.set_current_name(Basename(*filename, /*stripExtension=*/true) + @@ -1031,14 +1030,14 @@ bool GetSaveFile(std::string *filename, const std::string &defExtension, DialogChoice SaveFileYesNoCancel(void) { Glib::ustring message = - "The file has changed since it was last saved.\n" - "Do you want to save the changes?"; + _("The file has changed since it was last saved.\n\n" + "Do you want to save the changes?"); Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, /*is_modal*/ true); - dialog.set_title("SolveSpace - Modified File"); - dialog.add_button("_Save", Gtk::RESPONSE_YES); - dialog.add_button("Do_n't Save", Gtk::RESPONSE_NO); - dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); + dialog.set_title(Title(C_("title", "Modified File"))); + dialog.add_button(C_("button", "_Save"), Gtk::RESPONSE_YES); + dialog.add_button(C_("button", "Do_n't Save"), Gtk::RESPONSE_NO); + dialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); switch(dialog.run()) { case Gtk::RESPONSE_YES: @@ -1055,13 +1054,13 @@ DialogChoice SaveFileYesNoCancel(void) { DialogChoice LoadAutosaveYesNo(void) { Glib::ustring message = - "An autosave file is availible for this project.\n" - "Do you want to load the autosave file instead?"; + _("An autosave file is availible for this project.\n\n" + "Do you want to load the autosave file instead?"); Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, /*is_modal*/ true); - dialog.set_title("SolveSpace - Autosave Available"); - dialog.add_button("_Load autosave", Gtk::RESPONSE_YES); - dialog.add_button("Do_n't Load", Gtk::RESPONSE_NO); + dialog.set_title(Title(C_("title", "Autosave Available"))); + dialog.add_button(C_("button", "_Load autosave"), Gtk::RESPONSE_YES); + dialog.add_button(C_("button", "Do_n't Load"), Gtk::RESPONSE_NO); switch(dialog.run()) { case Gtk::RESPONSE_YES: @@ -1076,17 +1075,17 @@ DialogChoice LoadAutosaveYesNo(void) { DialogChoice LocateImportedFileYesNoCancel(const std::string &filename, bool canCancel) { Glib::ustring message = - "The linked file " + filename + " is not present.\n" - "Do you want to locate it manually?\n" + "The linked file " + filename + " is not present.\n\n" + "Do you want to locate it manually?\n\n" "If you select \"No\", any geometry that depends on " "the missing file will be removed."; Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, /*is_modal*/ true); - dialog.set_title("SolveSpace - Missing File"); - dialog.add_button("_Yes", Gtk::RESPONSE_YES); - dialog.add_button("_No", Gtk::RESPONSE_NO); + dialog.set_title(Title(C_("title", "Missing File"))); + dialog.add_button(C_("button", "_Yes"), Gtk::RESPONSE_YES); + dialog.add_button(C_("button", "_No"), Gtk::RESPONSE_NO); if(canCancel) - dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); + dialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); switch(dialog.run()) { case Gtk::RESPONSE_YES: @@ -1167,7 +1166,6 @@ public: set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY); set_skip_taskbar_hint(true); set_skip_pager_hint(true); - set_title("SolveSpace - Property Browser"); set_default_size(420, 300); _box.pack_start(_overlay, true, true); @@ -1298,7 +1296,8 @@ void DoMessageBox(const char *message, int rows, int cols, bool error) { Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, error ? Gtk::MESSAGE_ERROR : Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, /*is_modal*/ true); - dialog.set_title(error ? "SolveSpace - Error" : "SolveSpace - Message"); + dialog.set_title(error ? + Title(C_("title", "Error")) : Title(C_("title", "Message"))); dialog.run(); } @@ -1365,13 +1364,17 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gxevent, GdkEvent *, gpointer) /* Application lifecycle */ void RefreshLocale() { + SS.UpdateWindowTitle(); for(auto menu : GW->get_menubar().get_children()) { GW->get_menubar().remove(*menu); } InitMainMenu(&GW->get_menubar()); + RefreshRecentMenus(); GW->get_menubar().show_all(); GW->get_menubar().accelerate(*GW); GW->get_menubar().accelerate(*TW); + + TW->set_title(Title(C_("title", "Property Browser"))); } void ExitNow() { @@ -1390,7 +1393,7 @@ int main(int argc, char** argv) { We set it back to C after all. */ setlocale(LC_ALL, ""); if(!Glib::get_charset()) { - std::cerr << "Sorry, only UTF-8 locales are supported." << std::endl; + dbp("Sorry, only UTF-8 locales are supported."); return 1; } setlocale(LC_ALL, "C"); @@ -1441,8 +1444,7 @@ int main(int argc, char** argv) { if(argc >= 2) { if(argc > 2) { - std::cerr << "Only the first file passed on command line will be opened." - << std::endl; + dbp("Only the first file passed on command line will be opened."); } /* Make sure the argument is valid UTF-8. */ diff --git a/src/platform/w32main.cpp b/src/platform/w32main.cpp index f8ee1160..a5b9838f 100644 --- a/src/platform/w32main.cpp +++ b/src/platform/w32main.cpp @@ -72,6 +72,13 @@ HFONT FixedFont; SiHdl SpaceNavigator = SI_NO_HANDLE; #endif +//----------------------------------------------------------------------------- +// Utility routines +//----------------------------------------------------------------------------- +std::wstring Title(const std::string &s) { + return Widen("SolveSpace - " + s); +} + //----------------------------------------------------------------------------- // Routines to display message boxes on screen. Do our own, instead of using // MessageBox, because that is not consistent from version to version and @@ -161,16 +168,16 @@ void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error) MessageString = str; RECT r; GetWindowRect(GraphicsWnd, &r); - const char *title = error ? "SolveSpace - Error" : "SolveSpace - Message"; int width = cols*SS.TW.CHAR_WIDTH + 20, height = rows*SS.TW.LINE_HEIGHT + 60; MessageWidth = width; MessageHeight = height; - MessageWnd = CreateWindowClient(0, L"MessageWnd", Widen(title).c_str(), + MessageWnd = CreateWindowClient(0, L"MessageWnd", + (error ? Title(C_("title", "Error")) : Title(C_("title", "Message"))).c_str(), WS_OVERLAPPED | WS_SYSMENU, r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL); - OkButton = CreateWindowExW(0, WC_BUTTON, L"OK", + OkButton = CreateWindowExW(0, WC_BUTTON, Widen(C_("button", "OK")).c_str(), WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON, (width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20, 70, 25, MessageWnd, NULL, Instance, NULL); @@ -437,11 +444,8 @@ static void ThawWindowPos(HWND hwnd, const std::string &name) } void SolveSpace::SetCurrentFilename(const std::string &filename) { - if(!filename.empty()) { - SetWindowTextW(GraphicsWnd, Widen("SolveSpace - " + filename).c_str()); - } else { - SetWindowTextW(GraphicsWnd, L"SolveSpace - (not yet saved)"); - } + SetWindowTextW(GraphicsWnd, + Title(filename.empty() ? C_("title", "(new sketch)") : filename).c_str()); } void SolveSpace::SetMousePointerToHand(bool yes) { @@ -1146,8 +1150,9 @@ DialogChoice SolveSpace::SaveFileYesNoCancel() EnableWindow(TextWnd, false); int r = MessageBoxW(GraphicsWnd, - L"The file has changed since it was last saved.\n\n" - L"Do you want to save the changes?", L"SolveSpace", + Widen(_("The file has changed since it was last saved.\n\n" + "Do you want to save the changes?")).c_str(), + Title(C_("title", "Modified File")).c_str(), MB_YESNOCANCEL | MB_ICONWARNING); EnableWindow(TextWnd, true); @@ -1171,8 +1176,9 @@ DialogChoice SolveSpace::LoadAutosaveYesNo() EnableWindow(TextWnd, false); int r = MessageBoxW(GraphicsWnd, - L"An autosave file is availible for this project.\n\n" - L"Do you want to load the autosave file instead?", L"SolveSpace", + Widen(_("An autosave file is availible for this project.\n\n" + "Do you want to load the autosave file instead?")).c_str(), + Title(C_("title", "Autosave Available")).c_str(), MB_YESNO | MB_ICONWARNING); EnableWindow(TextWnd, true); @@ -1199,7 +1205,8 @@ DialogChoice SolveSpace::LocateImportedFileYesNoCancel(const std::string &filena "If you select \"No\", any geometry that depends on " "the missing file will be removed."; - int r = MessageBoxW(GraphicsWnd, Widen(message).c_str(), L"SolveSpace", + int r = MessageBoxW(GraphicsWnd, Widen(message).c_str(), + Title(C_("title", "Missing File")).c_str(), (canCancel ? MB_YESNOCANCEL : MB_YESNO) | MB_ICONWARNING); EnableWindow(TextWnd, true); @@ -1282,7 +1289,7 @@ static void DoRecent(HMENU m, Command base) c++; } } - if(c == 0) AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"(no recent files)"); + if(c == 0) AppendMenuW(m, MF_STRING | MF_GRAYED, 0, Widen(_("(no recent files)")).c_str()); } void SolveSpace::RefreshRecentMenus() { @@ -1363,7 +1370,7 @@ static void CreateMainWindows() ssassert(RegisterClassEx(&wc), "Cannot register window class"); GraphicsWnd = CreateWindowExW(0, L"GraphicsWnd", - L"SolveSpace (not yet saved)", + Title(C_("title", "(new sketch)")).c_str(), WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS, 50, 50, 900, 600, NULL, NULL, Instance, NULL); @@ -1384,8 +1391,9 @@ static void CreateMainWindows() // We get the desired Alt+Tab behaviour by specifying that the text // window is a child of the graphics window. - TextWnd = CreateWindowExW(0, - L"TextWnd", L"SolveSpace - Property Browser", WS_THICKFRAME | WS_CLIPCHILDREN, + TextWnd = CreateWindowExW(0, L"TextWnd", + Title(C_("title", "Property Browser")).c_str(), + WS_THICKFRAME | WS_CLIPCHILDREN, 650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL); ssassert(TextWnd != NULL, "Cannot create window"); @@ -1415,11 +1423,16 @@ static void CreateMainWindows() } void SolveSpace::RefreshLocale() { + SS.UpdateWindowTitle(); + HMENU oldMenu = GetMenu(GraphicsWnd); SetMenu(GraphicsWnd, CreateGraphicsWindowMenus()); if(oldMenu != NULL) { DestroyMenu(oldMenu); } + RefreshRecentMenus(); + + SetWindowTextW(TextWnd, Title(C_("title", "Property Browser")).c_str()); } #ifdef HAVE_SPACEWARE diff --git a/src/resource.cpp b/src/resource.cpp index 8203f138..b6467fd8 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -1088,14 +1088,14 @@ PluralExpr::Token PluralExpr::Lex() { if(reader.TryChar('=')) { t.op = Token::Op::LE; } else { - t.op = Token::Op::LT; + t.op = Token::Op::LT; } } else if(reader.TryChar('>')) { t.type = Token::Type::BINARY_OP; if(reader.TryChar('=')) { t.op = Token::Op::GE; } else { - t.op = Token::Op::GT; + t.op = Token::Op::GT; } } else if(reader.TryChar('!')) { reader.ExpectChar('='); diff --git a/src/solvespace.h b/src/solvespace.h index b3006760..f88b1856 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -169,67 +169,13 @@ DialogChoice LocateImportedFileYesNoCancel(const std::string &filename, #define AUTOSAVE_SUFFIX "~" -struct FileFilter { - const char *name; - const char *patterns[3]; -}; - -// SolveSpace native file format -const FileFilter SlvsFileFilter[] = { - { "SolveSpace models", { "slvs" } }, - { NULL, {} } -}; -// PNG format bitmap -const FileFilter PngFileFilter[] = { - { "PNG file", { "png" } }, - { NULL, {} } -}; -// Triangle mesh -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 -const FileFilter SurfaceFileFilter[] = { - { "STEP file", { "step", "stp" } }, - { NULL, {} } -}; -// 2d vector (lines and curves) format -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 -const FileFilter Vector3dFileFilter[] = { - { "STEP file", { "step", "stp" } }, - { "DXF file (AutoCAD 2007)", { "dxf" } }, - { NULL, {} } -}; -// All Importable formats -const FileFilter ImportableFileFilter[] = { - { "AutoCAD DXF and DWG files", { "dxf", "dwg" } }, - { NULL, {} } -}; -// Comma-separated value, like a spreadsheet would use -const FileFilter CsvFileFilter[] = { - { "CSV", { "csv" } }, - { NULL, {} } -}; - enum class Unit : uint32_t { MM = 0, INCHES }; +struct FileFilter; + bool GetSaveFile(std::string *filename, const std::string &defExtension, const FileFilter filters[]); bool GetOpenFile(std::string *filename, const std::string &defExtension, diff --git a/src/ui.h b/src/ui.h index c7fa0f75..fdcb1ec7 100644 --- a/src/ui.h +++ b/src/ui.h @@ -58,6 +58,63 @@ inline const char *C_(const char *msgctxt, const char *msgid) { } #endif +// Filters for the file formats that we support. +struct FileFilter { + const char *name; + const char *patterns[3]; +}; + +// SolveSpace native file format +const FileFilter SlvsFileFilter[] = { + { N_("SolveSpace models"), { "slvs" } }, + { NULL, {} } +}; +// PNG format bitmap +const FileFilter PngFileFilter[] = { + { N_("PNG file"), { "png" } }, + { NULL, {} } +}; +// Triangle mesh +const FileFilter MeshFileFilter[] = { + { N_("STL mesh"), { "stl" } }, + { N_("Wavefront OBJ mesh"), { "obj" } }, + { N_("Three.js-compatible mesh, with viewer"), { "html" } }, + { N_("Three.js-compatible mesh, mesh only"), { "js" } }, + { NULL, {} } +}; +// NURBS surfaces +const FileFilter SurfaceFileFilter[] = { + { N_("STEP file"), { "step", "stp" } }, + { NULL, {} } +}; +// 2d vector (lines and curves) format +const FileFilter VectorFileFilter[] = { + { N_("PDF file"), { "pdf" } }, + { N_("Encapsulated PostScript"), { "eps", "ps" } }, + { N_("Scalable Vector Graphics"), { "svg" } }, + { N_("STEP file"), { "step", "stp" } }, + { N_("DXF file (AutoCAD 2007)"), { "dxf" } }, + { N_("HPGL file"), { "plt", "hpgl" } }, + { N_("G Code"), { "ngc", "txt" } }, + { NULL, {} } +}; +// 3d vector (wireframe lines and curves) format +const FileFilter Vector3dFileFilter[] = { + { N_("STEP file"), { "step", "stp" } }, + { N_("DXF file (AutoCAD 2007)"), { "dxf" } }, + { NULL, {} } +}; +// All Importable formats +const FileFilter ImportableFileFilter[] = { + { N_("AutoCAD DXF and DWG files"), { "dxf", "dwg" } }, + { NULL, {} } +}; +// Comma-separated value, like a spreadsheet would use +const FileFilter CsvFileFilter[] = { + { N_("Comma-separated values"), { "csv" } }, + { NULL, {} } +}; + // This table describes the top-level menus in the graphics winodw. enum class Command : uint32_t { NONE = 0,