/*
    listPM list files under Presentation Manager. Uses Open Class Libarary.
    Copyright (C) 1998, 1999  Paul Elliott

    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; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Paul Elliott
    PMB # 181
    11900 Metric Blvd Ste. J
    Austin Tx 78758-3117
    pelliott@io.com

    HISTORY
    6-13-1999                  MLEFormat used instead of determining UNIX or DOS eol format.

*/
#include "listpm.h"            // constants for resourse libarary.
                               // do not edit listpm.h, but use URE to modify.

#include "list.hpp"            // List window defined.

#include "glbsize.hpp"         // How to store ISize externally.
#include "glbfont.hpp"         // How to store IFont Externally.
#include "glbwrap.hpp"         // How to save word wrap options.
#include "glbcolor.hpp"        // How to save colors
#include "listthrd.hpp"        // List threads defined


#include "OneOf.hpp"           // define one of a kind of object at runtime.
#include "IsDir.hpp"           // is file a directory?


#include <iapp.hpp>

#include "switch.hpp"

#include "pallet.hpp"


// Global statics interfacing external storage.
// we want One of these objects, but they must be initializied
// after application argc argv are set.
static OneOf<SizeStore> resizer;                 // source of ISize
static OneOf<ExternalFont> font_source;          // source of IFont s
static OneOf<ExternalWrap> wrap_source;          // source of wrap booleans.
static OneOf<ColorStore> color_source;           // source of colors


          // table of color setting functions must keep in sync with listpm.h
          // COLOR_IDM_START to IDM_COLOR_LAST table of pointers to ListFrame members
          // used to set reset colors.
          // COLOR_STR_START strings must also be in sync
          const ListFrame::listSetColor ListFrame::list_set_colors[] = {

                   &ListFrame::ListFrame::setFrameColor<&IWindow::setActiveColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setBackgroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setForegroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setDisabledBackgroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setDisabledForegroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setHiliteForegroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setHiliteBackgroundColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setInactiveColor>,
                   &ListFrame::ListFrame::setFrameColor<&IWindow::setShadowColor>,


                   &ListFrame::ListFrame::setMleColor<&IWindow::setBackgroundColor>,
                   &ListFrame::ListFrame::setMleColor<&IWindow::setForegroundColor>,

                   &ListFrame::ListFrame::setColor<&ITitle::setActiveTextBackgroundColor>,
                   &ListFrame::ListFrame::setColor<&ITitle::setActiveTextForegroundColor>,
                   &ListFrame::ListFrame::setColor<&ITitle::setInactiveTextBackgroundColor>,
                   &ListFrame::ListFrame::setColor<&ITitle::setInactiveTextForegroundColor>,

                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setActiveColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setBackgroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setForegroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setDisabledBackgroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setDisabledForegroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setHiliteForegroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setHiliteBackgroundColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setInactiveColor>,
                   &ListFrame::ListFrame::setTitlebColor<&IWindow::setShadowColor>

             };
          const ListFrame::listResetColor ListFrame::list_reset_colors[] = {

                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetActiveColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IFrameWindow::resetBackgroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetForegroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IFrameWindow::resetDisabledBackgroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetDisabledForegroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetHiliteForegroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetHiliteBackgroundColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetInactiveColor>,
                   &ListFrame::ListFrame::resetFrameColor<&IWindow::resetShadowColor>,


                   &ListFrame::ListFrame::resetMleColor<&IWindow::resetBackgroundColor>,
                   &ListFrame::ListFrame::resetMleColor<&IWindow::resetForegroundColor>,

                   &ListFrame::ListFrame::resetColor<&ITitle::resetActiveTextBackgroundColor>,
                   &ListFrame::ListFrame::resetColor<&ITitle::resetActiveTextForegroundColor>,
                   &ListFrame::ListFrame::resetColor<&ITitle::resetInactiveTextBackgroundColor>,
                   &ListFrame::ListFrame::resetColor<&ITitle::resetInactiveTextForegroundColor>,


                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetActiveColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetBackgroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetForegroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetDisabledBackgroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetDisabledForegroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetHiliteForegroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetHiliteBackgroundColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetInactiveColor>,
                   &ListFrame::ListFrame::resetTitlebColor<&IWindow::resetShadowColor>

             };
