Internationalize platform-specific code.

pull/106/head
whitequark 2017-01-11 02:44:29 +00:00
parent aeebc3c395
commit c12672be66
9 changed files with 696 additions and 181 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: SolveSpace 3.0\n" "Project-Id-Version: SolveSpace 3.0\n"
"Report-Msgid-Bugs-To: whitequark@whitequark.org\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" "PO-Revision-Date: 2017-01-05 10:30+0000\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: none\n" "Language-Team: none\n"
@ -1398,6 +1398,191 @@ msgstr "click to place bottom left of text"
msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT" msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
msgstr "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 #: style.cpp:161
msgid "" msgid ""
"Can't assign style to an entity that's derived from another entity; try " "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" msgid "Align view to active workplane"
msgstr "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 #: view.cpp:78
msgid "Scale cannot be zero or negative." msgid "Scale cannot be zero or negative."
msgstr "Scale cannot be zero or negative." msgstr "Scale cannot be zero or negative."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: SolveSpace 3.0\n" "Project-Id-Version: SolveSpace 3.0\n"
"Report-Msgid-Bugs-To: whitequark@whitequark.org\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -1212,6 +1212,180 @@ msgstr ""
msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT" msgid "NEW COMMENT -- DOUBLE-CLICK TO EDIT"
msgstr "" 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 #: style.cpp:161
msgid "" msgid ""
"Can't assign style to an entity that's derived from another entity; try assigning a style to this " "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" msgid "Align view to active workplane"
msgstr "" 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 #: view.cpp:78
msgid "Scale cannot be zero or negative." msgid "Scale cannot be zero or negative."
msgstr "" msgstr ""

View File

