/* Copyright (C) 1996,1997,1998,1999 by Salvador E. Tropea (SET),
   see copyrigh file for details */
#include <ceditint.h>
#define Uses_TCEditWindow
#define Uses_TCEditor_Commands
#define Uses_TCEditor_Internal
#define Uses_TApplication
#define Uses_TWindow
#define Uses_TPalette
#define Uses_TDeskTop
#define Uses_TRect
#define Uses_TFileDialog
#define Uses_TChDirDialog
#define Uses_TStringCollection
#define Uses_MsgBox
#define Uses_TCollection
#define Uses_TCommandSet
#define Uses_TScreen
#define Uses_TDeskTopClock
#define Uses_TMenuBar
#define Uses_TCalculator
#define Uses_TFileCollection // To set the default sorting
#ifdef __linux__
#define Uses_TGKey
#endif
#include <ceditor.h>
#define Uses_FileOpenAid
#include <inf.h>
#define Uses_SETAppAll
#define Uses_SETAppHelper
#include <setapp.h>
#include <dskwin.h>
#include <dskedito.h>
#include <dskclose.h>
#include <dskhelp.h>
#include <dskman.h>
#include <edcollec.h>
#include <sdginter.h>
#include <edprint.h>
#include <edmsg.h>
#include <tpaltext.h>
#include <diaghelp.h>
#include <splinman.h>
#include <pathtool.h>
#include <intermp3.h>
#include <ssyntax.h>

#include <string.h>
#include <stdlib.h>
#include <locale.h>
#include <stdarg.h>
#include <strstream.h>
#include <iomanip.h>
#include <stdio.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#ifdef __DJGPP__
#include <conio.h>
#include <io.h>
#include <dpmi.h>
#endif
#include <unistd.h>
#include <getopt.h>
#include <loadkbin.h>
#include <winoldap.h>
#include <runprog.h>

void AddToEditorsHelper(TCEditWindow *p, int SelectHL=0);
static void PrintEditor(void);
static void ExportAsHTML(void);
int  AskForClosedResume(EditorResume *r,char *fileName);
extern int  AskForProjectResume(EditorResume *r,char *fileName);
void BringListOfWindows(void);
void fexpand(char *);
void InitEditorSignals(void);
void ShowMenuLoadError(void);
void UnLoadTVMenu(void);
void SLPInterfaceInit(char *);
void SLPInterfaceDeInit(void);
void InitEnvirVariables(void);
void DeInitEnvirVariables(void);
// RHTV setting
extern void setIntenseState(void);

extern TEditorCollection *edHelper;

// That's the manager for InfView, we don't worry about the viewer all is handled by it
TDskWinHelp *TSetEditorApp::InfManager=NULL;
// For the tricky context latch
int    TSetEditorApp::helpRequest=0;
ushort TSetEditorApp::helpCtxRequested=0;
int    TSetEditorApp::maxOpenEditorsSame=1;
int    TSetEditorApp::DeleteFilesOnExit=0;
char   TSetEditorApp::ExtScrSaverOpts[extscrsParMxLen]="";

const char *KeyBindFName="keybind.dat";
const char *TipsFName="editor.tip";
#ifdef __DJGPP__
// Used to guess the info directory in DOS
static char *EditorFileExt="setedit.inf";
#endif
char *EditorFile="setedit";
DeclarePalette;

TSetEditorApp *editorApp;
#ifdef TEST_SPLINES
int spLines[]={10-1,11-1,20-1,splEndOfList};
#endif
#ifdef USE_TSTRCOL
TStrCol *ReservedWords;
TStrCol *UserWords;
TStrCol *PascalRWords;
TStrCol *ClipperRWords;
#else
TStringCollection *ReservedWords;
TStringCollection *UserWords;
TStringCollection *PascalRWords;
TStringCollection *ClipperRWords;
#endif
extern TCEditWindow *clipWindow;
int StdErrOri=2,StdErrNew;

extern void InsertProjectWindow(void);

//
// closeView() function
//
void closeView(TView *p, void *p1)
{
 if (p)
    message(p, evCommand, cmClose, p1);
}

#include <pal.h>

TPalette& TSetEditorApp::getPalette() const
{
 static TPalette color ( _cpColor, sizeof( _cpColor )-1 );
 static TPalette blackwhite( _cpBlackWhite, sizeof( _cpBlackWhite )-1 );
 static TPalette monochrome( _cpMonochrome, sizeof( _cpMonochrome )-1 );
 static TPalette *palettes[] =
     {
     &color,
     &blackwhite,
     &monochrome
     };
 return *(palettes[appPalette]);
}

static void FixUpName(char *fileName)
{
 if (!fileName) return;
 // Correct the name
 int l=strlen(fileName)-1;
 if (fileName[l]=='.')
    fileName[l]=0;
}

TCEditWindow *TSetEditorApp::openEditor(char *fileName, Boolean visible,
                                        EditorResume *res, int options)
{
 Boolean openAsReadOnly=False;
 int numEditors=0;
 TCEditWindow *ain=NULL;

 if (visible && fileName)
    ain=IsAllreadyOnDesktop(fileName,&numEditors);
 TRect r = deskTop->getExtent();
 TView *p;

 // Let some space
 r.b.y-=7;
 /* First check if the user wants more copies, in this case foget the one found */
 if (ain && numEditors<maxOpenEditorsSame)
   {
    /* If we have only RO editors open it as !RO */
    openAsReadOnly=ain->editor->isReadOnly ? False : True;
    ain=NULL;
   }
 if (ain)
   {
    if (options & oedNoSelect)
      {
       ain->options&= ~ofSelectable;
       ain->makeFirst();
       ain->options|=ofSelectable;
      }
    else
       ain->select();
    p=(TView *)ain;
   }
 else
   {
    FixUpName(fileName);
    p=validView(new TCEditWindow(r,fileName,wnNoNumber));
    // If fail during load
    if (!p)
       return (TCEditWindow *)p;

    if (openAsReadOnly)
       ((TCEditWindow *)p)->editor->isReadOnly=True;

    // Transfer the special lines
    int *spL=SpLinesGetFor(fileName);
    if (spL)
       ((TCEditWindow *)p)->editor->SetSpecialLines(spL);
    #ifdef TEST_SPLINES
    ((TCEditWindow *)p)->editor->SetSpecialLines(spLines);
    #endif

    if (visible)
      { // A local copy, we can use a pointer
       int validResume=0;
       EditorResume r;
       if (res)
         {
          CopyEditorResume(&r,res);
          validResume=1;
         }
       validResume|=AskForClosedResume(&r,fileName);
       validResume|=AskForProjectResume(&r,fileName);
       AddToEditorsHelper((TCEditWindow *)p,1);
       if (validResume)
          ((TCEditWindow *)p)->ApplyResume(r);
       deskTop->insert(p);
       // When the project is OFF zoom it
       if (!validResume && !IsPrjOpened())
          ((TCEditWindow *)p)->zoom();
      }
    else
      {
       p->hide();
       deskTop->insert(p);
      }
   }

 return (TCEditWindow *)p;
}