// number of table setting functions
static const int color_count = ( IDM_COLOR_LAST  - COLOR_IDM_START  + 1);


// main window consturctor.
// mostly constructed via ctor.
ListFrame::ListFrame( const IString file) :
                  file_displayed(file),                        //save title
                  // frame window from resource id & style
                  IFrameWindow( WIN_UICLLISTER ,

                      IFrameWindow::defaultStyle() |          // default style
                      IFrameWindow::shellPosition  |          // wps positions
                      IFrameWindow::accelerator    |          // accelerator
                      IFrameWindow::border         |          // and border
                      IFrameWindow::menuBar        |          // and menu
                      IFrameWindow::minimizedIcon  |          // icon for
                                                              // minimization
                      IFrameWindow::sizingBorder   |          // border sizing
                      IFrameWindow::systemMenu     |          // system menu
                      IFrameWindow::titleBar       ),         // with title bar


                  // Do not need to access the accelerator, the accelerator attribute insures that there is one (hidden)
                  //acc ( WIN_UICLLISTER , this),             // accelerator
                  info(this),                                 // info area

                  // multiline edit field
                  mle ( MLE_TEXT ,this , this, IRectangle(),
                    (IMultiLineEdit::defaultStyle()           // default style
                    | IMultiLineEdit::readOnly                // readonly
                    | IMultiLineEdit::verticalScroll          // scroll vert
                    | IMultiLineEdit::horizontalScroll ) ),   // edit client
                  title( this, STR_TITLE ),                   // title bar
                  help(ID_HELPTABLE,this),                    // attach help win
                  search_dialog( *this , help ),              // search dialog.
                  win_thread(*this,IDM_SEARCHNOT,mle,info)    // window
                                                              // threader.

