Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

DuneApp.cpp

/*
 * 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 <errno.h>
#ifdef WIN32
# include <direct.h>
#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 <new.h>

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<MainWindow *>::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<MainWindow *>::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<FileBackup *>::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<FileBackup *>::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("%s", 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<MainWindow *>::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<MainWindow *>::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<MainWindow *>::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<MainWindow *>::Iterator *curWinItr = NULL;
    for (List<MainWindow *>::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;
}


Generated by  Doxygen 1.6.0   Back to index