diff --git a/draw.cpp b/draw.cpp index 9dc8840..87f684c 100644 --- a/draw.cpp +++ b/draw.cpp @@ -900,6 +900,12 @@ void GraphicsWindow::Paint(int w, int h) { glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Nasty case when we're reloading the imported files; could be that + // we get an error, so a dialog pops up, and a message loop starts, and + // we have to get called to paint ourselves. If the sketch is screwed + // up, then we could trigger an oops trying to draw. + if(!SS.allConsistent) return; + // Let's use two lights, at the user-specified locations GLfloat f; glEnable(GL_LIGHT0); diff --git a/file.cpp b/file.cpp index d35248a..c3106a8 100644 --- a/file.cpp +++ b/file.cpp @@ -86,6 +86,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'g', "Group.visible", 'b', &(SS.sv.g.visible) }, { 'g', "Group.remap", 'M', &(SS.sv.g.remap) }, { 'g', "Group.impFile", 'P', &(SS.sv.g.impFile) }, + { 'g', "Group.impFileRel", 'P', &(SS.sv.g.impFileRel) }, { 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) }, { 'p', "Param.val", 'f', &(SS.sv.p.val) }, @@ -284,6 +285,8 @@ void SolveSpace::LoadUsingTable(char *key, char *val) { } bool SolveSpace::LoadFromFile(char *filename) { + allConsistent = false; + fh = fopen(filename, "rb"); if(!fh) { Error("Couldn't read from file '%s'", filename); @@ -398,6 +401,8 @@ bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le, SMesh *m) { } void SolveSpace::ReloadAllImported(void) { + allConsistent = false; + int i; for(i = 0; i < group.n; i++) { Group *g = &(group.elem[i]); @@ -405,7 +410,37 @@ void SolveSpace::ReloadAllImported(void) { g->impEntity.Clear(); g->impMesh.Clear(); - if(!LoadEntitiesFromFile(g->impFile, &(g->impEntity), &(g->impMesh))) { + + FILE *test = fopen(g->impFile, "rb"); + if(test) { + fclose(test); // okay, exists + } else { + // It doesn't exist. Perhaps the entire tree has moved, and we + // can use the relative filename to get us back. + if(SS.saveFile[0]) { + char fromRel[MAX_PATH]; + strcpy(fromRel, g->impFileRel); + MakePathAbsolute(SS.saveFile, fromRel); + test = fopen(fromRel, "rb"); + if(test) { + fclose(test); + // It worked, this is our new absolute path + strcpy(g->impFile, fromRel); + } + } + } + + if(LoadEntitiesFromFile(g->impFile, &(g->impEntity), &(g->impMesh))) { + if(SS.saveFile[0]) { + // Record the imported file's name relative to our filename; + // if the entire tree moves, then everything will still work + strcpy(g->impFileRel, g->impFile); + MakePathRelative(SS.saveFile, g->impFileRel); + } else { + // We're not yet saved, so can't make it absolute + strcpy(g->impFileRel, g->impFile); + } + } else { Error("Failed to load imported file '%s'", g->impFile); } } diff --git a/sketch.h b/sketch.h index c480365..49e364b 100644 --- a/sketch.h +++ b/sketch.h @@ -157,6 +157,7 @@ public: int remapCache[REMAP_PRIME]; char impFile[MAX_PATH]; + char impFileRel[MAX_PATH]; SMesh impMesh; EntityList impEntity; diff --git a/solvespace.cpp b/solvespace.cpp index ce4006a..9848b01 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -399,6 +399,8 @@ void SolveSpace::GenerateAll(int first, int last) { deleted.groups, deleted.groups == 1 ? "" : "s"); memset(&deleted, 0, sizeof(deleted)); } + + allConsistent = true; return; pruned: diff --git a/solvespace.h b/solvespace.h index fc54385..5221661 100644 --- a/solvespace.h +++ b/solvespace.h @@ -58,6 +58,7 @@ int SaveFileYesNoCancel(void); #define PNG_EXT "png" BOOL GetSaveFile(char *file, char *defExtension, char *selPattern); BOOL GetOpenFile(char *file, char *defExtension, char *selPattern); +void GetAbsoluteFilename(char *file); void CheckMenuById(int id, BOOL checked); void EnableMenuById(int id, BOOL checked); @@ -142,6 +143,8 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a21, double a22, double a23, double a24, double a31, double a32, double a33, double a34, double a41, double a42, double a43, double a44); +void MakePathRelative(char *base, char *path); +void MakePathAbsolute(char *base, char *path); class System { public: @@ -336,6 +339,11 @@ public: // The system to be solved. System sys; + // Everything has been pruned, so we know there's no dangling references + // to entities that don't exist. Before that, we mustn't try to display + // the sketch! + bool allConsistent; + struct { bool showTW; bool generateAll; diff --git a/textscreens.cpp b/textscreens.cpp index b296c7b..2b153e3 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -383,7 +383,7 @@ void TextWindow::ShowGroupInfo(void) { } if(g->type == Group::IMPORTED) { - Printf(true, "%FtIMPORT%E '%s'", g->impFile); + Printf(true, "%FtIMPORT%E '%s'", g->impFileRel); } if(g->type == Group::EXTRUDE || diff --git a/util.cpp b/util.cpp index 3afa780..eccfbd9 100644 --- a/util.cpp +++ b/util.cpp @@ -1,5 +1,75 @@ #include "solvespace.h" +void MakePathRelative(char *basep, char *pathp) +{ + int i; + char *p; + char base[MAX_PATH], path[MAX_PATH], out[MAX_PATH]; + + // Convert everything to lowercase + p = basep; + for(i = 0; *p; p++) { + base[i++] = tolower(*p); + } + base[i++] = '\0'; + p = pathp; + for(i = 0; *p; p++) { + path[i++] = tolower(*p); + } + path[i++] = '\0'; + + // Find the length of the common prefix + int com; + for(com = 0; base[com] && path[com]; com++) { + if(base[com] != path[com]) break; + } + if(!(base[com] && path[com])) return; // weird, prefix is entire string + if(com == 0) return; // maybe on different drive letters? + + int sections = 0; + int secLen = 0, secStart = 0; + for(i = com; base[i]; i++) { + if(base[i] == '/' || base[i] == '\\') { + if(secLen == 2 && memcmp(base+secStart, "..", 2)==0) return; + if(secLen == 1 && memcmp(base+secStart, ".", 1)==0) return; + + sections++; + secLen = 0; + secStart = i+1; + } else { + secLen++; + } + } + + // For every directory in the prefix of the base, we must go down a + // directory in the relative path name + strcpy(out, ""); + for(i = 0; i < sections; i++) { + strcat(out, "../"); + } + strcat(out, path+com); + + strcpy(pathp, out); +} + +void MakePathAbsolute(char *basep, char *pathp) { + char out[MAX_PATH]; + strcpy(out, basep); + + // Chop off the filename + int i; + for(i = strlen(out) - 1; i >= 0; i--) { + if(out[i] == '\\' || out[i] == '/') break; + } + if(i < 0) return; // base is not an absolute path, or something? + out[i+1] = '\0'; + + strcat(out, pathp); + GetAbsoluteFilename(out); + + strcpy(pathp, out); +} + void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a21, double a22, double a23, double a24, double a31, double a32, double a33, double a34, diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 21344c6..57120ff 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -63,8 +63,15 @@ void Error(char *str, ...) vsprintf(buf, str, f); va_end(f); + EnableWindow(GraphicsWnd, FALSE); + EnableWindow(TextWnd, FALSE); + HWND h = GetForegroundWindow(); MessageBox(h, buf, "SolveSpace Error", MB_OK | MB_ICONERROR); + + EnableWindow(TextWnd, TRUE); + EnableWindow(GraphicsWnd, TRUE); + SetForegroundWindow(GraphicsWnd); } void ExitNow(void) { @@ -696,9 +703,14 @@ BOOL GetOpenFile(char *file, char *defExtension, char *selPattern) ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; EnableWindow(GraphicsWnd, FALSE); + EnableWindow(TextWnd, FALSE); + BOOL r = GetOpenFileName(&ofn); + + EnableWindow(TextWnd, TRUE); EnableWindow(GraphicsWnd, TRUE); SetForegroundWindow(GraphicsWnd); + return r; } BOOL GetSaveFile(char *file, char *defExtension, char *selPattern) @@ -716,19 +728,38 @@ BOOL GetSaveFile(char *file, char *defExtension, char *selPattern) ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; EnableWindow(GraphicsWnd, FALSE); + EnableWindow(TextWnd, FALSE); + BOOL r = GetSaveFileName(&ofn); + + EnableWindow(TextWnd, TRUE); EnableWindow(GraphicsWnd, TRUE); SetForegroundWindow(GraphicsWnd); + return r; } int SaveFileYesNoCancel(void) { - return MessageBox(GraphicsWnd, + EnableWindow(GraphicsWnd, FALSE); + EnableWindow(TextWnd, FALSE); + + int r = MessageBox(GraphicsWnd, "The program has changed since it was last saved.\r\n\r\n" "Do you want to save the changes?", "SolveSpace", MB_YESNOCANCEL | MB_ICONWARNING); -} + EnableWindow(TextWnd, TRUE); + EnableWindow(GraphicsWnd, TRUE); + SetForegroundWindow(GraphicsWnd); + + return r; +} +void GetAbsoluteFilename(char *file) +{ + char absoluteFile[MAX_PATH]; + GetFullPathName(file, sizeof(absoluteFile), absoluteFile, NULL); + strcpy(file, absoluteFile); +} static void MenuById(int id, BOOL yes, BOOL check) { @@ -933,13 +964,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, s = strrchr(file, '"'); if(s) *s = '\0'; } - char absoluteFile[MAX_PATH] = ""; if(*file != '\0') { - GetFullPathName(file, sizeof(absoluteFile), absoluteFile, NULL); + GetAbsoluteFilename(file); } // Call in to the platform-independent code, and let them do their init - SS.Init(absoluteFile); + SS.Init(file); ShowWindow(TextWnd, SW_SHOWNOACTIVATE); ShowWindow(GraphicsWnd, SW_SHOW); diff --git a/wishlist.txt b/wishlist.txt index 75f05ec..5618416 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -5,7 +5,6 @@ DXF export TTF font text some kind of rounding / chamfer remove back button in browser? -relative paths for import auto-generate circles and faces when lathing copy the section geometry to other end when sweeping cylindrical faces