{   // code for constructor
    ICommandHandler::handleEventsFor(this);       // attach command handler.
    IMenuHandler::handleEventsFor(this);          // attach menu handler.
    IResizeHandler::handleEventsFor(this);        // attach resize handler.
    IFocusHandler::handleEventsFor(this);         // focus handler.

    SizeStore& l_resizer = resizer;               // get stoed window size
    if (l_resizer.sizeExternal() )                // if an ISize is external
    {
        // get external size
        ISize external_size( l_resizer );

        ISize current_size = external_size;

        // set window to that size
        sizeTo(current_size);

        // code to insure that the window is positioned on the desk
        // top if possible.

        // get size of desktop.
        ISize desk_size = IWindow::desktopWindow()->size();

        // get position currently proposed.
        IPoint current_position = position();

        // assume current position good.
        IPoint new_position = current_position;

        if (                            // if positioning is possible!
           // right of window is off screen
           ( current_position.x() + current_size.width() >
               desk_size.width() ) &&

           // the whole window will fit on the screen
           ( current_size.width() <= desk_size.width() )
           )
         {
            // degree of freedom in positioning.
            int x_slop = desk_size.width() - current_size.width();

            // a plausible pull back from against the right wall.
            int x_pullback = desk_size.width() / 10;
            if ( x_pullback > x_slop) x_pullback = x_slop;

            // reanomize the pullback.
            x_pullback *= float(rand()) / float(RAND_MAX) ;

            // new position is against right wall pulled back by random amount.
            new_position.setX( x_slop - x_pullback );
         };

        // this code is same as above except for doing y co-ordinate.
        if (
           // bottom of window is off screen
           ( current_position.y() + current_size.height() >
              desk_size.height() ) &&

           // the whole window will fit on the screen
           ( current_size.height() <= desk_size.height() )
           )
        {
            int y_slop = desk_size.height() - current_size.height();
            int y_pullback = desk_size.height() / 10;
            if ( y_pullback > y_slop) y_pullback = y_slop;
            y_pullback *= float(rand()) / float(RAND_MAX) ;
            new_position.setY( y_slop - y_pullback );
        };

        // if we have decided to move the window, move it.
        if ( new_position != current_position) moveTo( new_position );
    };

    // if font is stored.
    ExternalFont& l_font_source = font_source;
    if ( l_font_source.FontExistsExternally() )
    {
        // then set the stored font.
        mle.setFont( l_font_source );
    };

    Boolean word_wrap = false;                  // assume no word wrap

    // if WW option stored.
    ExternalWrap& l_wrap_source = wrap_source;
    if ( l_wrap_source.wrapExternal() )
    {
         word_wrap = l_wrap_source;             // get WW option.
    };
    mle.enableWordWrap(word_wrap);              // set WW option into

    ColorStore & color_store = color_source;
    // for each title color
    for( int i = 0; i < color_count ;i++)
    {
       // get string associated with this color index
       // listpm.h STR_ must be in sync with the tables!
       IString color_label =  IApplication::current().userResourceLibrary().
                                               loadString( i + COLOR_STR_START );

       // set the key of the color store to be the found key.
       color_store.setKey( color_label );
       // if key exists!
       if ( color_store.colorExternal() )
       {
          // get stored collor, for this key.
          IColor color = color_store;
          // get the function color setter for this key
          // menu items IDs must be in sync in listpm.h
          ListFrame::listSetColor requested_color_set = ListFrame::list_set_colors[ i ];
          // set the color to the stored color.
          (this->*requested_color_set) ( color );
       };
    };



    info.setText(file);                           // set info displayed
                                                  // in info area
    info.setInactiveText(file);                   // ditto, inactive
    setClient( &mle );                            // set edit window as client

    help.addLibraries("listpm.hlp");              // library to use for help
    help.setTitle(STR_EXENAME);                   // title of help library.
    IHelpHandler::handleEventsFor(this);          // help handler.


    // if requested file is accessable
    if (file.length() != 0)
    {
      if ( access(file,04) )
      {
        // if file is not accesable, complain bitterly and leave.
        mle.setFocus();
        show();
        IMessageBox ( this )
         .show(ERR_NOTREAD,IMessageBox::information);
      }
      else
      {
        // if accessable, load into client window.
        import( file);
      };
    }
    else
    {
      while( cin.good() )
      {
        IString line = IString::lineFrom( cin );
        mle.addLineAsLast( line );
      };
    };
    setTaskList( file );

    mle.setCursorPosition ( 0 );                  // set cursor at 0,0
    mle.setFocus();                               // give edit area focus, and
    postEvent( control, GOTFOCUS );               // make edit error the focus.
    show();                                       // show.


};

bool ListFrame::gotFocus( IControlEvent& ce )
{
   // if the search field already has focus, skip.
   if ( mle.hasFocus() ) return IFocusHandler::gotFocus( ce );

   // when we get focus we post GOTFOCUS message.
   postEvent( control, GOTFOCUS );

   return IFocusHandler::gotFocus( ce );
};

void ListFrame::setTaskList( const IString& myFile )
{
       changeSwitch(
             handle() ,
             ICurrentApplication::current().currentPID() ,
             IApplication::current().userResourceLibrary().loadString( STR_TLPRE ) +
             myFile );
};

       // optional call back to user before running thread.
       // to search for text.