/**[txh]********************************************************************

  Description:
  That's a hook for the editor. The editor doesn't interact with the user
directly instead most of the dialogs are created through calls
TCEditor::editorDialog in this way we can change the behavior easilly. But
this routine takes a variable number of arguments and hence is hard to
"hook the hook". For this reason now I provide a default function in
doedidia.cc that must be called indirectly by the editor. Synopsis: That's
a hook for the hook function.

***************************************************************************/

unsigned doEditDialogLocal(int dialog, ...)
{
 typedef char *_charPtr;
 va_list arg,localArg;
 char *str;
 int   flags;

 va_start(arg,dialog);
 localArg=arg;

 switch(dialog)
   {
    case edFileExists:
         str=va_arg(localArg,_charPtr);
         flags=va_arg(localArg,int);
         if (!flags && IsAllreadyOnDesktop(str))
           {
            messageBox(_("This file is already opened, first close it."),mfError | mfOKButton);
            return cmNo;
           }
         break;
   }

 return doEditDialog(dialog,arg);
}

TSetEditorApp::TSetEditorApp() :
    TProgInit( TSetEditorApp::initStatusLine,
               TSetEditorApp::initMenuBar,
               TSetEditorApp::initDeskTop
             ),
    TApplication()
{
 TCommandSet ts;
 ts.enableCmd( cmcSave );
 ts.enableCmd( cmcSaveAs );
 ts.enableCmd( cmcCut );
 ts.enableCmd( cmcCopy );
 ts.enableCmd( cmcPaste );
 ts.enableCmd( cmcClear );
 ts.enableCmd( cmcUndo );
 ts.enableCmd( cmcRedo );
 ts.enableCmd( cmcFind );
 ts.enableCmd( cmcReplace );
 ts.enableCmd( cmcSearchAgain );
 ts.enableCmd( cmeClosePrj );
 disableCommands( ts );

 TCEditor::editorDialog=doEditDialogLocal;
 doNotReleaseCPU=1;

 // The clipboard is created during the load of the DeskTop
 clipWindow=0;
}


/* No longer used
int FileOpenDialog(char *title, char *file)
{
 return
 execDialog( new TFileDialog( file, title, _("~N~ame"), fdOpenButton, hID_FileOpen ),
             file) != cmCancel;
}*/


void TSetEditorApp::fileOpen()
{
 char fileName[PATH_MAX];
 strcpy(fileName,"*");

 if (GenericFileDialog(_("File Open"),fileName,0,hID_FileOpen)!=cmCancel)
     openEditor(fileName,True);
}

void TSetEditorApp::fileOpenCopy()
{
 TCEditor *e;

 if ((e=GetCurrentIfEditor())!=0)
   { // Open a RO copy
    maxOpenEditorsSame++;
    openEditor(e->fileName,True);
    maxOpenEditorsSame--;
   }
 else
   messageBox(_("You must select an editor window for this operation"),mfError | mfOKButton);
}

void TSetEditorApp::fileNew()
{
 openEditor( 0, True );
}

void TSetEditorApp::changeDir()
{
 TChDirDialog *d=new TChDirDialog(cdNormal,0);
 d->helpCtx=cmeChangeDrct;
 execDialog(d,0);
}

void FullSuspendScreen()
{
 SuspendPaletteSystem();
 TProgram::application->suspend();
}

int ResetVideoMode(int mode, int redraw)
{
 int wasExt=0;
 // Kill the clock because it's never resized correctly
 editorApp->KillClock();
 // Set the video mode
 if (TSetEditorApp::UseExternPrgForMode)
   {
    TProgram::application->setScreenMode(0xFFFF,TSetEditorApp::ExternalPrgMode);
    wasExt=1;
   }
 else
    TProgram::application->setScreenMode(mode);
 // We ever use intense mode I don't need blinks
 setIntenseState();
 // Restore the user palette
 RestorePaletteSystem();
 // Redraw ALL
 if (redraw)
   { // Force a full redraw
    TProgram::deskTop->setState(sfVisible,True);
    TProgram::deskTop->redraw();
    TProgram::application->redraw();
   }
 if (wasExt)
    return 0;
 // Check for succesfull change
 if (mode==7 || mode==3)
    return TScreen::screenMode!=mode;
 return TScreen::screenMode==3 || TScreen::screenMode==7;
}

void FullResumeScreen()
{
 TProgram::application->resume();
 ResetVideoMode(TScreen::screenMode); // It resumes the palette too
}

void TSetEditorApp::dosShell()
{
 SaveAllEditors(); // To avoid crashes and inconsistences
 FullSuspendScreen();
 /*system("cls"); I think the programmers doesn't need it ;-)
 cout << _("Type EXIT to return...");*/
 dup2(StdErrOri,fileno(stderr)); // Restore stderr
 // Stop the playing engine
 MP3Suspend;
 system(getenv("COMSPEC"));
 MP3Resume;
 dup2(StdErrNew,fileno(stderr)); // Redirected again
 FullResumeScreen();
 ReLoadModifEditors();
}

void TSetEditorApp::showClip()
{
 clipWindow->select();
 clipWindow->show();
}

void TSetEditorApp::tile()
{
 deskTop->tile( deskTop->getExtent() );
}

void TSetEditorApp::cascade()
{
 deskTop->cascade( deskTop->getExtent() );
}

void CopyHelp2Clip(char *b, long l)
{
 if (clipWindow)
    clipWindow->editor->insertBuffer( b,0,(unsigned)l,False,True);
}

void CopyHelp2ClipGUI(char *b, long l)
{
 WINOLDAP_SetClipboard(b,l);
}

