#include "sample1.h"

#include XColor_i
#include XFileDialog_i
#include XString_i
#include XMessageBox_i
#include XException_i
#include XDragEvent_i
#include XDragHandler_i
#include XDragItem_i
#include XFile_i
#include XFrame_i
#include XToolBar_i
#include XPushButton_i
#include XFileInfo_i
#include XMLESearch_i
#include XCheckBox_i
#include XFontDialog_i

#include <stdlib.h>
#include <string.h>
/* This is the code for sample1, a simple text-editor */

class MyMLE: public XMultiLineEdit
{
      MyAppWindow * owner;
   public:
      MyMLE( MyAppWindow * w, XRect * r): XMultiLineEdit( w, r, MY_MLE, MLE_HORZSCROLL | MLE_VERTSCROLL | WIN_VISIBLE) { owner = w; }
      void DoControl(XControlEvent*);
};

/* we want to accept files wich are dropped on our window, so we need */
/* a handler wich can handle this messages for drag/drop. we need to derive */
/* a class from class XDragHandler and overwrite the method HandleEvent */
/* and the constructor */
class MyDragHandler: public XDragHandler
{
   public:
      MyDragHandler( XWindow * w): XDragHandler( w ) { ;}
      void HandleEvent(XDragEvent*);
};


/* here the method HandleEvent is overridden, so we can handle drag/drop events*/
void MyDragHandler :: HandleEvent ( XDragEvent * event)
{
   switch( event->GetEventID())
      {
         /* the mousepointer is over the MLE holding one or more objects */
         case DRG_DRAGOVER:
            {
               SHORT i;
               XDragItem item;
               /* ask for every object the type and format */
               for(i=0; i < event->GetDragItemCount(); i ++)
                 {
                    event->GetDragItem( &item, i);
                    if(item.VerifyItemType( DRG_M_FILE ) == FALSE)
                       {
                          /* no textfile, we will not accept these objects */
                          event->SetAcceptMode( DRG_NEVERDROP);
                          return;
                       }
                 }
               event->SetAcceptMode( DRG_DROP);       //ok, can be dropped
               event->SetOperation( DO_COPY);         //we will work with a copy
             }
             return;
         /* one or more object are dropped, because weīve tested the format (see above) */
         /* we donīt have to test it here again, the object will be files */
         case DRG_DROP:
            {
               XDragItem item;
               XString name, container;
               SHORT i;

               /* query for every object the information we need */
               for(i=0; i < event->GetDragItemCount(); i++)
                  {
                     event->GetDragItem( &item, i);
                     item.Accept();                        //let the sender know that we accept the object

                     item.GetName( &name);               //query the filename
                     item.GetContainerName( &container); //query the directory
                     container+= name;                     //make a complete path

                     MyAppWindow * appWin = (MyAppWindow*) GetWindow()->QueryWindow( WIN_PARENT);
                     appWin->LoadFile( container );        //load the file
                  }
            }
            return;
      }
}


/* here the mainwindow will be created */
MyAppWindow :: MyAppWindow( XResourceLibrary * resLib, XResource * r ): XFrameWindow( r, "Sample1", XFrameWindow::defaultStyle | FRM_ICON )
{
   loading = FALSE;

   XRect rec( 100, 100, 500, 350);
   SetSize( &rec );
   /* we create a MLE-control; because the MLE is used as client we dont want a border */
   /* and donīt use the defaults (see <xmle.h>) */
   XRect rec2( 0, 0, 400, 250);
   mle = new MyMLE( this, &rec2 );

   /*set backgroundcolor to white*/
   XColor col( COL_WHITE);
   mle->SetBackgroundColor( &col );

   /* the MLE is used as a new client-area, so sizing and moving is automaticaly done */
   SetClient( mle );

   /* we want a menubar, it is defined in the resources of the exe-file with the id IDM_MAIN */
   XResource rs( IDM_MAIN, resLib);
   menu = new XMenuBar( this, &rs);

   /* we put a string into the MLE */
   mle->SetText( "this is our MLE where you can edit your text...");

   /*we generate our drag-handler and attach it to the MLE */
   MyDragHandler * myDragHandler = new MyDragHandler( mle );

   /* setup our needed flags */
   saved = TRUE;
   loaded = FALSE;

   dlg = NULL;
   /*the mle get the focus */
   mle->SetFocus();
}


class SearchDialog: public XFrameWindow
{
      XMultiLineEdit * mle;
      MyAppWindow * win;
      XMLESearch search;
   public:
      BOOL DoCommand( LONG );
      void DoControl( XControlEvent*);
      SearchDialog( XMultiLineEdit*, XResource *, MyAppWindow * );
      ~SearchDialog();
};


SearchDialog :: ~SearchDialog()
{
   win->dlg = NULL;
}