void  ListWindowThread::begin()
{

         OurWindow().disable();
         mle.disableUpdate();                      // freeze updates on screen

         info.setFocus();

         saved_text = info.text();

         info.setText(
          IApplication::current().userResourceLibrary().loadString(STR_SEARCH)
          + ": " + search_string);


         if ( exact )                     // depends on exact case.
            looked_for = search_string;
         else
            looked_for = IString::upperCase(search_string);
         // if not exact case then always work with upcased strings.
         // for both target text and examined text.


         if(forward)
         {
             direction_sign = direction_mult =1;
             dir_search = &I0String::indexOf;       // forward search fn
             start_pos = 0;                        // starting pos for fn.

             limit = mle.numberOfLines() - 1 ;     // last line to look at.
         }
         else
         {
             direction_sign = -1;
             direction_mult = 0;
             dir_search = &I0String::lastIndexOf;   // reverse search fn.
             start_pos = UINT_MAX - 1;             // starting pos for fn.
             limit = 0;                            // last line to look at.
         };

         // the current line we are on.
         current_line_no = mle.cursorLinePosition();

         // save the current position.
         current_pos  = mle.cursorPosition();

         // set the cursor to the begining of this line.
         mle.setCursorLinePosition( current_line_no );

         // mark beginning of this line.
         current_bol  = mle.cursorPosition();

         // put cursor position back where it was.
         mle.setCursorPosition(current_pos);
};

// code to run on other thread.  When searching for text.
void  ListWindowThread::thread()
{

         // offset of our cursor in this line.
         offset_within_current_line = current_pos - current_bol;

         // get text of current line.
         current_line = mle.text(current_line_no);


         // the fragment of line we are looking at.

         if ( forward )
         {
             // offset of the fragment within the current line
             offset = offset_within_current_line;

             // get the relevant fragment of current line = the end.
             line =
               current_line.subString( offset_within_current_line );
         }
         else
         {
             // fragment starts at begin
             offset = 0;

             // get the relevant fragment of current line = the beginning.
             line =
               current_line.subString( 0 , offset_within_current_line );
         };

         // if !exact case work with upcased line.
         if ( ! exact) line = I0String::upperCase(line);

         // the current line number. for loop.
         line_no = current_line_no;

         //  search for text.
         while ( ! line.includes( looked_for ) )
         {

            // after first look at whole line.
            offset = 0;
            if (line_no == limit) return;


            // increment line number.
            line_no += direction_sign;

            // get new line.
            line = mle.text(line_no);

            // if not exact case, work with upcased string.
            if ( ! exact ) line = I0String::upperCase(line);

         };

};

// optional call back to user after running thread. after searching for text.
void  ListWindowThread::finish()
{
     mle.enableUpdate();         // unfreeze updates.

     if (  line.includes( looked_for ) )
     {

         // go to begining of found line
         mle.setCursorLinePosition( line_no );

         // first position of the found string.
         IPair::Coord first = mle.cursorPosition() + offset +
              (line.*dir_search)(looked_for , start_pos );

         // end of the found string.
         IPair::Coord last = first +  search_string.length() - 1;



         // postion past the found string in the direction of travel.
         mle.setCursorPosition ( first + direction_mult *
                                  ( search_string.length() - 1 ) );

         // select the found string.
         // cursor will be positined at second parameter of the IRange.
         if ( forward )
            mle.selectRange( IRange(first,last) );
         else
            mle.selectRange( IRange(last,first) );
     };



     info.setText(saved_text);
     OurWindow().enable();
     mle.setFocus();
     return;


};

// Do an importFromFile after determining
// type of the file.
unsigned long ListFrame::import( const char * file)
{

   IMultiLineEdit::EOLFormat format = IMultiLineEdit::MLEFormat;

   // import the data.
   return mle.importFromFile( file , format );
};