static
void ExportAsHTML(void)
{
 TCEditor *e;

 if ((e=GetCurrentIfEditor())!=0)
   {
    #pragma pack(1)
    struct
    {
     uint16 flags __attribute__((packed));
     uint16 color __attribute__((packed));
    } flags;
    #pragma pack()
    flags.flags=xhtmlTitle | xhtmlBackground | xhtmlMonoFont | xhtmlBoldFont;
    flags.color=0;
    if (!TCEditor::editorDialog(edExportHTMLOps,&flags)) return;
   
    char fileName[PATH_MAX];
    strcpy(fileName,"*.html");
   
    if (GenericFileDialog(_("Export file as"),fileName,"*",hID_FileSave,
        fdDialogForSave)!=cmCancel)
      {
       if (access(fileName,F_OK)==0 &&
           TCEditor::editorDialog(edFileExists,fileName,0)==cmNo)
          return;
       FILE *f=fopen(fileName,"wb");
       if (f)
         {/* Create a table with the palette */
          unsigned pal[16];
          GetRGBArrayPaletteSystem(pal);
          unsigned flgs=flags.flags;
          if (flags.color==0)
             flgs|=xhtmlUseColors;
          e->SourceToHTML(f,pal,flgs);
          if (ferror(f))
             TCEditor::editorDialog(edWriteError,fileName);
          fclose(f);
         }
       else
         TCEditor::editorDialog(edCreateError,fileName);
      }
   }
}

static
char *GetWordUnderCursor(unsigned size)
{
 TCEditor *e;

 if ((e=GetCurrentIfEditor())!=0)
    return e->WordUnderCursor(size);
 return 0;
}

static
void PrintEditor(void)
{
 TCEditor *e;

 if ((e=GetCurrentIfEditor())!=0)
   {
    if (e->IslineInEdition)
       e->MakeEfectiveLineInEdition();
    e->buffer[e->bufLen]=0;
    char *s=strrchr(e->fileName,'/');
    if (!s)
       s=e->fileName;
    else
       s++;
    PrintSource(e->buffer,s,e->tabSize);
   }
 else
   messageBox(_("This window can't be printed select an editor"),mfError | mfOKButton);
}

void Colors(void);
#define T(a) \
case cme##a:\
     event.message.command=cm##a;\
     TApplication::handleEvent(event);\
     return;
#define TCheck(a) \
case cme##a:\
     if (TProgram::deskTop->current)\
       {\
        event.message.command=cm##a;\
        TApplication::handleEvent(event);\
       }\
     return;

void TSetEditorApp::getEvent(TEvent& event)
{
 // Why in the hell that's so tricky? If I don't get the context here but in the
 // handleEvent the originating routine allready lost the focus so I get nothing
 TApplication::getEvent(event);
 if (helpRequest && event.what==evNothing)
   { // If we already got a request now pop-up the help
    event.what=evCommand;
    event.message.command=cmHelp;
   }
 switch (event.what)
   {
    case evCommand:
         switch (event.message.command)
           {
            case cmHelp:
            case cmeInfView:
                 if (!helpRequest)
                   { // It acts like a latch to hold the context until the help is displayed
                    helpRequest=1;
                    helpCtxRequested=getHelpCtx();
                    event.message.command=evNothing;
                   }
                 else
                   {
                    GetContextHelp();
                    clearEvent(event);
                   }
                 break;
           }
         break;
   }
}

void TSetEditorApp::handleEvent( TEvent& event )
{
 TApplication::handleEvent( event );
 if (event.what==evBroadcast)
   {
    if (event.message.command==cmClosingWindow && edHelper)
       edHelper->removeWindow(event.message.infoPtr);
       // We clear the event before returning
    else
       return;
   }
 else
 if (event.what!=evCommand)
    return;
 else
    {
     switch (event.message.command)
        {
         case cmeOpen:
              fileOpen();
              break;
 
         case cmeNew:
              fileNew();
              break;

         case cmeOpenROCopy:
              fileOpenCopy();
              break;
 
         case cmeChangeDrct:
              changeDir();
              break;
 
         case cmeDosShell:
              dosShell();
              break;
 
         case cmeShowClip:
              showClip();
              break;
 
         case cmeTile:
              tile();
              break;
 
         case cmeCascade:
              cascade();
              break;

         case cmeCalculator:
              executeCalc(GetWordUnderCursor(250));
              break;

         case cmePocketCalc:
              pocketCalculator();
              break;

         case cmeGrepDialog:
              grepWindow(GetWordUnderCursor(maxGrepString));
              break;

         case cmeInfView:
              GetContextHelp();
              break;

         case cmeLastHelp:
              InfManager->MakeVisible();
              break;

         case cmeAnotherInfView:
             {
              TDskWinHelp *p;

              p=new TDskWinHelp(EditorFile,"");
              deskTop->insert(p->window);
              edHelper->addNonEditor(p);
              p->window->select();
              p->window->show();
             }
              break;

         case cmeManPageView:
             {
              TDskWinMan *p;

              p=ManPageView();
              if (p)
                {
                 deskTop->insert(p->view);
                 edHelper->addNonEditor(p);
                 p->view->select();
                 p->view->show();
                }
             }
              break;

         case cmeSyntaxHelp:
             {
              char *FileName,*NodeName;
              char *word=GetWordUnderCursor(MAX_NODE_NAME);
              switch (SyntaxSearch_Search(word,FileName,NodeName))
                {
                 case 0:
                      messageBox(_("Couldn't find any matching help"),mfOKButton);
                      break;
                 case 1:
                      InfManager->Goto(FileName,NodeName,
                                       SyntaxSearch_GetJumpOption() ? word : 0);
                      InfManager->MakeVisible();
                      break;
                }
              delete[] word;
             }
              break;

         case cmeSyntaxHelpOps:
              SyntaxSearch_EditSettings();
              break;

         case cmeSyntaxHelpFiles:
              SyntaxSearch_EditFilesList();
              break;

         case cmeTipOfTheDay:
              ShowTips(ExpandHome(TipsFName),1);
              break;

         case cmeAboutBox:
              FullAboutBox();
              break;

         case cmeListWin:
              BringListOfWindows();
              break;

         case cmeUserScreen:
              ShowUserScreen(event);
              break;

         case cmeEditKeyBind:
              if (KeyBindEdit())
                 SaveKeyBind(ExpandHomeSave(KeyBindFName));
              break;

         case cmeSetUpAltKeys:
              if (AltKeysSetUp())
                 SaveKeyBind(ExpandHomeSave(KeyBindFName));
              break;

         case cmeKbBackDefault:
              if (KeyBackToDefault())
                 SaveKeyBind(ExpandHomeSave(KeyBindFName));
              break;

         case cmeKeyPadBehavior:
              if (KeyPadSetUp())
                 SaveKeyBind(ExpandHomeSave(KeyBindFName));
              break;

         case cmeKeyboardSetUp:
              KeyboardModeSetUp();
              break;

         case cmeSeeScanCodes:
              SeeScanCodes();
              break;

         case cmeOpenPrj:
              OpenProject();
              break;

         case cmeClosePrj:
              CloseProject(1);
              break;

         case cmeSDG:
              SDGInterfaceRun();
              break;

         case cmeSDGDialog:
              SDGInterfaceDialog();
              break;

         case cmeSetColors:
              Colors();
              break;

         case cmePrintEditor:
              PrintEditor();
              break;

         case cmeSetUpPrinter:
              PrintSetup();
              break;

         case cmeNextMessage:
              EdMessageSelectNext();
              break;

         case cmePrevMessage:
              EdMessageSelectPrev();
              break;

         case cmeSetScreenOps:
              SetScreenOps();
              break;

         case cmeEditPalette:
              EditPalette();
              break;

         case cmeEdGralOptions:
              SetGeneralEditorOptions();
              break;

         case cmeScreenSaverOpts:
              SetScreenSaversOptions();
              break;

         case cmcSetGlobalOptions:
              TCEditor::SetGlobalOptions();
              break;

         case cmeEditUserWords:
              TCEditUserWords(ExpandHomeSave(GetNameOfUserWordsFile()),cmeEditUserWords);
              edHelper->redrawEditors();
              break;

         case cmeEditDeflOpts:
              TCEditDefaultOpts(ExpandHomeSave(GetNameOfDefaultOptsFile()),cmeEditDeflOpts);
              break;

         case cmeRunCommand:
              RunExternalProgram();
              break;

         case cmeConfRunCommand:
              ConfigureRunCommand();
              break;

         case cmeHTMLAccents:
              HTMLAcc_Entry();
              break;

         case cmeExportAsHTML:
              ExportAsHTML();
              break;

         case cmeQuitDelete:
              DeleteFilesOnExit=1;
         case cmeQuit:
              event.message.command=cmQuit;
              TApplication::handleEvent(event);
              break;

         case cmeDeleteBkps:
              KillFilesToKill();
              ReleaseFilesToKill();
              break;

         case cmeASCIIChart:
              ASCIIWindow();
              break;

         case cmeCalendar:
              CalendarWindow();
              break;

         case cmeFileOpenOptions:
              SetFileOpenDialogOptions();
              break;

         case cmeRemapCodePage:
              RemapCodePageEd();
              break;

         case cmeReDraw:
              redraw();
              break;

         // These commands are traslated to the original values
         TCheck(Resize)
         TCheck(Zoom)
         TCheck(Next)
         TCheck(Prev)
         TCheck(Close)

         default:
             ProcessMP3Commands;
             if (isAMacroInMenu(event.message.command)) break;
             return;
         }
    }
 clearEvent( event );
}
#undef T