@ -125,10 +125,14 @@ else()
endforeach() endforeach()
endif() endif()
set(every_platform_SOURCES
platform/w32main.cpp
platform/gtkmain.cpp
platform/cocoamain.mm)
# solvespace library # solvespace library
set(solvespace_core_HEADERS set(solvespace_core_HEADERS
config.h
dsc.h dsc.h
expr.h expr.h
polygon.h polygon.h
@ -212,11 +216,15 @@ if(HAVE_GETTEXT)
set(output_pot ${CMAKE_CURRENT_SOURCE_DIR}/../res/messages.pot) set(output_pot ${CMAKE_CURRENT_SOURCE_DIR}/../res/messages.pot)
set(templ_po ${CMAKE_CURRENT_BINARY_DIR}/messages.po) set(templ_po ${CMAKE_CURRENT_BINARY_DIR}/messages.po)
set(output_po ${CMAKE_CURRENT_SOURCE_DIR}/../res/locales/en_US.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( add_custom_command(
OUTPUT ${output_pot} OUTPUT ${output_pot}
COMMAND ${XGETTEXT} COMMAND ${XGETTEXT}
--language=C++
--keyword --keyword=_ --keyword=N_ --keyword=C_:2,1c --keyword=CN_:2,1c --keyword --keyword=_ --keyword=N_ --keyword=C_:2,1c --keyword=CN_:2,1c
--force-po --width=100 --sort-by-file --force-po --width=100 --sort-by-file
--package-name=SolveSpace --package-name=SolveSpace

View File

@ -13,16 +13,22 @@
using SolveSpace::dbp; using SolveSpace::dbp;
/* Utility functions */
static NSString* Wrap(const std::string &s) {
return [NSString stringWithUTF8String:s.c_str()];
}
/* Settings */ /* Settings */
namespace SolveSpace { namespace SolveSpace {
void CnfFreezeInt(uint32_t val, const std::string &key) { void CnfFreezeInt(uint32_t val, const std::string &key) {
[[NSUserDefaults standardUserDefaults] [[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) { 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]) if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
return [[NSUserDefaults standardUserDefaults] integerForKey:nsKey]; return [[NSUserDefaults standardUserDefaults] integerForKey:nsKey];
return val; return val;
@ -30,11 +36,11 @@ uint32_t CnfThawInt(uint32_t val, const std::string &key) {
void CnfFreezeFloat(float val, const std::string &key) { void CnfFreezeFloat(float val, const std::string &key) {
[[NSUserDefaults standardUserDefaults] [[NSUserDefaults standardUserDefaults]
setFloat:val forKey:[NSString stringWithUTF8String:key.c_str()]]; setFloat:val forKey:Wrap(key)];
} }
float CnfThawFloat(float val, const std::string &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]) if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey])
return [[NSUserDefaults standardUserDefaults] floatForKey:nsKey]; return [[NSUserDefaults standardUserDefaults] floatForKey:nsKey];
return val; return val;
@ -42,12 +48,12 @@ float CnfThawFloat(float val, const std::string &key) {
void CnfFreezeString(const std::string &val, const std::string &key) { void CnfFreezeString(const std::string &val, const std::string &key) {
[[NSUserDefaults standardUserDefaults] [[NSUserDefaults standardUserDefaults]
setObject:[NSString stringWithUTF8String:val.c_str()] setObject:Wrap(val)
forKey:[NSString stringWithUTF8String:key.c_str()]]; forKey:Wrap(key)];
} }
std::string CnfThawString(const std::string &val, const std::string &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]) { if([[NSUserDefaults standardUserDefaults] objectForKey:nsKey]) {
NSString *nsNewVal = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey]; NSString *nsNewVal = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey];
return [nsNewVal UTF8String]; return [nsNewVal UTF8String];
@ -459,45 +465,45 @@ void GetGraphicsWindowSize(int *w, int *h) {
*h = (int)size.height; *h = (int)size.height;
} }
void InvalidateGraphics(void) { void InvalidateGraphics() {
[GWView setNeedsDisplay:YES]; [GWView setNeedsDisplay:YES];
} }
void PaintGraphics(void) { void PaintGraphics() {
[GWView setNeedsDisplay:YES]; [GWView setNeedsDisplay:YES];
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES); CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, YES);
} }
void SetCurrentFilename(const std::string &filename) { void SetCurrentFilename(const std::string &filename) {
if(!filename.empty()) { if(!filename.empty()) {
[GW setTitleWithRepresentedFilename:[NSString stringWithUTF8String:filename.c_str()]]; [GW setTitleWithRepresentedFilename:Wrap(filename)];
} else { } else {
[GW setTitle:@"(new sketch)"]; [GW setTitle:Wrap(C_("title", "(new sketch)"))];
[GW setRepresentedFilename:@""]; [GW setRepresentedFilename:@""];
} }
} }
void ToggleFullScreen(void) { void ToggleFullScreen() {
[GW toggleFullScreen:nil]; [GW toggleFullScreen:nil];
} }
bool FullScreenIsActive(void) { bool FullScreenIsActive() {
return [GWDelegate isFullscreen]; return [GWDelegate isFullscreen];
} }
void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
const std::string &str) { const std::string &str) {
[GWView startEditing:[NSString stringWithUTF8String:str.c_str()] [GWView startEditing:Wrap(str)
at:(NSPoint){(CGFloat)x, (CGFloat)y} at:(NSPoint){(CGFloat)x, (CGFloat)y}
withHeight:fontHeight withHeight:fontHeight
withMinWidthInChars:minWidthChars]; withMinWidthInChars:minWidthChars];
} }
void HideGraphicsEditControl(void) { void HideGraphicsEditControl() {
[GWView stopEditing]; [GWView stopEditing];
} }
bool GraphicsEditControlIsVisible(void) { bool GraphicsEditControlIsVisible() {
return [GWView isEditing]; return [GWView isEditing];
} }
} }
@ -547,13 +553,13 @@ void AddContextMenuItem(const char *label, ContextCommand cmd) {
} }
} }
void CreateContextSubmenu(void) { void CreateContextSubmenu() {
ssassert(!contextSubmenu, "Unexpected nested submenu"); ssassert(!contextSubmenu, "Unexpected nested submenu");
contextSubmenu = [[NSMenu alloc] initWithTitle:@""]; contextSubmenu = [[NSMenu alloc] initWithTitle:@""];
} }
ContextCommand ShowContextMenu(void) { ContextCommand ShowContextMenu() {
if(!contextMenu) if(!contextMenu)
return ContextCommand::CANCELLED; return ContextCommand::CANCELLED;
@ -629,7 +635,7 @@ void InitMainMenu(NSMenu *mainMenu) {
current_level = entry->level; current_level = entry->level;
if(entry->label) { if(entry->label) {
label = [NSString stringWithUTF8String:Translate(entry->label).c_str()]; label = Wrap(Translate(entry->label));
/* OS X does not support mnemonics */ /* OS X does not support mnemonics */
label = [label stringByReplacingOccurrencesOfString:@"&" withString:@""]; label = [label stringByReplacingOccurrencesOfString:@"&" withString:@""];
@ -668,7 +674,7 @@ void InitMainMenu(NSMenu *mainMenu) {
for(auto locale : Locales()) { for(auto locale : Locales()) {
NSMenuItem *localeMenuItem = NSMenuItem *localeMenuItem =
[localeMenu addItemWithTitle: [localeMenu addItemWithTitle:
[NSString stringWithUTF8String:locale.displayName.c_str()] Wrap(locale.displayName)
action:NULL keyEquivalent:@""]; action:NULL keyEquivalent:@""];
[localeMenuItem setTag:(NSInteger)i++]; [localeMenuItem setTag:(NSInteger)i++];
[localeMenuItem setTarget:[MainMenuResponder class]]; [localeMenuItem setTarget:[MainMenuResponder class]];
@ -701,7 +707,7 @@ static void RefreshRecentMenu(SolveSpace::Command cmd, SolveSpace::Command base)
if(std::string(RecentFile[0]).empty()) { if(std::string(RecentFile[0]).empty()) {
NSMenuItem *placeholder = [[NSMenuItem alloc] NSMenuItem *placeholder = [[NSMenuItem alloc]
initWithTitle:@"(no recent files)" action:nil keyEquivalent:@""]; initWithTitle:Wrap(_("(no recent files)")) action:nil keyEquivalent:@""];
[placeholder setEnabled:NO]; [placeholder setEnabled:NO];
[menu addItem:placeholder]; [menu addItem:placeholder];
} else { } else {
@ -710,7 +716,7 @@ static void RefreshRecentMenu(SolveSpace::Command cmd, SolveSpace::Command base)
break; break;
NSMenuItem *item = [[NSMenuItem alloc] NSMenuItem *item = [[NSMenuItem alloc]
initWithTitle:[[NSString stringWithUTF8String:RecentFile[i].c_str()] initWithTitle:[Wrap(RecentFile[i])
stringByAbbreviatingWithTildeInPath] stringByAbbreviatingWithTildeInPath]
action:nil keyEquivalent:@""]; action:nil keyEquivalent:@""];
[item setTag:((uint32_t)base + i)]; [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::OPEN_RECENT, Command::RECENT_OPEN);
RefreshRecentMenu(Command::GROUP_RECENT, Command::RECENT_LINK); RefreshRecentMenu(Command::GROUP_RECENT, Command::RECENT_LINK);
} }
void ToggleMenuBar(void) { void ToggleMenuBar() {
[NSMenu setMenuBarVisible:![NSMenu menuBarVisible]]; [NSMenu setMenuBarVisible:![NSMenu menuBarVisible]];
} }
bool MenuBarIsVisible(void) { bool MenuBarIsVisible() {
return [NSMenu menuBarVisible]; return [NSMenu menuBarVisible];
} }
} }
@ -801,8 +807,8 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension,
desc += *ssPattern; desc += *ssPattern;
} }
} }
std::string title = std::string(ssFilter->name) + " (" + desc + ")"; std::string title = Translate(ssFilter->name) + " (" + desc + ")";
[button addItemWithTitle:[NSString stringWithUTF8String:title.c_str()]]; [button addItemWithTitle:Wrap(title)];
[extensions addObject:[NSString stringWithUTF8String:ssFilter->patterns[0]]]; [extensions addObject:[NSString stringWithUTF8String:ssFilter->patterns[0]]];
} }
[panel setAllowedFileTypes:extensions]; [panel setAllowedFileTypes:extensions];
@ -810,8 +816,7 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension,
int extensionIndex = 0; int extensionIndex = 0;
if(defExtension != "") { if(defExtension != "") {
extensionIndex = [extensions indexOfObject: extensionIndex = [extensions indexOfObject:Wrap(defExtension)];
[NSString stringWithUTF8String:defExtension.c_str()]];
if(extensionIndex == -1) { if(extensionIndex == -1) {
extensionIndex = 0; extensionIndex = 0;
} }
@ -819,14 +824,15 @@ bool SolveSpace::GetSaveFile(std::string *file, const std::string &defExtension,
[button selectItemAtIndex:extensionIndex]; [button selectItemAtIndex:extensionIndex];
if(file->empty()) { if(file->empty()) {
[panel setNameFieldStringValue:[@"untitled" [panel setNameFieldStringValue:
stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; [Wrap(_("untitled"))
stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]];
} else { } else {
[panel setDirectoryURL: [panel setDirectoryURL:
[NSURL fileURLWithPath:[NSString stringWithUTF8String:Dirname(*file).c_str()] [NSURL fileURLWithPath:Wrap(Dirname(*file))
isDirectory:NO]]; isDirectory:NO]];
[panel setNameFieldStringValue: [panel setNameFieldStringValue:
[[NSString stringWithUTF8String:Basename(*file, /*stripExtension=*/true).c_str()] [Wrap(Basename(*file, /*stripExtension=*/true))
stringByAppendingPathExtension:[extensions objectAtIndex:extensionIndex]]]; 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]; NSAlert *alert = [[NSAlert alloc] init];
if(!std::string(SolveSpace::SS.saveFile).empty()) { if(!std::string(SolveSpace::SS.saveFile).empty()) {
[alert setMessageText: [alert setMessageText:
[[@"Do you want to save the changes you made to the sketch “" [[@"Do you want to save the changes you made to the sketch “"
stringByAppendingString: stringByAppendingString:
[[NSString stringWithUTF8String:SolveSpace::SS.saveFile.c_str()] [Wrap(SolveSpace::SS.saveFile)
stringByAbbreviatingWithTildeInPath]] stringByAbbreviatingWithTildeInPath]]
stringByAppendingString:@"”?"]]; stringByAppendingString:@"”?"]];
} else { } 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 setInformativeText:Wrap(_("Your changes will be lost if you don't save them."))];
[alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:Wrap(C_("button", "Save"))];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:Wrap(C_("button", "Cancel"))];
[alert addButtonWithTitle:@"Don't Save"]; [alert addButtonWithTitle:Wrap(C_("button", "Don't Save"))];
switch([alert runModal]) { switch([alert runModal]) {
case NSAlertFirstButtonReturn: case NSAlertFirstButtonReturn:
return DIALOG_YES; 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]; NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText: [alert setMessageText:
@"An autosave file is availible for this project."]; Wrap(_("An autosave file is availible for this project."))];
[alert setInformativeText: [alert setInformativeText:
@"Do you want to load the autosave file instead?"]; Wrap(_("Do you want to load the autosave file instead?"))];
[alert addButtonWithTitle:@"Load"]; [alert addButtonWithTitle:Wrap(C_("button", "Load"))];
[alert addButtonWithTitle:@"Don't Load"]; [alert addButtonWithTitle:Wrap(C_("button", "Don't Load"))];
switch([alert runModal]) { switch([alert runModal]) {
case NSAlertFirstButtonReturn: case NSAlertFirstButtonReturn:
return DIALOG_YES; return DIALOG_YES;
@ -886,16 +893,16 @@ SolveSpace::DialogChoice SolveSpace::LoadAutosaveYesNo(void) {
SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel( SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel(
const std::string &filename, bool canCancel) { const std::string &filename, bool canCancel) {
NSAlert *alert = [[NSAlert alloc] init]; NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String: [alert setMessageText:
("The linked file " + filename + " is not present.").c_str()]]; Wrap("The linked file " + filename + " is not present.")];
[alert setInformativeText: [alert setInformativeText:
@"Do you want to locate it manually?\n" Wrap(_("Do you want to locate it manually?\n"
"If you select \"No\", any geometry that depends on " "If you select “No”, any geometry that depends on "
"the missing file will be removed."]; "the missing file will be removed."))];
[alert addButtonWithTitle:@"Yes"]; [alert addButtonWithTitle:Wrap(C_("button", "Yes"))];
if(canCancel) if(canCancel)
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:Wrap(C_("button", "Cancel"))];
[alert addButtonWithTitle:@"No"]; [alert addButtonWithTitle:Wrap(C_("button", "No"))];
switch([alert runModal]) { switch([alert runModal]) {
case NSAlertFirstButtonReturn: case NSAlertFirstButtonReturn:
return DIALOG_YES; return DIALOG_YES;
@ -1039,7 +1046,6 @@ void InitTextWindow() {
NSUtilityWindowMask)]; NSUtilityWindowMask)];
[[TW standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; [[TW standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[TW standardWindowButton:NSWindowZoomButton] setHidden:YES]; [[TW standardWindowButton:NSWindowZoomButton] setHidden:YES];
[TW setTitle:@"Property Browser"];
[TW setFrameAutosaveName:@"TextWindow"]; [TW setFrameAutosaveName:@"TextWindow"];
[TW setFloatingPanel:YES]; [TW setFloatingPanel:YES];
[TW setBecomesKeyOnlyIfNeeded:YES]; [TW setBecomesKeyOnlyIfNeeded:YES];
@ -1082,7 +1088,7 @@ double GetScreenDpi() {
return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f; return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f;
} }
void InvalidateText(void) { void InvalidateText() {
NSSize size = [TWView convertSizeToBacking:[TWView frame].size]; NSSize size = [TWView convertSizeToBacking:[TWView frame].size];
size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2; size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2;
[TWView setFrameSize:[TWView convertSizeFromBacking:size]]; [TWView setFrameSize:[TWView convertSizeFromBacking:size]];
@ -1098,15 +1104,15 @@ void SetMousePointerToHand(bool is_hand) {
} }
void ShowTextEditControl(int x, int y, const std::string &str) { 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}]; at:(NSPoint){(CGFloat)x, (CGFloat)y}];
} }
void HideTextEditControl(void) { void HideTextEditControl() {
return [TWView stopEditing]; return [TWView stopEditing];
} }
bool TextEditControlIsVisible(void) { bool TextEditControlIsVisible() {
return [TWView isEditing]; return [TWView isEditing];
} }
}; };
@ -1116,10 +1122,10 @@ bool TextEditControlIsVisible(void) {
void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error) { void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error) {
NSAlert *alert = [[NSAlert alloc] init]; NSAlert *alert = [[NSAlert alloc] init];
[alert setAlertStyle:(error ? NSWarningAlertStyle : NSInformationalAlertStyle)]; [alert setAlertStyle:(error ? NSWarningAlertStyle : NSInformationalAlertStyle)];
[alert addButtonWithTitle:@"OK"]; [alert addButtonWithTitle:Wrap(C_("button", "OK"))];
/* do some additional formatting of the message these are /* do some additional formatting of the message;
heuristics, but they are made failsafe and lead to nice results. */ these are heuristics, but they are made failsafe and lead to nice results. */
NSString *input = [NSString stringWithUTF8String:str]; NSString *input = [NSString stringWithUTF8String:str];
NSRange dot = [input rangeOfCharacterFromSet: NSRange dot = [input rangeOfCharacterFromSet:
[NSCharacterSet characterSetWithCharactersInString:@".:"]]; [NSCharacterSet characterSetWithCharactersInString:@".:"]];
@ -1195,10 +1201,14 @@ std::vector<std::string> SolveSpace::GetFontFiles() {
@end @end
void SolveSpace::RefreshLocale() { void SolveSpace::RefreshLocale() {
SS.UpdateWindowTitle();
SolveSpace::InitMainMenu([NSApp mainMenu]); SolveSpace::InitMainMenu([NSApp mainMenu]);
RefreshRecentMenus();
[TW setTitle:Wrap(C_("title", "Property Browser"))];
} }
void SolveSpace::ExitNow(void) { void SolveSpace::ExitNow() {
[NSApp stop:nil]; [NSApp stop:nil];
} }

View File

@ -10,8 +10,6 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <iostream>
#include <json-c/json_object.h> #include <json-c/json_object.h>
#include <json-c/json_util.h> #include <json-c/json_util.h>
@ -53,6 +51,11 @@
#endif #endif
namespace SolveSpace { namespace SolveSpace {
/* Utility functions */
std::string Title(const std::string &s) {
return "SolveSpace - " + s;
}
/* Settings */ /* Settings */
/* Why not just use GSettings? Two reasons. It doesn't allow to easily see /* 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) { void SetCurrentFilename(const std::string &filename) {
if(!filename.empty()) { GW->set_title(Title(filename.empty() ? C_("title", "(new sketch)") : filename.c_str()));
GW->set_title("SolveSpace - " + filename);
} else {
GW->set_title("SolveSpace - (not yet saved)");
}
} }
void ToggleFullScreen(void) { void ToggleFullScreen(void) {
@ -893,13 +892,13 @@ static void RefreshRecentMenu(Command cmd, Command base) {
Gtk::Menu *menu = new Gtk::Menu; Gtk::Menu *menu = new Gtk::Menu;
recent->set_submenu(*menu); recent->set_submenu(*menu);
if(std::string(RecentFile[0]).empty()) { if(RecentFile[0].empty()) {
Gtk::MenuItem *placeholder = new Gtk::MenuItem("(no recent files)"); Gtk::MenuItem *placeholder = new Gtk::MenuItem(_("(no recent files)"));
placeholder->set_sensitive(false); placeholder->set_sensitive(false);
menu->append(*placeholder); menu->append(*placeholder);
} else { } else {
for(size_t i = 0; i < MAX_RECENT; i++) { for(size_t i = 0; i < MAX_RECENT; i++) {
if(std::string(RecentFile[i]).empty()) if(RecentFile[i].empty())
break; break;
RecentMenuItem *item = new RecentMenuItem(RecentFile[i], (uint32_t)base + i); 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) { Gtk::FileChooser *chooser) {
for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) { for(const FileFilter *ssFilter = ssFilters; ssFilter->name; ssFilter++) {
Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create(); Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
filter->set_name(ssFilter->name); filter->set_name(Translate(ssFilter->name));
bool is_active = false; bool is_active = false;
std::string desc = ""; 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, bool GetOpenFile(std::string *filename, const std::string &activeOrEmpty,
const FileFilter filters[]) { const FileFilter filters[]) {
Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Open File"); Gtk::FileChooserDialog chooser(*GW, Title(C_("title", "Open File")));
chooser.set_filename(*filename); chooser.set_filename(*filename);
chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL); chooser.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
chooser.add_button("_Open", Gtk::RESPONSE_OK); chooser.add_button(_("_Open"), Gtk::RESPONSE_OK);
chooser.set_current_folder(CnfThawString("", "FileChooserPath")); chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
ConvertFilters(activeOrEmpty, filters, &chooser); ConvertFilters(activeOrEmpty, filters, &chooser);
@ -998,17 +997,17 @@ static void ChooserFilterChanged(Gtk::FileChooserDialog *chooser)
bool GetSaveFile(std::string *filename, const std::string &defExtension, bool GetSaveFile(std::string *filename, const std::string &defExtension,
const FileFilter filters[]) { const FileFilter filters[]) {
Gtk::FileChooserDialog chooser(*GW, "SolveSpace - Save File", Gtk::FileChooserDialog chooser(*GW, Title(C_("title", "Save File")),
Gtk::FILE_CHOOSER_ACTION_SAVE); Gtk::FILE_CHOOSER_ACTION_SAVE);
chooser.set_do_overwrite_confirmation(true); chooser.set_do_overwrite_confirmation(true);
chooser.add_button("_Cancel", Gtk::RESPONSE_CANCEL); chooser.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
chooser.add_button("_Save", Gtk::RESPONSE_OK); chooser.add_button(C_("button", "_Save"), Gtk::RESPONSE_OK);
std::string activeExtension = ConvertFilters(defExtension, filters, &chooser); std::string activeExtension = ConvertFilters(defExtension, filters, &chooser);
if(filename->empty()) { if(filename->empty()) {
chooser.set_current_folder(CnfThawString("", "FileChooserPath")); chooser.set_current_folder(CnfThawString("", "FileChooserPath"));
chooser.set_current_name("untitled." + activeExtension); chooser.set_current_name(std::string(_("untitled")) + "." + activeExtension);
} else { } else {
chooser.set_current_folder(Dirname(*filename)); chooser.set_current_folder(Dirname(*filename));
chooser.set_current_name(Basename(*filename, /*stripExtension=*/true) + chooser.set_current_name(Basename(*filename, /*stripExtension=*/true) +
@ -1031,14 +1030,14 @@ bool GetSaveFile(std::string *filename, const std::string &defExtension,
DialogChoice SaveFileYesNoCancel(void) { DialogChoice SaveFileYesNoCancel(void) {
Glib::ustring message = Glib::ustring message =
"The file has changed since it was last saved.\n" _("The file has changed since it was last saved.\n\n"
"Do you want to save the changes?"; "Do you want to save the changes?");
Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE, /*is_modal*/ true); Gtk::BUTTONS_NONE, /*is_modal*/ true);
dialog.set_title("SolveSpace - Modified File"); dialog.set_title(Title(C_("title", "Modified File")));
dialog.add_button("_Save", Gtk::RESPONSE_YES); dialog.add_button(C_("button", "_Save"), Gtk::RESPONSE_YES);
dialog.add_button("Do_n't Save", Gtk::RESPONSE_NO); dialog.add_button(C_("button", "Do_n't Save"), Gtk::RESPONSE_NO);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); dialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
switch(dialog.run()) { switch(dialog.run()) {
case Gtk::RESPONSE_YES: case Gtk::RESPONSE_YES:
@ -1055,13 +1054,13 @@ DialogChoice SaveFileYesNoCancel(void) {
DialogChoice LoadAutosaveYesNo(void) { DialogChoice LoadAutosaveYesNo(void) {
Glib::ustring message = Glib::ustring message =
"An autosave file is availible for this project.\n" _("An autosave file is availible for this project.\n\n"
"Do you want to load the autosave file instead?"; "Do you want to load the autosave file instead?");
Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE, /*is_modal*/ true); Gtk::BUTTONS_NONE, /*is_modal*/ true);
dialog.set_title("SolveSpace - Autosave Available"); dialog.set_title(Title(C_("title", "Autosave Available")));
dialog.add_button("_Load autosave", Gtk::RESPONSE_YES); dialog.add_button(C_("button", "_Load autosave"), Gtk::RESPONSE_YES);
dialog.add_button("Do_n't Load", Gtk::RESPONSE_NO); dialog.add_button(C_("button", "Do_n't Load"), Gtk::RESPONSE_NO);
switch(dialog.run()) { switch(dialog.run()) {
case Gtk::RESPONSE_YES: case Gtk::RESPONSE_YES:
@ -1076,17 +1075,17 @@ DialogChoice LoadAutosaveYesNo(void) {
DialogChoice LocateImportedFileYesNoCancel(const std::string &filename, DialogChoice LocateImportedFileYesNoCancel(const std::string &filename,
bool canCancel) { bool canCancel) {
Glib::ustring message = Glib::ustring message =
"The linked file " + filename + " is not present.\n" "The linked file " + filename + " is not present.\n\n"
"Do you want to locate it manually?\n" "Do you want to locate it manually?\n\n"
"If you select \"No\", any geometry that depends on " "If you select \"No\", any geometry that depends on "
"the missing file will be removed."; "the missing file will be removed.";
Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION, Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE, /*is_modal*/ true); Gtk::BUTTONS_NONE, /*is_modal*/ true);
dialog.set_title("SolveSpace - Missing File"); dialog.set_title(Title(C_("title", "Missing File")));
dialog.add_button("_Yes", Gtk::RESPONSE_YES); dialog.add_button(C_("button", "_Yes"), Gtk::RESPONSE_YES);
dialog.add_button("_No", Gtk::RESPONSE_NO); dialog.add_button(C_("button", "_No"), Gtk::RESPONSE_NO);
if(canCancel) if(canCancel)
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); dialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
switch(dialog.run()) { switch(dialog.run()) {
case Gtk::RESPONSE_YES: case Gtk::RESPONSE_YES:
@ -1167,7 +1166,6 @@ public:
set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY); set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
set_skip_taskbar_hint(true); set_skip_taskbar_hint(true);
set_skip_pager_hint(true); set_skip_pager_hint(true);
set_title("SolveSpace - Property Browser");
set_default_size(420, 300); set_default_size(420, 300);
_box.pack_start(_overlay, true, true); _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, Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true,
error ? Gtk::MESSAGE_ERROR : Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, error ? Gtk::MESSAGE_ERROR : Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK,
/*is_modal*/ true); /*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(); dialog.run();
} }
@ -1365,13 +1364,17 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gxevent, GdkEvent *, gpointer)
/* Application lifecycle */ /* Application lifecycle */
void RefreshLocale() { void RefreshLocale() {
SS.UpdateWindowTitle();
for(auto menu : GW->get_menubar().get_children()) { for(auto menu : GW->get_menubar().get_children()) {
GW->get_menubar().remove(*menu); GW->get_menubar().remove(*menu);
} }
InitMainMenu(&GW->get_menubar()); InitMainMenu(&GW->get_menubar());
RefreshRecentMenus();
GW->get_menubar().show_all(); GW->get_menubar().show_all();
GW->get_menubar().accelerate(*GW); GW->get_menubar().accelerate(*GW);
GW->get_menubar().accelerate(*TW); GW->get_menubar().accelerate(*TW);
TW->set_title(Title(C_("title", "Property Browser")));
} }
void ExitNow() { void ExitNow() {
@ -1390,7 +1393,7 @@ int main(int argc, char** argv) {
We set it back to C after all. */ We set it back to C after all. */
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
if(!Glib::get_charset()) { 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; return 1;
} }
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
@ -1441,8 +1444,7 @@ int main(int argc, char** argv) {
if(argc >= 2) { if(argc >= 2) {
if(argc > 2) { if(argc > 2) {
std::cerr << "Only the first file passed on command line will be opened." dbp("Only the first file passed on command line will be opened.");
<< std::endl;
} }
/* Make sure the argument is valid UTF-8. */ /* Make sure the argument is valid UTF-8. */

View File

@ -72,6 +72,13 @@ HFONT FixedFont;
SiHdl SpaceNavigator = SI_NO_HANDLE; SiHdl SpaceNavigator = SI_NO_HANDLE;
#endif #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 // Routines to display message boxes on screen. Do our own, instead of using
// MessageBox, because that is not consistent from version to version and // 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; MessageString = str;
RECT r; RECT r;
GetWindowRect(GraphicsWnd, &r); GetWindowRect(GraphicsWnd, &r);
const char *title = error ? "SolveSpace - Error" : "SolveSpace - Message";
int width = cols*SS.TW.CHAR_WIDTH + 20, int width = cols*SS.TW.CHAR_WIDTH + 20,
height = rows*SS.TW.LINE_HEIGHT + 60; height = rows*SS.TW.LINE_HEIGHT + 60;
MessageWidth = width; MessageWidth = width;
MessageHeight = height; 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, WS_OVERLAPPED | WS_SYSMENU,
r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL); 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, WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
(width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20, (width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20,
70, 25, MessageWnd, NULL, Instance, NULL); 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) { void SolveSpace::SetCurrentFilename(const std::string &filename) {
if(!filename.empty()) { SetWindowTextW(GraphicsWnd,
SetWindowTextW(GraphicsWnd, Widen("SolveSpace - " + filename).c_str()); Title(filename.empty() ? C_("title", "(new sketch)") : filename).c_str());
} else {
SetWindowTextW(GraphicsWnd, L"SolveSpace - (not yet saved)");
}
} }
void SolveSpace::SetMousePointerToHand(bool yes) { void SolveSpace::SetMousePointerToHand(bool yes) {
@ -1146,8 +1150,9 @@ DialogChoice SolveSpace::SaveFileYesNoCancel()
EnableWindow(TextWnd, false); EnableWindow(TextWnd, false);
int r = MessageBoxW(GraphicsWnd, int r = MessageBoxW(GraphicsWnd,
L"The file has changed since it was last saved.\n\n" Widen(_("The file has changed since it was last saved.\n\n"
L"Do you want to save the changes?", L"SolveSpace", "Do you want to save the changes?")).c_str(),
Title(C_("title", "Modified File")).c_str(),
MB_YESNOCANCEL | MB_ICONWARNING); MB_YESNOCANCEL | MB_ICONWARNING);
EnableWindow(TextWnd, true); EnableWindow(TextWnd, true);
@ -1171,8 +1176,9 @@ DialogChoice SolveSpace::LoadAutosaveYesNo()
EnableWindow(TextWnd, false); EnableWindow(TextWnd, false);
int r = MessageBoxW(GraphicsWnd, int r = MessageBoxW(GraphicsWnd,
L"An autosave file is availible for this project.\n\n" Widen(_("An autosave file is availible for this project.\n\n"
L"Do you want to load the autosave file instead?", L"SolveSpace", "Do you want to load the autosave file instead?")).c_str(),
Title(C_("title", "Autosave Available")).c_str(),
MB_YESNO | MB_ICONWARNING); MB_YESNO | MB_ICONWARNING);
EnableWindow(TextWnd, true); EnableWindow(TextWnd, true);
@ -1199,7 +1205,8 @@ DialogChoice SolveSpace::LocateImportedFileYesNoCancel(const std::string &filena
"If you select \"No\", any geometry that depends on " "If you select \"No\", any geometry that depends on "
"the missing file will be removed."; "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); (canCancel ? MB_YESNOCANCEL : MB_YESNO) | MB_ICONWARNING);
EnableWindow(TextWnd, true); EnableWindow(TextWnd, true);
@ -1282,7 +1289,7 @@ static void DoRecent(HMENU m, Command base)
c++; 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() void SolveSpace::RefreshRecentMenus()
{ {
@ -1363,7 +1370,7 @@ static void CreateMainWindows()
ssassert(RegisterClassEx(&wc), "Cannot register window class"); ssassert(RegisterClassEx(&wc), "Cannot register window class");
GraphicsWnd = CreateWindowExW(0, L"GraphicsWnd", 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_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS, WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS,
50, 50, 900, 600, NULL, NULL, Instance, NULL); 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 // We get the desired Alt+Tab behaviour by specifying that the text
// window is a child of the graphics window. // window is a child of the graphics window.
TextWnd = CreateWindowExW(0, TextWnd = CreateWindowExW(0, L"TextWnd",
L"TextWnd", L"SolveSpace - Property Browser", WS_THICKFRAME | WS_CLIPCHILDREN, Title(C_("title", "Property Browser")).c_str(),
WS_THICKFRAME | WS_CLIPCHILDREN,
650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL); 650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL);
ssassert(TextWnd != NULL, "Cannot create window"); ssassert(TextWnd != NULL, "Cannot create window");
@ -1415,11 +1423,16 @@ static void CreateMainWindows()
} }
void SolveSpace::RefreshLocale() { void SolveSpace::RefreshLocale() {
SS.UpdateWindowTitle();
HMENU oldMenu = GetMenu(GraphicsWnd); HMENU oldMenu = GetMenu(GraphicsWnd);
SetMenu(GraphicsWnd, CreateGraphicsWindowMenus()); SetMenu(GraphicsWnd, CreateGraphicsWindowMenus());
if(oldMenu != NULL) { if(oldMenu != NULL) {
DestroyMenu(oldMenu); DestroyMenu(oldMenu);
} }
RefreshRecentMenus();
SetWindowTextW(TextWnd, Title(C_("title", "Property Browser")).c_str());
} }
#ifdef HAVE_SPACEWARE #ifdef HAVE_SPACEWARE

View File

@ -1088,14 +1088,14 @@ PluralExpr::Token PluralExpr::Lex() {
if(reader.TryChar('=')) { if(reader.TryChar('=')) {
t.op = Token::Op::LE; t.op = Token::Op::LE;
} else { } else {
t.op = Token::Op::LT; t.op = Token::Op::LT;
} }
} else if(reader.TryChar('>')) { } else if(reader.TryChar('>')) {
t.type = Token::Type::BINARY_OP; t.type = Token::Type::BINARY_OP;
if(reader.TryChar('=')) { if(reader.TryChar('=')) {
t.op = Token::Op::GE; t.op = Token::Op::GE;
} else { } else {
t.op = Token::Op::GT; t.op = Token::Op::GT;
} }
} else if(reader.TryChar('!')) { } else if(reader.TryChar('!')) {
reader.ExpectChar('='); reader.ExpectChar('=');

View File

@ -169,67 +169,13 @@ DialogChoice LocateImportedFileYesNoCancel(const std::string &filename,
#define AUTOSAVE_SUFFIX "~" #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 { enum class Unit : uint32_t {
MM = 0, MM = 0,
INCHES INCHES
}; };
struct FileFilter;
bool GetSaveFile(std::string *filename, const std::string &defExtension, bool GetSaveFile(std::string *filename, const std::string &defExtension,
const FileFilter filters[]); const FileFilter filters[]);
bool GetOpenFile(std::string *filename, const std::string &defExtension, bool GetOpenFile(std::string *filename, const std::string &defExtension,

View File

@ -58,6 +58,63 @@ inline const char *C_(const char *msgctxt, const char *msgid) {
} }
#endif #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. // This table describes the top-level menus in the graphics winodw.
enum class Command : uint32_t { enum class Command : uint32_t {
NONE = 0, NONE = 0,