void SearchDialog :: DoControl( XControlEvent * event )
{
   switch( event->GetEventID() )
     {
        case WIN_CHANGED:
           switch( event->GetWindowID())
              {
                 case CHECK_CASE:
                    {
                       XCheckBox * c = (XCheckBox*) event->GetWindow();
                       if( c->IsSelected())
                          search.SetCaseSensitive( FALSE );
                       else
                          search.SetCaseSensitive( TRUE );
                    }
                    break;
              }
            break;
     }
}


BOOL SearchDialog :: DoCommand( LONG com )
{
   switch( com )
     {
        case PUSH_CANCEL:
           delete this;
           break;
        case PUSH_REPLACE:
           {
              XString buffer;
              GetWindow( ENTRY_REPLACE )->GetText( &buffer );
              mle->InsertString( buffer );
           }
           break;
        case PUSH_SEARCH:
           {
              XString buffer;
              GetWindow( ENTRY_SEARCH )->GetText( &buffer );
              search.SetSearchItem( buffer );
              GetWindow( ENTRY_REPLACE )->GetText( &buffer );
              search.SetReplaceItem( buffer );
              LONG start, end;
              mle->GetSelection( start, end);
              if(start > end )
                 end = start;
              search.SetStartPoint( end );
              XCheckBox * c = (XCheckBox*) GetWindow( CHECK_REPLACE );
              if( c->IsSelected())
                 search.SetReplaceAll( TRUE );
              else
                 search.SetReplaceAll( FALSE );
              if( mle->Search( &search ) )
                 mle->Activate();
              else
                 XMessageBox( "pattern not found!", "Search", MB_OK|MB_ICONEXCLAMATION, this);
              break;
           }
     }
   return TRUE;
}


/*this is our search-dialog (very rudimental). We construct it out of the resources (see sample.dlg).*/
SearchDialog :: SearchDialog ( XMultiLineEdit * m, XResource * r, MyAppWindow * w): XFrameWindow( r, "Search...", defaultDialogStyle | FRM_CENTER,  NULL, NULL, TRUE)
{
   mle = m;
   win = w;

   /*enable the checkbox "ignore case" */
   XCheckBox * c = (XCheckBox*) GetWindow( CHECK_CASE);
   c->Select();
}


/*the destructor is called automaticaly when the window is close*/
MyAppWindow :: ~MyAppWindow()
{
   /*is there a serch-dialog open? */
   if(dlg)
      delete dlg; /* yes, kill it */
}


/* this is the overridden method DoCmmand from XFrameWindow. all commands */
/* from the menu (or toolbars in later samples) will be placed here. */
BOOL MyAppWindow :: DoCommand( LONG command )
{
   switch ( command )
      {
         //default clipboard actions
         case IDM_PASTE:
            mle->Paste();
            break;
         case IDM_COPY:
            mle->Copy();
            break;
         case IDM_CUT:
            mle->Cut();
            break;
         case IDM_CLEAR:
            mle->Clear();
            break;
         case IDM_FONT:
            {
               //select a new font for the MLE
               XFontDialog dlg( this );
               if( dlg.GetCommand() == USER_OK)
                  {
                     XString buffer;
                     LONG s = dlg.GetFontSize();
                     dlg.GetFontName( &buffer );
                     mle->SetFont(buffer, s );
                  }
            }
            break;
         case IDM_SEARCH:
            {
               //open a search dialog
               if( dlg ) //a dialog is already existing
                  dlg->Activate();
               else //create a new dialog
                  {
                     MyThread * t = (MyThread*) GetProcess();
                     XResource newRes( DIALOG_SEARCH, t->resLib);
                     dlg = new SearchDialog( mle, &newRes, this);
                  }
            }
            break;
         case IDM_OPEN:
           {
               /* display the filedialog defined by the system */
               /* set the file-suffix and title of the dialog */
               XFileDialog fileDlg(this, "*.TXT", NULL, NULL, FD_OPEN | FD_CENTER | FD_MULTIPLESEL);

               /* the user selected a file */
               if( fileDlg.GetCommand() == USER_OK)
                 {
                    XString fileName;
                    SHORT i, files = fileDlg.GetFileCount();  //how much files are selected?
                    for(i=0; i < files; i++)
                       {
                          fileDlg.GetFileName( &fileName, i );  //get filename and path for every file
                          LoadFile( fileName);                  //load the file
                       }
                 }
            }
            break;
         case IDM_CLOSE:
            /* the user selected close, so we try to close the window */
            /* the (overridden) method QueryForClose is called automaticaly (see below)*/
            delete this;
            break;
         case IDM_SAVE:
            SaveFile();
            break;
       }
   return TRUE;
}