// command handler for main window.
// this code overrides ICommandHandler, gets called
// when command event caused by user from menu, accelerator.
Boolean ListFrame::command( ICommandEvent& event )
{


   // classify the request.
   switch (  event.commandId() )
   {
    // have gotten focus
    case GOTFOCUS:
       if ( ! mle.hasFocus() )
       {
          mle.setFocus();
       };
       return false;            // not handled.
       break;

    // change font request
    // user wants to change font.
    case IDM_FONT:
       {
          // get current font
          IFont font = mle.font();
          // and store in the settings
          IFontDialog::Settings fsettings( &font );
          // set the title.
          fsettings.setTitle(STR_FONTTITLE);
          // create dialog to get the new font.
          IFontDialog dlg(IWindow::desktopWindow(),this,
                      IFontDialog::defaultStyle() |
                      IFontDialog::resetButton,
                      fsettings);
          // when the constructor returns, dialog has run

          if(dlg.pressedOK() )                 // if user said ok.
          {
             mle.setFont(font);                // store new font

             // ask user if he want to store this new font as the new default.
             // create message box.
             IMessageBox msg(this);
             if (
                  msg.show(STR_DEFAULTFONT,
                            IMessageBox::queryIcon |
                            IMessageBox::yesNoButton ) ==
                    IMessageBox::yes
                )
              {
                // store the new font.
                ExternalFont(font_source) << font;
              };
          };
          return true;                         // we have handled.
       };

    // word wrap on request.
    case IDM_ON:
       {
          // set word wrap if not already.
          if( ! mle.isWordWrap() ) mle.enableWordWrap();

          // record word wrapping;
          ExternalWrap(wrap_source) << true;
          return true;     // we have handled.
       };

    // word wrap off request.
    case IDM_OFF:
       {
          // if on turn it off
          if( mle.isWordWrap() ) mle.disableWordWrap();

          // record no word wrapping;
          ExternalWrap(wrap_source) << false;
          return true;
       };

    // open new file on new window request.
    // user wants to open a new file.
    case IDM_OPENNEW :
       {
          char fullfile[_MAX_PATH];            // space for full path name.

          // get full path of our current file
          _fullpath(fullfile,file(),sizeof(fullfile));

          // fields to break path into
          char drive[_MAX_DRIVE],dir[_MAX_DIR],fname[_MAX_FNAME],ext[_MAX_EXT];

          // break fullpath into fields
          _splitpath(fullfile,drive,dir,fname,ext);

          char usepath[_MAX_PATH];             // space for path to use.

          // create path to use, with wild card filename.
          _makepath(usepath,drive,dir,"*","");

          // declare settings.
          IFileDialog::Settings fsettings;

          // set the indicated file name to search for
          fsettings.setFileName( usepath );

          // set the ititial drive as has been discovered.
          fsettings.setInitialDrive(drive);

          // set title of the  get file name dialog.
          fsettings.setTitle(STR_TITLENEW);

          // dialog is to get an existing file.
          fsettings.setOpenDialog();

          // declare the dialog on the current frame, with the settings.
          IFileDialog dlg( IWindow::desktopWindow(), this,
                IFileDialog::defaultStyle(),
                fsettings);
          // The above call will return when the above modal dialog
          // has already run.

          // set the focus back to our edit ring.
          mle.setFocus();

          // if the dialog ran successfully.
          if ( dlg.pressedOK() )
          {
             // for each file selected by the dialog.
             for(int i=0; i< dlg.selectedFileCount() ; i++)
             {
               // start a whole new thread to handle the new frame.
               // the new ListThreadFn will be stored in a managed pointer
               // and will be destroyed when the thread exits!
               IThread list ( new ListThreadFn ( dlg.fileName(i) ) );
             };
          };
          return true;                         // Have handled.
          break;
       };

    // user wants to Open new file in the current window.
    case IDM_REPLACEFILE:
       {
          // space for the fullfile name in the dialog.
          char fullfile[_MAX_PATH];

          // create the fullpath of the current file.
          _fullpath(fullfile, file(),sizeof(fullfile));

          // space to store the parts of the current file.
          char drive[_MAX_DRIVE],dir[_MAX_DIR],fname[_MAX_FNAME],ext[_MAX_EXT];

          // split the file into parts.
          _splitpath(fullfile,drive,dir,fname,ext);

          // space to store desired file
          char usepath[_MAX_PATH];

          // make a wild card file path.
          _makepath(usepath,drive,dir,"*","");

          // settings for the dialog.
          IFileDialog::Settings fsettings;

          // set files to chose from. all
          fsettings.setFileName( usepath );

          // set the initial drive discovered.
          fsettings.setInitialDrive(drive);

          // set the title of this dialog.
          fsettings.setTitle(STR_TITLEREPLACE);

          // This is an open dialog, choose amoung existing files.
          fsettings.setOpenDialog();


          // returned OK, is it directroy name of file which is chosen.
          Boolean is_dir , ok ;
          IString chosen_file;

          do
          {
             // create the dialog.
             IFileDialog dlg( IWindow::desktopWindow(), this,
                IFileDialog::defaultStyle() ,
                fsettings);
             // constructor will return when dialog has run to completion.

             ok =  dlg.pressedOK();

             if ( ok ) chosen_file = dlg.fileName();

             is_dir =  dlg.pressedOK() && IsDir( dlg.fileName() ) ;

             if (is_dir) fsettings.setFileName( ( chosen_file + "\\*" ) );

          } while ( is_dir );

          // if the dialog ran ok.
          if ( ok )
          {

             if ( access(chosen_file,04) )             // is it accessable?
             {
                // if file is not accesable, complain bitterly and leave.
                IMessageBox ( this )
                .show(ERR_NOTREAD,IMessageBox::information);
             }
             else
             {
                mle.hide();                            // hide the client while
                mle.removeAll();                       // we remove all the
                                                       // lines.
                // import all lines from
                import( chosen_file );                 // the selected file.

                file_displayed = chosen_file;          // save current filename.
                mle.setCursorPosition ( 0 );           // set position=0,0

                info.setText( chosen_file );           // set the filename
                info.setInactiveText( chosen_file );   // in the info area.
                setTaskList( chosen_file );
                mle.show();                            // show edit area again.
             };
          };

          // set the focus new edit area
          mle.setFocus();
          return true;                                 // yes, handled.
          break;
       };

    // copy to clipboard request.     CTRL+ins
    case IDM_COPYTOCLIPBOARD :
       {


          // if no text selected, done
          if ( ! mle.hasSelectedText() ) return true;

          mle.copy();
          #if 0 // does not work and above mle copy works better.
          // get the clipboard window.
          IClipboard clipboard(event.window()->handle());

          // empty the clipboard.
          clipboard.empty();

          // move in the selected event
        //clipboard.setText(mle.selectedText());

          IString selected ( mle.selectedText() );

          int len = selected.length();

          char * selectedP = selected;

          IString selectedS( selectedP );
          int lenS = selectedS.length();

          clipboard.setText( selectedP );
          //clipboard.setData( clipboard.textFormat, selectedP, lenS);
          #endif // does not work and above mle copy works better.

          return true;  // we have handled.
          break;
       };

    // Handle  all items that display help but are not handled by help system.
    case IDM_COPYRIGHT :
    case IDM_GNUPUBLICLICENCE :
    case IDM_PROGRAMAUTHOR :
       // We require that the item number for these items must be ==
       // the panel ID # this requirement is met in resource editor
       // and ipf editor. We check that it has been met hare.
       {                                               // show help page.
          #if !( (  IDM_COPYRIGHT == Copyright ) && \
                 (  IDM_GNUPUBLICLICENCE == GNU ) && \
                 ( IDM_PROGRAMAUTHOR == Author)      )
          #error Resource ID must be equal to panel ID for app help items.
          #endif

          // display the help.
          help.show(IResourceId( event.commandId() ));
          return true;                                 // handled
          break;
       };

    // user wants search for text in the mle
    case IDM_SEARCH :

       //     get search parameters from user.

       // run the search dialog.
       search_dialog.GetToBeSearched();

       // deliberate FALLTHRU !!!!!!!

    // user wants to search for text string again.
    case IDM_AGAIN :                                   // search for text again.
       {
          // get text to search for
          IString search_string = search_dialog;

          // get exact case match parameter.
          Boolean exact = search_dialog.Exact();

          // get direction of search.
          Boolean forward = search_dialog.DirectionForward();

          // search for the text.

          win_thread.init_and_start(search_string, exact, forward);

          break;
       };


   };

  // if command ID is in color Range, color ID's must be in sync
  if ( (  event.commandId() >= COLOR_IDM_START ) && (  event.commandId() <= IDM_COLOR_LAST ) )
  {

          // get set function, reset function for this menu request.
          // list must be in sync with IDs in listpm.h
          ListFrame::listSetColor requested_color_set = list_set_colors[ event.commandId() - COLOR_IDM_START ];
          ListFrame::listResetColor requested_color_reset = list_reset_colors[ event.commandId() - COLOR_IDM_START ];

          // get string ID for this menu request.
          // listpm.h STR_ for these ID's must be in sync.
          IString color_label =  IApplication::current().userResourceLibrary().
                                               loadString(  event.commandId() - COLOR_IDM_START
                                               + COLOR_STR_START );

          // run the select color dialog.
          int frameX = 0, frameY = 0;
          IPalletDialog colorDialog(color_label,frameX,frameY, this);
          colorDialog.showModally();

          // if reset pushed reset the color
          if (colorDialog.result() == ID_RESET)
          {
             pcolor chosen_color = colorDialog.color();
             IColor ichosen_color = chosen_color;
             (this->*requested_color_reset)();

             // ask user if he want to REVERT this  COLOR TO THE NEW default.
             // create message box.
             IMessageBox msg(this);
             if (
                  msg.show(STR_REVERTQUERY,
                            IMessageBox::queryIcon |
                            IMessageBox::yesNoButton ) ==
                    IMessageBox::yes
                )
             {
                ColorStore & color_store = color_source;
                // set key to delete
                color_store.setKey( color_label );
                if ( color_store.colorExternal() ) color_store.deleteKey();
                // deleting the key means that future ListFrame windows
                // will not set a stored color!
             };
          };

          // if OK set the color.
          if (colorDialog.result() == ID_OK)
          {
             // get color chosen.
             pcolor chosen_color = colorDialog.color();
             // convert to an IColor
             IColor ichosen_color = chosen_color;
             // set this new color
             (this->*requested_color_set)(ichosen_color);

             // ask user if he want to store this new COLOR as the new default.
             // create message box.
             IMessageBox msg(this);
             if (
                  msg.show(STR_COLORSAVEQUERY,
                            IMessageBox::queryIcon |
                            IMessageBox::yesNoButton ) ==
                    IMessageBox::yes
                )
             {
                // get reference to color store.
                ColorStore & color_store = color_source;
                // set the KEY ID to store
                color_store.setKey( color_label );
                // store the new color.
                color_store << chosen_color;
             };
          };
          return true;
  };

   return ICommandHandler::command(event);
};   //  Boolean ListFrame::command( ICommandEvent& event )