void TSetEditorApp::RemapCodePageEd(void)
{
 TCEditor *e;
 int from,to;
 ushort ops;

 if ((e=GetCurrentIfEditor())!=0 && ChooseConvCPs(from,to,ops))
    e->RemapCodePageBuffer(from,to,ops);
}

void TSetEditorApp::pocketCalculator(void)
{
 TCalculator *calc=(TCalculator *)validView(new TCalculator);

 if (calc)
   {
    //calc->helpCtx = hcCalculator; very obvious to use
    deskTop->insert(calc);
   }
}



#ifdef __DJGPP__
#include <sys/exceptn.h>
void CtrlCOff(void)
{
 // Interrupt key: RightShift+Ctrl+Del
 __djgpp_set_sigint_key(0x553);
 __djgpp_set_sigquit_key(0x553);
}

#define BestWrite(a,b) _write(STDERR_FILENO,a,b)
#else
void CtrlCOff(void)
{
 // Just ignore ^C and ^\ so people doesn't break the editor unintentionally
 signal(SIGINT,SIG_IGN);
 signal(SIGQUIT,SIG_IGN);
}

#define BestWrite(a,b) write(STDERR_FILENO,a,b)
#endif

char DumpStartName[]="\n\n>>>>>>>>>>>>>>\n";

void DumpEditors(void)
{
 if (edHelper)
   {
    int c=edHelper->Editors;
    if (c>0 && c<100)
      {
       int i;
       TDskWinEditor *st;
       for (i=0; i<c; i++)
          {
           st=(TDskWinEditor *)(edHelper->at(i));
           if (st)
             {
              TCEditWindow *edw=st->edw;
              if (edw)
                {
                 TCEditor *ed=edw->editor;
                 if (ed->modified==True)
                   {
                    unsigned l=ed->bufLen;
                    char *b=ed->buffer;
                    ed->fileName[PATH_MAX-1]=0;
                    BestWrite(DumpStartName,sizeof(DumpStartName)-1);
                    BestWrite(ed->fileName,strlen(ed->fileName));
                    BestWrite(DumpStartName,sizeof(DumpStartName)-1);
                    if (b && l<1000000)
                       BestWrite(b,l);
                   }
                }
             }
          }
      }
   }
}


static
void WaitForKeyDown(TEvent &event)
{
 #ifdef __DJGPP__
 do
   {
    editorApp->idle();
    editorApp->clearEvent(event);
    event.getKeyEvent();
   }
 while ((event.what & (evKeyDown)) == 0);
 #else
 // The Linux port is totally broken and needs this workaround
 getchar();
 editorApp->clearEvent(event);
 #endif
}

void TSetEditorApp::ShowUserScreen(TEvent &event)
{
 #ifndef __DJGPP__
 ShowUserScreenDialog();
 #endif

 TProgram::deskTop->setState(sfVisible,False);
 TProgram::application->suspend();
 WaitForKeyDown(event);
 clearEvent(event);
 TProgram::application->resume();
 ResetVideoMode(TScreen::screenMode);
}

__link( RBackground )
__link( RCEditWindow )
__link( RInfViewer )

//
//   Some of the following functions are just wrappers to hide
// the implementation of the editor's collection. In this way
// other files doesn't need to know about edHelper and your
// class.
//

void AddToEditorsHelper(TCEditWindow *p, int SelectHL)
{
 edHelper->addEditor(p,SelectHL);
}

int IsAnEditor(void *p)
{
 if (!edHelper) return 0;
 return edHelper->search(p,dktEditor)>=0;
}

TCEditor *GetCurrentIfEditor()
{
 TView *p=editorApp->deskTop->current;
 if (!p) return 0; // Avoid a search
 if (IsAnEditor(p))
    return ((TCEditWindow *)p)->editor;
 return 0;
}

