/* * DuneApp.cpp * * Copyright (C) 1999 Stephen F. White * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include #ifdef WIN32 # include #endif #include "stdafx.h" #include "swt.h" #include "DuneApp.h" #include "Scene.h" #include "Path.h" #include "MainWindow.h" #include "URL.h" #include "EulerAngles.h" #include "x3dtranslators.h" #include "resource.h" #include "xerrorhandler.h" DuneApp *TheApp = NULL; void returntracker(void) { TheApp->ReturnTracker(); } #ifdef HAVE_NEW_HANDLER #include static void dune_new_handler(void) { TheApp->emergency_rescue(); exit(1); } #endif #define MAX_RECENT_FILES 4 DuneApp::DuneApp() : PreferencesApp(), StereoViewApp(), InputDeviceApp(), EcmaScriptApp() { TheApp = this; swSetDefaultIcon(IDI_DUNE_ICON); _mainWnd = NULL; _browser = swBrowserInit(getPrefs()); _upload = swUploadInit(getPrefs()); _helpBrowser = swHelpBrowserInit(getPrefs()); _textedit = swTexteditInit(getPrefs()); StereoViewLoadPreferences(); EcmaScriptLoadPreferences(); OutputLoadPreferences(); int n = GetIntPreference("RecentFiles", 0); for (int i = 0; i < n; i++) { char buf[1024]; const char *filename; sprintf(buf, "RecentFile%d", i); filename = TheApp->GetPreference(buf, ""); _recentFiles.append(filename); } _clipboardNode = NULL; swSetCleanup(returntracker); #ifndef _WIN32 init_handlers(); #endif _currentWindow = NULL; #ifdef HAVE_NEW_HANDLER set_new_handler(dune_new_handler); #endif _keyProtoFile = "ProtoFile"; _keyProtoCategory = "ProtoCategory"; _is4Kids = false; _dontCareFocus = false; _numberLoadedInlines = 0; } void DuneApp::SaveRecentFileList() { int i, n = _recentFiles.size(); SetIntPreference("RecentFiles", n); for (i = 0; i < n; i++) { char buf[1024]; sprintf(buf, "RecentFile%d", i); SetPreference(buf, _recentFiles[i]); } } void DuneApp::PrintMessageWindows(const char *text) { List::Iterator *i; for (i = _windows.first(); i; i = i->next()) { i->item()->setStatusText(text); } } void DuneApp::newMainWnd(SWND &mainWnd) { int width = GetIntPreference("WindowWidth", 800); int height = GetIntPreference("WindowHeight", 600); // sanity check if ((width <= 0) || (height <= 0)) { width = 800; height = 600; } mainWnd = swCreateMainWindow("Dune", 0, 0, width, height); } void DuneApp::OnFileNew() { #ifndef HAVE_OPEN_IN_NEW_WINDOW if (!TheApp->checkSaveOldWindow()) return; #endif Scene *scene = new Scene(); newMainWnd(_mainWnd); MainWindow *window = new MainWindow(scene, _mainWnd); _windows.append(window); #ifndef HAVE_OPEN_IN_NEW_WINDOW TheApp->deleteOldWindow(); #endif } void DuneApp::OnFileNewWindow() { Scene *scene = new Scene(); newMainWnd(_mainWnd); MainWindow *window = new MainWindow(scene, _mainWnd); _windows.append(window); } /* * temporary files are written into the current directory * temporary files are written into a list and deleted at program end */ char * DuneApp::SaveTempFile(Scene *scene, char *name, bool pureVRML97) { static char path[1024]; mystrncpy_secure(path,scene->getPath(),1024); int i=0; // produce a absolute path (remote netscape commands need one) #ifdef WIN32 char* dirsign="\\"; if (path[1] != ':') if (getcwd(path, 1023)!=NULL) { #else char* dirsign="/"; if (path[0] != '/') if (getcwd(path, 1023)!=NULL) { #endif mystrncpy_secure(path+strlen(path),dirsign,1024-strlen(path)); mystrncpy_secure(path+strlen(path),scene->getPath(), 1024-strlen(path)); } // strip filename for (i=strlen(path);(i>=0) && (path[i]!=dirsign[0]);i--); i++; swGetTempFile(path+i, name, ".wrl", 1024-i); int f = open(path, O_WRONLY | O_CREAT,00666); if (f == -1) { swGetTempPath(path, name, ".wrl", 1024); f = open(path, O_WRONLY | O_CREAT,00666); if (f == -1) { char errorstring1[256]; swLoadString(IDS_SAVE_PREVIEW_ERROR1, errorstring1, 255); char errorstring2[256]; swLoadString(IDS_SAVE_PREVIEW_ERROR2, errorstring2, 255); char msg[1024]; mysnprintf(msg,1023,errorstring1, path, strerror(errno), errorstring2); swMessageBox(_mainWnd, msg, "temporary save", SW_MB_OK, SW_MB_WARNING); return ""; } } URL fileURL; fileURL.FromPath(path); if (scene->write(f, fileURL, true, pureVRML97)<0) { char msg[1024]; mysnprintf(msg,1023,"%s: %s", path, strerror(errno)); swMessageBox(_mainWnd, msg, "temporary save", SW_MB_OK, SW_MB_WARNING); } #ifndef _WIN32 ftruncate(f,lseek(f,0,SEEK_CUR)); #else _chsize(f, tell(f)); #endif close(f); AddToFilesToDelete(path); return path; } void DuneApp::OnFilePreview(Scene* scene) { #ifndef WIN32 if (!swBrowserGetUseFork(TheApp->GetBrowser())) swIconifyWindow(_mainWnd); #endif #ifdef HAVE_AFLOCK stopTrackers(); #endif char *temppath = SaveTempFile(scene,".dune_preview", swBrowserGetPureVRML97(TheApp->GetBrowser()) == 0 ? false : true); if (strlen(temppath) != 0) swBrowserPreview(GetBrowser(), temppath, _mainWnd); #ifdef HAVE_AFLOCK restartTrackers(); #endif #ifndef WIN32 if (!swBrowserGetUseFork(TheApp->GetBrowser())) { swDeIconifyWindow(_mainWnd); swInvalidateWindow(_mainWnd); } #endif } void DuneApp::OnFileUpload(Scene* scene) { char *temppath = SaveTempFile(scene,".dune_upload", swBrowserGetPureVRML97(TheApp->GetBrowser()) == 0 ? false : true); char *htmlpath = ""; if (strlen(temppath) != 0) { htmlpath = swUpload(_upload, temppath, _helpBrowser, _mainWnd); } if (strlen(htmlpath) != 0) AddToFilesToDelete(htmlpath); } #ifndef HAVE_OPEN_IN_NEW_WINDOW // save VRML files (with .wrl or .txt extension) into a list of temporaery // files and hide the windows (to call a editor next) // the file of the currentWindow is first stored in the list bool DuneApp::saveTempFiles(MainWindow *currentWindow, int useExtensionTxt) { _tempFiles.Init(); int count = 0; List::Iterator *wIter; for (wIter = _windows.first(); wIter != NULL; wIter = wIter ->next()) { Scene *scene = wIter->item()->GetScene(); URL oldURL = scene->getURL(); const char* oldPath = strdup(scene->getPath()); char *filename = (char *)malloc(1024); mysnprintf(filename,1024,"%s_%d",".dune_textedit",count++); char *savefile = (char *)malloc(1024); if (!useExtensionTxt) swGetTempFile(savefile, filename, ".wrl", 1024); else swGetTempFile(savefile, filename, ".txt", 1024); // is file writable ? int f = open(savefile, O_WRONLY | O_CREAT,00666); if (f == -1) { if (!useExtensionTxt) swGetTempPath(savefile,filename,".wrl",1024); else swGetTempPath(savefile,filename,".txt",1024); f = open(savefile, O_WRONLY | O_CREAT,00666); if (f == -1) { char errorstring1[256]; swLoadString(IDS_SAVE_EDIT_ERROR1, errorstring1, 255); char errorstring2[256]; swLoadString(IDS_SAVE_EDIT_ERROR2, errorstring2, 255); char msg[1024]; mysnprintf(msg,1023,errorstring1, savefile, strerror(errno), "save to a writeable directory first"); swMessageBox(_mainWnd, msg, "temporary save", SW_MB_OK, SW_MB_WARNING); return false; } } AddToFilesToDelete(savefile); close(f); free(filename); URL url; url.FromPath(savefile); if (wIter->item() == currentWindow) initSelectionLinenumber(); if (wIter->item()->SaveFile(savefile, url)) { if ((currentWindow != NULL) && (wIter->item() == currentWindow)) _tempFiles.insert(new FileBackup(savefile, oldURL, oldPath)); else _tempFiles.append(new FileBackup(savefile, oldURL, oldPath)); // hide and remove window swHideWindow(wIter->item()->getParentWindow()); swUpdate(); } else { // save failed, need to restore... return false; } } // deleting from a shrinking List is not trivial... wIter = _windows.first(); while (wIter != NULL) { MainWindow * windowToDelete = wIter->item(); wIter = wIter ->next(); _windows.remove(_windows.find(windowToDelete)); delete windowToDelete; } return true; } static bool checkLinenumberOption(const char *linenumberOption) { bool hasLinenumberOption = (linenumberOption[0] != 0); // check if linenumber option only contain blanks if (hasLinenumberOption) { hasLinenumberOption = false; for (int i = 0; i < strlen(linenumberOption); i++) if (linenumberOption[i] != ' ') hasLinenumberOption = true; } return hasLinenumberOption; } void DuneApp::restoreTempFiles(void) { List::Iterator *fIter = _tempFiles.first(); for (int i = 0; i < _tempFiles.size(); i++) { Scene* scene = new Scene(); bool error = false; do { newMainWnd(_mainWnd); if (ImportFile(fIter->item()->_backupFile, scene)) { scene->setExtraModifiedFlag(); scene->setURL(fIter->item()->_url); scene->setPath(fIter->item()->_path); MainWindow *newwindow = new MainWindow(scene, _mainWnd); _windows.append(newwindow); error = false; } else { error = true; const char *texteditCommand; const char *texteditLinenumberOption; int texteditUseExtensionTxt; int texteditAllowPopup; swTexteditGetSettings(_textedit, &texteditCommand, &texteditLinenumberOption, &texteditUseExtensionTxt, &texteditAllowPopup); MyString command = strdup(texteditCommand); if (checkLinenumberOption(texteditLinenumberOption)) { command += " "; command += strdup(texteditLinenumberOption); char number[1024]; mysnprintf(number, 1023, "%d", scene->getErrorLineNumber()); command += number; } command += " "; command += fIter->item()->_backupFile; swHideWindow(_mainWnd); swUpdate(); // swDestroyWindow(_mainWnd); delete scene; scene = new Scene(); system((const char*)command); } } while (error); free(fIter->item()->_backupFile); free(fIter->item()->_path); fIter = fIter->next(); } } void DuneApp::OnFileEdit(MainWindow *window, Scene* oldScene, char* filename) { const char *texteditCommand; const char *texteditLinenumberOption; int texteditUseExtensionTxt; int texteditAllowPopup; swTexteditGetSettings(_textedit, &texteditCommand, &texteditLinenumberOption, &texteditUseExtensionTxt, &texteditAllowPopup); bool saveSuccess = saveTempFiles(window, texteditUseExtensionTxt); MyString command = strdup(texteditCommand); if (checkLinenumberOption(texteditLinenumberOption)) { command += " "; command += strdup(texteditLinenumberOption); char number[1024]; mysnprintf(number, 1023, "%d", getSelectionLinenumber()); command += number; } SavePreferences(); int i; if (filename == NULL) { List::Iterator *fIter = _tempFiles.first(); for (i = 0; i < _tempFiles.size(); i++) { command += " "; command += fIter->item()->_backupFile; fIter = fIter->next(); } } else { command += " "; command += filename; } bool commandFailed = false; swHideWindow(_mainWnd); swUpdate(); if (saveSuccess) if (system((const char*)command) != 0) commandFailed = true; newMainWnd(_mainWnd); if (!saveSuccess) { char msg[1024]; mysnprintf(msg,1023,"%s: %s", "save failed ", strerror(errno)); swMessageBox(_mainWnd, msg, "Error: save ", SW_MB_OK, SW_MB_WARNING); } if (commandFailed) { char errorstring[256]; swLoadString(IDS_EDIT_COMMAND_ERROR, errorstring, 255); swMessageBox(_mainWnd, errorstring, "Error: textedit", SW_MB_OK, SW_MB_WARNING); } swDestroyWindow(_mainWnd); restoreTempFiles(); } #endif bool DuneApp::ImportFile(const char *openpath, Scene* scene, bool protoLibrary) { FILE *file; char path1[1024]; char path2[1024]; char* filepath; mystrncpy_secure(path1,openpath,1024); filepath=swKillFileQuotes(path1); #ifdef READ_XML if (swIsXML(filepath)) { swGetTempPath(path2,"from_xml",".wrl",1024); if (!x3d2vrml(path2,(char*)filepath)) return false; } else #endif mystrncpy_secure(path2,filepath,1024); if (!path2[0] || !(file = fopen(path2, "r"))) { return false; } URL importURL; importURL.FromPath(openpath); setImportURL(importURL); scene->saveProtoStatus(); int undoStackTopBeforeParse = scene->getUndoStackTop(); const char *errors = scene->parse(file, protoLibrary); fclose(file); importURL=scene->getURL(); scene->setSelection(scene->getRoot()); scene->UpdateViews(NULL, UPDATE_SELECTION); scene->UpdateViews(NULL, UPDATE_ALL, NULL); if (errors[0]) { swMessageBox(_mainWnd, errors, "Parse Errors", SW_MB_OK, SW_MB_WARNING); swDebugf(errors); // delete so far successfull imported nodes on errors while ((scene->getUndoStackTop() > undoStackTopBeforeParse) && scene->canUndo()) scene->undo(); scene->restoreProtoStatus(); return false; } return true; } bool DuneApp::OpenFile(const char *openpath) { URL url; url.FromPath(openpath); Scene *scene = new Scene(); scene->setURL(url); scene->setPath(openpath); if (!ImportFile(openpath, scene)) { delete scene; return false; } scene->setSelection(scene->getRoot()); scene->UpdateViews(NULL, UPDATE_SELECTION); scene->UpdateViews(NULL, UPDATE_ALL); AddToRecentFiles(openpath); // create a new window for our new scene newMainWnd(_mainWnd); MainWindow *window = new MainWindow(scene, _mainWnd); _windows.append(window); return true; } void DuneApp::OnFileClose(MainWindow *window) { if (!window->SaveModified()) { return; } _windows.remove(_windows.find(window)); // MacOSX X11 can freeze here. Bug in X11 shutdown code ? #ifndef MACOSX delete window; #endif if (_windows.size() == 0) { for (int j=0;j<_filesToDelete.size();j++) swRemoveFile(*_filesToDelete[j]); swUploadCleanupPasswd(_upload); Exit(); } } void DuneApp::OnFileExit() { List::Iterator *i; for (i = _windows.first(); i; i = i->next()) { if (!i->item()->SaveModified()) return; } for (i = _windows.first(); i; i = i->next()) { delete i->item(); } for (int j=0;j<_filesToDelete.size();j++) swRemoveFile(*_filesToDelete[j]); swUploadCleanupPasswd(_upload); Exit(); } void DuneApp::Exit() { SaveRecentFileList(); SavePreferences(); swBrowserShutdown(_browser); // do not delete _prefs, cause there may be a crash in swExit 8-( // swDeletePreferences(_prefs); swExit(); } void DuneApp::UpdateAllWindows() { for (List::Iterator *i = _windows.first(); i; i = i->next()) { i->item()->GetScene()->UpdateViews(NULL, UPDATE_ALL); } } int DuneApp::GetNumRecentFiles() const { return _recentFiles.size(); } const MyString & DuneApp::GetRecentFile(int index) const { return _recentFiles[index]; } void DuneApp::AddToRecentFiles(const MyString &filename) { int i = _recentFiles.find(filename); if (i >= 0) { _recentFiles.remove(i); } int n = MIN(_recentFiles.size(), MAX_RECENT_FILES-1); for (i = n; i > 0; i--) { _recentFiles[i] = _recentFiles[i-1]; } _recentFiles[0] = filename; } void DuneApp::AddToFilesToDelete(char* string) { // test if string is already in list for (int i=0;i<_filesToDelete.size();i++) if (strcmp(string,(char*) _filesToDelete[i])==0) return; _filesToDelete.append(new MyString(string)); } void DuneApp::initSelectionLinenumber(void) { _selectionLinenumberFlag = false; _selectionLinenumber = 1; } void DuneApp::checkSelectionLinenumberCounting(Scene* scene, Node* node) { if (scene->getSelection()->getNode() == node) _selectionLinenumberFlag = true; } void DuneApp::incSelectionLinenumber(int increment) { if (!_selectionLinenumberFlag) _selectionLinenumber += increment; } int DuneApp::getSelectionLinenumber(void) { if (_selectionLinenumberFlag) return _selectionLinenumber; else return 1; } void DuneApp::reOpenWindow(Scene* scene) { #ifndef HAVE_OPEN_IN_NEW_WINDOW newMainWnd(_mainWnd); MainWindow *window = new MainWindow(scene, _mainWnd); _windows.append(window); TheApp->deleteOldWindow(); #endif } bool DuneApp::checkSaveOldWindow(void) { // give user a chance to save first List::Iterator *curWinItr = _windows.last(); _currentWindow = curWinItr->item(); if (curWinItr != NULL) { if (curWinItr->item() != NULL) if (!curWinItr->item()->SaveModified()) return false; SavePreferences(); } return true; } void DuneApp::deleteOldWindow(void) { // delete the old window List::Iterator *curWinItr = NULL; for (List::Iterator *WinItr = _windows.first(); WinItr; WinItr = WinItr->next()) if (WinItr->item() == _currentWindow) { curWinItr = WinItr; _currentWindow = NULL; break; } if (curWinItr != NULL) { if (curWinItr->item() != NULL) delete curWinItr->item(); _windows.remove(curWinItr); } } bool DuneApp::hasUpload(void) { return swHasUpload(_upload); } void DuneApp::addToProtoLibrary(char* category, char* protoFile) { // search for a free key for (int i=1; i<1000; i++) { char key[1024]; mysnprintf(key, 1023, "%s%d", _keyProtoFile, i); const char *value = GetPreference(key, ""); if (strlen(value) == 0) { SetPreference(key, protoFile); mysnprintf(key, 1023, "%s%d", _keyProtoCategory, i); SetPreference(key,category); break; } } } bool DuneApp::readProtoLibrary(Scene* scene) { for (int i=1; i<1000; i++) { char key[1024]; mysnprintf(key, 1023, "%s%d", _keyProtoFile, i); const char *value = GetPreference(key, ""); if (strlen(value) == 0) { if (!ImportFile(value, scene)) return false; } } return true; } bool DuneApp::loadNewInline() { if (_numberLoadedInlines > GetMaxInlinesToLoad()) return false; _numberLoadedInlines++; return true; } FileBackup::FileBackup(char* backupFile, URL url, const char* path) { _backupFile = backupFile; _url = url; _path = (char*) path; }