/* when contents of window-controls change, we will get an event of the class */
/* XControlEvent where we can ask for information about the event and the sending */
/* window. in later examples the other types of events  will be shown */
void MyMLE :: DoControl( XControlEvent * event )
{
   //ask for the id of the sending window and the type of event
   if( event->GetEventID() == WIN_CHANGED && owner->loading == FALSE)
      {
         //sender is the MLE, event is that the text will be changed
         if( owner->saved == TRUE)
            {
               XString s;
               owner->GetText( &s );
               s += " - changed";
               owner->SetText( s );
               owner->saved = FALSE;
            }
       }
}


/* this is an overridden method of XFrameWindow, it is called if the user try to close the */
/* framewindow with the close-button or with ALT-F4. Return TRUE if the window can be closed, */
/* otherwise return FALSE */
BOOL MyAppWindow :: QueryForClose( void )
{
   if(saved == FALSE)    //text changed
      {
         XMessageBox msb( "Text changed - discard?", "Close Window", MB_YESNO|MB_WARNING|MB_MOVEABLE);
         if( msb.GetCommand() == MBID_NO)
            return FALSE;  //donīt close the window
      }
  return TRUE;
}


/* load a file */
BOOL MyAppWindow :: LoadFile ( char * p)
{
   // is there already a file loaded or has the user entered some text?
   if(loaded == FALSE && saved == TRUE)
      {
         //no, we load the file in the window of the current thread
         XFile loadfile;

         /* now open the file, fail if given filename is not existing */
         /* open the file for read-access, dont allow any other programm to use the file while it is open*/
         if( loadfile.Open( p, XFILE_FAIL_IF_NEW | XFILE_OPEN_EXISTING, XFILE_SHARE_DENYWRITE | XFILE_READONLY ) == 0)
           {
              XString s;

              loading = TRUE;

              //how large is the file?
              XFileInfo info;
              loadfile.GetFileInfo( &info );
              LONG size = info.GetFileSize();

              //read the complete file
              loadfile.Read ( (PVOID) s.GetBuffer(info.GetFileSize() + 1), size);
              s.ReleaseBuffer( info.GetFileSize() );

              //set the XString content to the mle
              mle->SetText( s );
              //dontīforget to close the file
              loadfile.Close();
              loaded = TRUE;
              path = p;
              mle->SetFocus();
              GetText( &s );
              s+= " - ";
              s+= p;
              SetText( s );

              loading = FALSE;

              return TRUE;
            }
         else
            {
               XMessageBox( p, "couldnīt open File!", MB_OK|MB_ERROR);
               return FALSE;
            }
      }
   else
     {
         //there is a file loaded, or the user has entered some text, so
         // we create a new thread which will load the file
         MyThread * th = new MyThread( p);
         th->Run();
         return TRUE;
     }
}


BOOL MyAppWindow :: SaveFile( void )
{
   XFile savefile;
   XString s;

   mle->GetText( &s );

   /* now open the file, create a new one if the given filename is not existing, otherwise overwrite */
   /* it. open the file for write-access, dont allow any other programm to use the file while it is open*/
   if( savefile.Open( path, XFILE_CREATE_IF_NEW | XFILE_REPLACE_EXISTING, XFILE_SHARE_DENYNONE | XFILE_WRITEONLY, s.GetLength()) == 0)
      {
         /* set the file-cursor to the beginning of the file */
         savefile.Seek( 0, FILE_BEGIN);

         /*save the string */
         savefile.Write ( (char*) s, s.GetLength());

         /* close the file */
         savefile.Close();
         saved = TRUE;
         return TRUE;
      }
   else
      {
         XMessageBox( "could not open file", "error", MB_OK| MB_ERROR);
         return FALSE;
      }
}


/* this is the overriden method Init() from XThread. here we can setup */
/* the data for every thread and generate its objects*/
void MyThread :: Init(void)
{
   resLib = new XResourceLibrary( this );  //create a resourcelibary out of the exe-file
   XResource res( IDM_MAIN, resLib);
   try
   {
      appWindow = new MyAppWindow( resLib, &res );  //create new framewindow (see above)
   }
   catch( XException& e)
   {
      e.ShowError();
      exit(-1);
   }

   if(iniFile.GetLength())                             //a file was specified in the constructur (it was in the command line)
      appWindow->LoadFile(iniFile);        // so we load the file
}


/* our thread-constructor, p can be a pointer to a filename of a file to load */
MyThread :: MyThread( char * p)
{
  if( p )
     iniFile = p;
}


int main ( int argc, void ** argv)
{
   if( argc > 1)  //there were filenames in the commandline
   {
      SHORT i;
      for(i=1; i < argc; i++)
      {
          MyThread * thread = new MyThread( (char*) argv[i] ); //start for every file a new thread
          thread->Run();                                       //let the thread run
      }
   }
   else
   {
      MyThread * thread = new MyThread();      //create a thread
      thread->Run();                           //let the thread run
   }

   XThread :: RunThreads();                       //let all threads work

   return 0;
}