TCEditWindow *IsAllreadyOnDesktop(char *fileName, int *cant)
{
 // First search by inode, the only way
 ccIndex pos=edHelper->searchEditorbyINode(fileName,cant);
 if (pos<0)
    return NULL;
 TDskWinEditor *st=(TDskWinEditor *)edHelper->at(pos);
 return st->edw;
}

int AskForClosedResume(EditorResume *r,char *fileName)
{
 if (!fileName)
    return 0;
 ccIndex pos=edHelper->search(fileName,dktClosed);
 if (pos<0)
    return 0;
 CopyEditorResume(r,&(((TDskWinClosed *)edHelper->at(pos))->resume));
 return 2;
}

int SearchInHelper(int type, void *p)
{
 return edHelper->search(p,type)>=0;
}

void AddNonEditorToHelper(TDskWin *p)
{
 if (p)
    edHelper->addNonEditor(p);
}


static int NamesPrinted;
static
void EdPrintName(void *p, void *f)
{
 TDskWin *dsk=(TDskWin *)p;
 if (dsk->type==dktEditor)
   {
    fprintf((FILE *)f," %s ",((TDskWinEditor *)p)->edw->editor->fileName);
    NamesPrinted++;
   }
}

/**[txh]********************************************************************

  Description:
  Writes all the names of the opened windows to the stream f. The names are
separated by spaces.@p
  Used by the Grep Interface.@p

  Returns:
  The number of names sent to the stream.

***************************************************************************/

int WriteNamesOfOpenedTo(FILE *f)
{
 NamesPrinted=0;
 if (edHelper)
    edHelper->forEach(EdPrintName,f);
 return NamesPrinted;
}

/**[txh]********************************************************************

  Description:
  Shows the file and line specified. If the file isn't opened the routine
opens it.

***************************************************************************/

int ShowFileLine(int line, char *file)
{
 if (line<0)
    return 0;
 TCEditWindow *edw=editorApp->openEditor(file,True,0,oedNoSelect);
 if (edw)
   {
    TCEditor *ed=edw->editor;
    ed->lock();
    ed->GoAndSelectLine(line);
    ed->trackCursor(True);
    ed->update(ufView);
    ed->unlock();
    return 1;
   }
 return 0;
}


/**[txh]********************************************************************

  Description:
  Goes to the file and line specified. If the file isn't opened the routine
opens it.

***************************************************************************/

int GotoFileLine(int line, char *file, char *msg)
{
 if (line<0)
   {
    messageBox(_("This line no longer exists"),mfOKButton);
    return 0;
   }
 TCEditWindow *edw=editorApp->openEditor(file,True);
 if (edw)
   {
    TCEditor *ed=edw->editor;
    ed->lock();
    ed->GoAndSelectLine(line);
    ed->trackCursor(True);
    if (msg)
       ed->setStatusLine(msg);
    ed->unlock();
    return 1;
   }
 return 0;
}

/**[txh]********************************************************************

  Description:
  Called by the special lines manager to notify that some special lines list
changed.

***************************************************************************/

void ApplySpLines(char *fileName,int *spLines)
{
 TCEditWindow *edw=IsAllreadyOnDesktop(fileName);
 if (edw)
    edw->editor->SetSpecialLines(spLines);
}

void SaveAllEditors(void)
{
 if (edHelper)
    edHelper->saveEditors();
}

static
int EdReLoad(void *p)
{
 TDskWin *dsk=(TDskWin *)p;
 if (dsk->type==dktEditor)
   {
    TCEditor *e=((TDskWinEditor *)p)->edw->editor;
    struct stat s;
    /* Read Only editors are like snap-shots, don't reload */
    /* Don't be fooled by new files, they aren't in disk! */
    if (!e->isReadOnly && e->DiskTime!=0 && stat(e->fileName,&s)==0)
      {
       if (s.st_mtime>e->DiskTime)
         {
          if (messageBox(mfYesButton | mfNoButton | mfConfirmation,
              _("The disk copy of %s is newer, reload it?"),e->fileName)==cmYes)
            {
             char *name=strdup(e->fileName);
             closeView(((TDskWinEditor *)p)->edw,0);
             editorApp->openEditor(name,True);
             delete name;
             return 1;
            }
         }
      }
   }
 return 0;
}

/**[txh]********************************************************************

  Description:
  Writes all the names of the opened windows to the stream f. The names are
separated by spaces.@p
  Used by the Grep Interface.@p

  Returns:
  The number of names sent to the stream.

***************************************************************************/

void ReLoadModifEditors(void)
{
 if (edHelper)
   {
    int again=1;
    int c,i;
    do
      {
       again=0;
       c=edHelper->getCount();
       i=0;
       while (i<c && !again)
          again=EdReLoad(edHelper->at(i++));
      }
    while (again);
   }
}

clock_t TSetEditorApp::LastTimeUpdate=0;
char TSetEditorApp::UseScreenSaver=1;
char TSetEditorApp::ShowClock=1;
char TSetEditorApp::UseExternPrgForMode=0;
char *TSetEditorApp::WhichScrSaver=0;
int  TSetEditorApp::screenSaverTime=180;
int  TSetEditorApp::screenSaverTimeMouse=3;
TDeskTopClock *TSetEditorApp::Clock=0;
char TSetEditorApp::ExternalPrgMode[80]="c:/etc/stm -t c:/etc/TextConfig 108x30";

void TSetEditorApp::CreateClock()
{
 // Take the last 6 characters for the clock
 TRect r=menuBar->getExtent();
 int start=r.a.x;
 r.a.x=r.b.x-6;
 Clock=new TDeskTopClock(r);
 insert(Clock);
 // Reduce the menubar
 r.b.x=r.a.x;
 r.a.x=start;
 menuBar->changeBounds(r);
}

void TSetEditorApp::KillClock()
{
 if (Clock)
   {
    remove(Clock);
    destroy(Clock);
    Clock=0;
    // Enlarge the menuBar again
    TRect r=menuBar->getExtent();
    r.b.x+=6;
    menuBar->changeBounds(r);
   }
}

void TSetEditorApp::setCmdState( uint16 command, Boolean enable )
{
 if (enable)
    enableCommand(command);
 else
    disableCommand(command);
}

#ifdef __DJGPP__
const int clockResolution=CLOCKS_PER_SEC;
#else
const int clockResolution=100;
#endif