//
// this handler called when the word wrap menu opened.
// overrides from IMenuHandler. called when Word wrap submenu
// is opened.
Boolean ListFrame::menuShowing ( IMenuEvent& event,ISubmenu& submenu)
{
   // if word wrap menu called.
   if (submenu.id() == IDM_WORDWRAP )
   {
      // is word wrap on?
      Boolean wrap_on = mle.isWordWrap();

      // word wrap is of is opposite.
      Boolean wrap_off = !wrap_on;

      // set items checked approapiately
      submenu.checkItem(IDM_ON,wrap_on);
      submenu.checkItem(IDM_OFF,wrap_off);
   };
   // pass on to default handler.
   return IMenuHandler::menuShowing( event, submenu);
};

// overrides IResizeHandler is called when main frame is resized.
// bug bug bug
// we should only save new size on SHIFT drag, but How do we
// distinguish this case??
// If you know the answer email pelliott@io.com

Boolean ListFrame::windowResize(IResizeEvent& evt)
{
   SizeStore(resizer) << evt.newSize();   // Save new size.
   return false;                          // we did not handle, only hooked.
};

// overides IHelpHander.
// get s called to find out context help for this window.
// tells keys help what panel to use.
Boolean ListFrame::keysHelpId(IEvent& evt)
{
   int panel = KeysHelp;                  // panel for Keys Help.
   evt.setResult( panel );                // set the panel to use.
   return true;                           // we have handled.
};