void TSetEditorApp::idle()
{
 ProcessMP3Idle;
 TApplication::idle();
 clock_t DifLastTime=lastIdleClock-LastTimeUpdate;
 // Update 2 times per second
 if (DifLastTime<clockResolution/2)
   {
    yieldProcessor(-1);
    return;
   }
 LastTimeUpdate=lastIdleClock;
 /***** Update cme Commands here *****/
 // Now update commands
 // cme commands must follow cm commands
 setCmdState(cmeResize,commandEnabled(cmResize));
 setCmdState(cmeZoom,commandEnabled(cmZoom));
 setCmdState(cmeClose,commandEnabled(cmClose));
 Boolean genState=commandEnabled(cmNext);
 setCmdState(cmeNext,genState);
 setCmdState(cmePrev,genState);
 setCmdState(cmeTile,genState);
 setCmdState(cmeCascade,genState);
 // Disable "Local Options" if no editors are available
 TCEditor *e;
 if ((e=GetCurrentIfEditor())!=0)
   {
    setCmdState(cmePrintEditor,True);
    setCmdState(cmeOpenROCopy,True);
    setCmdState(cmeRemapCodePage,True);
    if (e->ShowMatchPairFly)
       e->handleCommand(cmcForceMatchPairHL);
   }
 else
   {
    setCmdState(cmePrintEditor,False);
    setCmdState(cmeOpenROCopy,False);
    setCmdState(cmeRemapCodePage,False);
   }
 if (ShowClock)
   {
    if (!Clock)
       CreateClock();
    Clock->update();
   }
 else
    if (Clock)
       KillClock();

 int seconds=inIdleTime/clockResolution;
 if (seconds==screenSaverTimeMouse)
   {
    MouseEventType me;
    TMouse::getEvent(me);
    TRect r=deskTop->getExtent();
    if (me.where.x==r.b.x-1 && me.where.y==0)
       screenSaver();
   }
 else
 if (seconds>=screenSaverTime)
    screenSaver();
}


void OpenFileFromEditor(char *fullName)
{
 editorApp->openEditor(fullName,True);
}


static
void ShowAboutStartBox(void)
{
 if (AboutStartBox())
   {// Check to avoid showing it more than ones
    char *end;
    char *s=(char *)GetVariable("SET_README_SHOWN");
    if (s && (unsigned)strtol(s,&end,0)>=TCEDITOR_VERSION)
       return;

    char b[12];
    sprintf(b,"0x%06X",(int)TCEDITOR_VERSION);
    InsertEnviromentVar("SET_README_SHOWN",b);

    // Load the readme.1st
    #ifdef __DJGPP__
    char Name[PATH_MAX];
    GetPathRelativeToRunPoint(Name,"contrib/setedit.bin/","readme.1st");
    if (!edTestForFile(Name)) // The simplified distribution have a more simple name
       GetPathRelativeToRunPoint(Name,"texts/","readme.1st");
    #else
    char *Name=ExpandFileNameToThePointWhereTheProgramWasLoaded("readme.1st");
    #endif

    if (!edTestForFile(Name))
      {
       messageBox(_("I can't find the readme.1st file, please look for it the .ZIP and read the file."),mfOKButton);
       //messageBox(Name,mfOKButton);
       return;
      }
    editorApp->openEditor(Name,True);
   }
}

static
int GuessOneSET_FILES(const char *OSShareDir)
{
 // The fool doesn't put SET_FILES in the autoexec.bat, will try to guess here
 char Name[PATH_MAX],Val[PATH_MAX];
 #ifdef __DJGPP__
 char *end=GetPathRelativeToRunPoint(Name,OSShareDir,SHLFile);
 #else
 strcpy(Name,OSShareDir);
 char *end=Name+strlen(Name);
 strcat(Name,"/" SHLFile);
 #endif

 if (!edTestForFile(Name))
    // Bad luck guessing
    return 1;

 *end=0;
 sprintf(Val,"SET_FILES=%s",Name);
 putenv(Val);
 return 0;
}

#ifdef __DJGPP__
const char *OSShareDir1="share/setedit/";
#else
const char *OSShareDir1="/usr/share/setedit";
const char *OSShareDir2="/usr/local/share/setedit";
#endif

static
int GuessSET_FILES()
{
 int ret;
 ret=GuessOneSET_FILES(OSShareDir1);
#ifndef __DJGPP__
 if (ret)
    ret=GuessOneSET_FILES(OSShareDir2);
#endif
 return ret;
}

#ifdef __DJGPP__
static
int GuessINFOPATH(void)
{
 char Name[PATH_MAX],Val[PATH_MAX];
 char *end=GetPathRelativeToRunPoint(Name,"info/",EditorFileExt);

 if (!edTestForFile(Name))
    return 1;

 *end=0;
 sprintf(Val,"INFOPATH=%s",Name);
 putenv(Val);
 return 0;
}
#endif

static
void ShowInstallError(char *var, const char *suggest, int end)
{
 TScreen::suspend();
 fprintf(stderr,_("\nWrong installation! You must define the %s environment variable.\n"),var);
 fprintf(stderr,_("Read the readme.1st file included in the .zip distribution file.\n\n"));
 #ifdef __DJGPP__
 char *s=getenv("DJDIR");
 if (s)
   fprintf(stderr,_("I suggest that: SET %s=%s/%s\n\n"),var,s,suggest);
 #else
 fprintf(stderr,_("I suggest that: SET %s=%s\n\n"),var,suggest);
 #endif
 fflush(stderr);
 if (end)
    exit(1);
 fprintf(stderr,_("press ENTER to continue\n"));
 getchar();
 TScreen::resume();
}

static
void ShowErrorSET_FILES()
{
 TScreen::suspend();
 fputs(_("\nYou defined SET_FILES wrongly, it doesn't point to a directory.\n\n"),stderr);
 fflush(stderr);
 exit(1);
}

extern void RestoreScreen();

/*************************************************************************************
   Memory full protection, I hope it will be usefull with djgpp v2.02
*************************************************************************************/

#include <new>

static char *safetypool;

static void mynewhandler(void)
{
 if (safetypool==NULL)
    _exit(1); // We can't call exit because it can need malloc
 delete [] safetypool;
 safetypool=NULL;
 set_new_handler(NULL);
 messageBox(_("Memory is nearly full. Please exit, and restart."), mfOKButton | mfError);
}
/*************************************************************************************/

/******* Command line parsing *******/
static char *ProjectAskedByUser=0;
static char  UseMSignal=1,RedirectStderr=1,CommandLineParsed=0;
// Red Hat 5.2 keyboard layout
static char  UseRH52=0;
extern int   use_mouse_handler;
#ifdef __DJGPP__
extern char useBIOS_VGA_State;
#endif

static
struct option longopts[] =
{
  { "no-signal", 0, 0, 's' },
  { "no-redirect", 0, 0, 'r' },
  { "force-no-lfn", 0, 0, 'l' },
  { "force-lfn", 0, 0, 'L' },
  { "no-mouse-hook", 0, 0, 'm' },
  { "use-rh-52-keys", 0, 0, 'k' },
  { "project", 1, 0, 'p' },
  { "low-vga-save", 0, 0, 'S' },
  { "help", 0, 0, 'h' },
  { 0, 0, 0, 0 }
};

static
void ParseCommandLine(int argc, char *argv[])
{
 if (CommandLineParsed)
    return;
    
 int optc;

 while ((optc=getopt_long(argc,argv,"srlLmkp:Sh",longopts,0))!=EOF)
   {
    switch (optc)
      {
       case 's':
            UseMSignal=0;
            break;
       case 'r':
            RedirectStderr=0;
            break;
       case 'l':
            putenv("LFN=N");
            break;
       case 'L':
            putenv("LFN=Y");
            break;
       case 'm':
            use_mouse_handler=0;
            break;
       case 'k':
            UseRH52=1;
            break;
       case 'p':
            ProjectAskedByUser=optarg;
            break;
       #ifdef __DJGPP__
       case 'S':
            useBIOS_VGA_State=0;
            break;
       #endif
       case 'h':
       default:
            TScreen::suspend();
            fprintf(stderr,_("Setedit "TCEDITOR_VERSION_STR". Copyright (c) 1996-1999 by Salvador E. Tropea\n\n"));
            fprintf(stderr,_("setedit [options] [file_name ...]\n\n"));
            fprintf(stderr,_("Valid options are:\n"));
            fprintf(stderr,_("-s, --no-signal:         disables the hook of signals to save the modified\n"
                             "                         buffers during a crash. Could be needed if the editor\n"
                             "                         hangs in an extraordinary way.\n"));
            fprintf(stderr,_("-r, --no-redirect:       disables the stderr redirection. Only used during\n"
                             "                         debugging.\n"));
            #ifdef __DJGPP__ // Don't name it under Linux
            fprintf(stderr,_("-l, --force-no-lfn:      avoids the use of long file names under W9x.\n"));
            fprintf(stderr,_("-L, --force-lfn:         forces the use of long file names under W9x.\n"));
            fprintf(stderr,_("-m, --no-mouse-hook:     don't hook the mouse.\n"));
            fprintf(stderr,_("-S, --low-vga-save:      use low level functions to save/restore VGA state.\n"));
            #else
            fprintf(stderr,_("-k, --use-rh-52-keys:    enables the Red Hat 5.2 style keyboard mapping.\n"));
            #endif
            fprintf(stderr,_("-p, --project file_name: loads the indicated project, if the file doesn't exist\n"
                             "                         the editor creates a new one\n"));
            fprintf(stderr,_("-h, --help:              displays this text ;-).\n\n"));
            fflush(stderr);
            exit(1);
            break;
      }
   }
 CommandLineParsed=1;
}
/******* End of Command line parsing *******/


#ifdef __DJGPP__
// Command line options from crt0 module
extern int   __crt0_argc;
extern char **__crt0_argv;
// DJGPP share options
extern int __djgpp_share_flags;

static __attribute__ ((constructor))
void initProgram(void)
{
 // MUST BE ON or some frt files will fail
 __system_flags|=__system_allow_multiple_cmds;
 // Fine tune the share flags
 __djgpp_share_flags=SH_DENYWR;
 ParseCommandLine(__crt0_argc,__crt0_argv);
}
#endif

int main(int argc, char *argv[])
{
 ParseCommandLine(argc,argv);
 CheckIfCurDirValid();

 /* That's better for me, easier for incremental searchs */
 TFileCollection::sortOptions=fcolAlphabetical | fcolCaseInsensitive;

 /* Be sure we can store temporals, or at least do our best */
 CheckForValidTMPDIR();
 /*
   The point where the exe was loaded is a reference. Before SET_FILES because I use
   it to guess.
 */
 SetReferencePath(argv[0]);

 /*
   That's a new policy, after releasing v0.4.1 and saw that nobody bothers about
   reading the readme.1st (mainly because of the distribution structure) now the
   editor assumes that:
   In 0.4.14 I added a guess to help fools.
 */
 char *set_files=getenv("SET_FILES");
 if (set_files && !IsADirectory(set_files) && GuessSET_FILES())
    ShowErrorSET_FILES();
 if (!set_files && GuessSET_FILES())
    ShowInstallError("SET_FILES",OSShareDir1,1);

 /*
   After SET_FILES because it needs SET_FILES.
 */
 InitEnvirVariables();

 /*
   Check if the user have the INFOPATH defined. On UNIX InfView already makes a guess
 */
 #ifdef __DJGPP__
 LoadInfoEnviroment();
 if (!getenv("INFOPATH") && GuessINFOPATH())
    ShowInstallError("INFOPATH","info",0);
 #endif

 /*
   Redirect stderr to a unique file to catch any kind of errors.
 */
 char *TemporalStdErr=0;
 if (RedirectStderr)
    TemporalStdErr=RedirectStdErrToATemp(StdErrOri,StdErrNew);

 InitPaletteSystem();
 setIntenseState();

 safetypool=new char [64000];
 set_new_handler (mynewhandler);

 TSetEditorApp::WhichScrSaver=GetDefaultScreenSaver();

 #ifdef USE_TSTRCOL
 ReservedWords = new TStrCol(20,5);
 UserWords     = new TStrCol(20,5);
 PascalRWords  = new TStrCol(20,5);
 ClipperRWords = new TStrCol(20,5);
 #else
 ReservedWords = new TStringCollection(20,5);
 UserWords     = new TStringCollection(20,5);
 PascalRWords  = new TStringCollection(20,5);
 ClipperRWords = new TStringCollection(20,5);
 #endif

 #define I(a) ReservedWords->insert((void *)a)
 I("int");        I("char");      I("unsigned");
 I("signed");     I("float");     I("double");
 I("short");      I("long");      I("void");

 I("for");        I("while");     I("do");
 I("if");         I("else");      I("return");
 I("continue");   I("goto");

 I("static");     I("const");      I("enum");
 I("struct");     I("extern");     I("sizeof");
 I("union");      I("typedef");    I("inline");
 I("register");   I("volatile");

 I("switch");     I("case");       I("default");
 I("break");

 I("new");        I("delete");

 I("class");      I("private");     I("protected");
 I("public");     I("this");        I("template");
 I("throw");      I("catch");       I("virtual");
 I("friend");     I("operator");    I("try");
 ReservedWords->setOwnerShip(False);
 #undef I

 #define I(a) UserWords->insert((void *)a)
 I("uchar");      I("ushort");      I("uint16");
 I("uint32");
 UserWords->setOwnerShip(False);
 #undef I

 // List of reserved words for Turbo Pascal 5.0, if some of them isn't
 // reserved in GPC please comment this line.
 #define I(a) PascalRWords->insert((void *)a)
 I("absolute");   I("and");         I("array");
 I("begin");      I("case");        I("const");
 I("div");        I("do");          I("downto");
 I("else");       I("end");         I("external");
 I("file");       I("for");         I("forward");
 I("function");   I("goto");        I("if");
 I("implementation");               I("in");
 I("inline");     I("interface");   I("interrupt");
 I("label");      I("mod");         I("nil");
 I("not");        I("of");          I("or");
 I("packed");     I("procedure");   I("program");
 I("record");     I("repeat");      I("set");
 I("shl");        I("shr");         I("string");
 I("then");       I("to");          I("type");
 I("unit");       I("until");       I("uses");
 I("var");        I("while");       I("with");
 I("xor");
 PascalRWords->setOwnerShip(False);
 #undef I

 // Taked from the Clipper.EXE 5.2e
 #define I(a) ClipperRWords->insert((void *)a)
 I("announce");   I("begin");       I("break");
 I("call");       I("case");        I("declare");
 I("do");         I("else");        I("elseif");
 I("endcase");    I("enddo");       I("endif");
 I("exit");       I("external");    I("field");
 I("for");        I("function");    I("if");
 I("iif");        I("in");          I("init");
 I("local");      I("loop");        I("memvar");
 I("next");       I("otherwise");   I("parameters");
 I("private");    I("procedure");   I("public");
 I("recover");    I("return");      I("sequence");
 I("static");     I("step");        I("text");
 I("to");         I("then");        I("using");
 I("with");       I("while");
 ClipperRWords->setOwnerShip(False);
 #undef I

 PrintSetDefaults();

 LoadKeysForTCEditor(ExpandHome(KeyBindFName));
 #ifdef __linux__
 if (UseRH52)
    TGKey::SetKbdMapping(KBD_REDHAT52_STYLE);
 #endif
 TCEditor::SHLGenList=new TNoCaseStringCollection(5,5);
 LoadSyntaxHighLightFile(ExpandHome(SHLFile),TCEditor::SHLArray,TCEditor::SHLGenList,
                         TCEditor::SHLCant);

 if (UseMSignal)
    InitEditorSignals();
 // After InitEditorSignals()!
 CtrlCOff();

 //------ International support
 #ifndef NO_INTL_SUP
 char *locale_dir,localedir[PATH_MAX];
 setlocale(LC_ALL, "");
 // Use the LOCALEDIR var for the directory
 locale_dir=getenv("LOCALEDIR");

 if (!locale_dir)
   {
    #ifdef __DJGPP__
    // if LOCALEDIR doesn't exists use %DJDIR%/share/locale
    locale_dir=getenv("DJDIR");
    if (locale_dir)
      {
       strcpy(localedir,locale_dir);
       strcat(localedir,"/share/locale");
      }
    else
      {
       // if DJDIR doesn't exists use SET_FILES
       locale_dir=(char *)GetVariable("SET_FILES");
       if (locale_dir)
          strcpy(localedir,locale_dir);
       else
         {
          // if SET_FILES doesn't exists (imposible) use .
          // That's only to avoid a GPF in the strcpy and add a little facility
          localedir[0]='.';
          localedir[1]=0;
         }
      }
    #else
    strcpy(localedir,"/usr/share/locale");
    #endif
   }
 else
    strcpy(localedir,locale_dir);

 BINDTEXTDOMAIN(EditorFile,localedir);
 TEXTDOMAIN(EditorFile);
 #endif
 //------ end of int. support

 // Initialize the MP3 Stuff, it must be done before loading the desktop.
 // It just disable some commands
 MP3Initialize;

 editorApp = new TSetEditorApp();
 LoadEditorDesktop(1,ProjectAskedByUser,(GetDSTOptions() & dstOpenSpec) && (optind<argc));

 // Open all the files indicated in the command line
 char tmpPath[PATH_MAX];
 while (optind<argc)
   {
    strcpy(tmpPath,argv[optind]);
    fexpand(tmpPath);
    editorApp->openEditor(tmpPath,True);
    optind++;
   }
 ShowMenuLoadError();
 ShowKeyBindError();
 SLPInterfaceInit(ExpandHome("macros.slp"));

 ShowAboutStartBox();
 ShowTips(ExpandHome(TipsFName));

 editorApp->run();

 if (TSetEditorApp::DeleteFilesOnExit)
   {
    int ret=messageBoxDSA(_("Do you want to delete all the .BKP, desktop and project files?"),
        mfYesButton | mfNoButton | mfWarning,"SET_CONFQUIT",cmYes);
    if (ret!=cmYes)
       TSetEditorApp::DeleteFilesOnExit=0;
   }

 // That saves the desktop too, even if there isn't a project
 SaveProject();
 destroy(edHelper);

 if (TSetEditorApp::DeleteFilesOnExit)
   {
    KillFilesToKill();
    DeleteWildcard("*.bkp");
    DeleteWildcard("*.BKP");
    DeleteWildcard("*.dst");
    DeleteWildcard("*.epr");
   }

 ReleaseFilesToKill();
 ShutDownPaletteSystem();
 SaveEnviromentFile();

 /*
   Restore stderr
 */
 if (TemporalStdErr)
   {
    dup2(StdErrOri,fileno(stderr));
    close(StdErrNew);
    close(StdErrOri);

    struct stat s;
    if (stat(TemporalStdErr,&s)==0)
       if (s.st_size==0)
          unlink(TemporalStdErr);
    free(TemporalStdErr);
   }

 MP3DeInitialize;
 SyntaxSearch_ShutDown();
 UnLoadSyntaxHighLightFile(TCEditor::SHLArray,TCEditor::SHLGenList,TCEditor::SHLCant);
 UnLoadTVMenu();
 destroy(editorApp);
 delete TSetEditorApp::WhichScrSaver; // static member
 UnLoadEditorFonts();
 SLPInterfaceDeInit();
 destroy(ReservedWords);
 destroy(UserWords);
 destroy(PascalRWords);
 destroy(ClipperRWords);
 DeInitEnvirVariables();
 delete[] safetypool;

 return 0;
}
