// @(#)root/gui:$Id: TGTextEditDialogs.cxx 35622 2010-09-23 08:29:15Z bellenot $
// Author: Fons Rademakers   10/7/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/
/**************************************************************************

    This source is based on Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

    Xclass95 is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGTextEditDialogs                                                    //
//                                                                      //
// This file defines several dialogs that are used by the TGTextEdit    //
// widget via its associated context popup menu.                        //
// The following dialogs are available: TGSearchDialog, TGGotoDialog    //
// and TGPrintDialog.                                                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGTextEditDialogs.h"
#include "TGButton.h"
#include "TGLabel.h"
#include "TGTextEntry.h"
#include "TGIcon.h"
#include "TGMsgBox.h"
#include "TGComboBox.h"

#include <stdlib.h>


ClassImp(TGSearchDialog)
ClassImp(TGPrintDialog)
ClassImp(TGGotoDialog)

static TString gLastSearchString;
TGSearchDialog *TGSearchDialog::fgSearchDialog = 0;

//______________________________________________________________________________
TGSearchDialog::TGSearchDialog(const TGWindow *p, const TGWindow *main,
                               UInt_t w, UInt_t h, TGSearchType *sstruct,
                               Int_t *ret_code, UInt_t options) :
     TGTransientFrame(p, main, w, h, options)
{
   // Create a search dialog box. Used to get from the user the required
   // search instructions. Ret_code is kTRUE when sstruct has been set,
   // kFALSE otherwise (like when dialog was canceled).

   if (!p && !main) {
      MakeZombie();
      return;
   }
   fRetCode = ret_code;
   fType = sstruct;

   ChangeOptions((GetOptions() & ~kVerticalFrame) | kHorizontalFrame);

   fF1 = new TGCompositeFrame(this, 60, 20, kVerticalFrame | kFixedWidth);
   fF2 = new TGCompositeFrame(this, 60, 20, kVerticalFrame);
   fF3 = new TGCompositeFrame(fF2, 60, 20, kHorizontalFrame);
   fF4 = new TGCompositeFrame(fF2, 60, 20, kHorizontalFrame);

   fSearchButton = new TGTextButton(fF1, new TGHotString("&Search"), 1);
   fCancelButton = new TGTextButton(fF1, new TGHotString("&Cancel"), 2);
   fF1->Resize(fSearchButton->GetDefaultWidth()+40, GetDefaultHeight());

   fSearchButton->Associate(this);
   fCancelButton->Associate(this);

   fL1 = new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0);
   fL2 = new TGLayoutHints(kLHintsTop | kLHintsRight | kLHintsExpandX,
                           2, 5, 0, 2);
   fL21 = new TGLayoutHints(kLHintsTop | kLHintsRight, 2, 5, 10, 0);

   fF1->AddFrame(fSearchButton, fL1);
   fF1->AddFrame(fCancelButton, fL1);

   AddFrame(fF1, fL21);

   fLSearch = new TGLabel(fF3, new TGHotString("Search &for:"));

   fBSearch = new TGTextBuffer(100);
   if (!gLastSearchString.IsNull())
      fBSearch->AddText(0, gLastSearchString.Data());
   else
      fSearchButton->SetState(kButtonDisabled);

   fCombo = new TGComboBox(fF3, "");
   fSearch = fCombo->GetTextEntry();
   fBSearch = fSearch->GetBuffer();
   fSearch->Associate(this);
   fCombo->Resize(220, fSearch->GetDefaultHeight());
   fSearch->SelectAll();

   fL5 = new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 3, 5, 0, 0);
   fL6 = new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 0, 2, 0, 0);

   fF3->AddFrame(fLSearch, fL5);
   fF3->AddFrame(fCombo, fL6);

   fG2 = new TGGroupFrame(fF4, new TGString("Direction"), kHorizontalFrame);

   fL3 = new TGLayoutHints(kLHintsTop | kLHintsRight, 2, 2, 2, 2);
   fL9 = new TGLayoutHints(kLHintsBottom | kLHintsLeft, 0, 0, 0, 0);
   fL4 = new TGLayoutHints(kLHintsBottom | kLHintsLeft, 0, 0, 5, 0);
   fL10 = new TGLayoutHints(kLHintsBottom | kLHintsRight, 0, 0, 5, 0);

   fCaseCheck = new TGCheckButton(fF4, new TGHotString("&Case sensitive"), 1);
   fCaseCheck->Associate(this);
   fF4->AddFrame(fCaseCheck, fL9);

   fDirectionRadio[0] = new TGRadioButton(fG2, new TGHotString("Forward"),  1);
   fDirectionRadio[1] = new TGRadioButton(fG2, new TGHotString("Backward"), 2);

   fG2->AddFrame(fDirectionRadio[0], fL4);
   fG2->AddFrame(fDirectionRadio[1], fL10);
   fDirectionRadio[0]->Associate(this);
   fDirectionRadio[1]->Associate(this);

   if (fType->fCaseSensitive == kFALSE)
      fCaseCheck->SetState(kButtonUp);
   else
      fCaseCheck->SetState(kButtonDown);

   if (fType->fDirection)
      fDirectionRadio[0]->SetState(kButtonDown);
   else
      fDirectionRadio[1]->SetState(kButtonDown);

   fF4->AddFrame(fG2, fL3);

   fF2->AddFrame(fF3, fL1);
   fF2->AddFrame(fF4, fL1);

   AddFrame(fF2, fL2);

   MapSubwindows();
   Resize(GetDefaultSize());
   SetEditDisabled(kEditDisable);

   CenterOnParent();

   SetWindowName("Search");
   SetIconName("Search");

   SetMWMHints(kMWMDecorAll | kMWMDecorMaximize | kMWMDecorMenu,
               kMWMFuncAll | kMWMFuncMaximize | kMWMFuncResize,
               kMWMInputModeless);

   if (fType->fClose) {
      MapWindow();
      fSearch->RequestFocus();
      fClient->WaitFor(this);
   }
}

//______________________________________________________________________________
TGSearchDialog::~TGSearchDialog()
{
   // Clean up search dialog.

   if (IsZombie()) return;
   delete fSearchButton;
   delete fCancelButton;
   delete fDirectionRadio[0]; delete fDirectionRadio[1];
   delete fCaseCheck;
   delete fCombo;
   delete fLSearch;
   delete fG2;
   delete fF1; delete fF2; delete fF3; delete fF4;
   delete fL1; delete fL2; delete fL3; delete fL4; delete fL5; delete fL6;
   delete fL21;delete fL9; delete fL10;
}

//______________________________________________________________________________
void TGSearchDialog::CloseWindow()
{
   // Close the dialog. On close the dialog will be deleted and cannot be
   // re-used.

   if (fType->fClose) {
      DeleteWindow();
   } else {
      UnmapWindow();
   }
}

//______________________________________________________________________________
void TGSearchDialog::TextEntered(const char *text)
{
   // emit signal when search text entered

   Emit("TextEntered(const char *)", text);
}

//______________________________________________________________________________
Bool_t TGSearchDialog::ProcessMessage(Long_t msg, Long_t parm1, Long_t)
{
   // Process search dialog widget messages.

   const char *string;

   switch (GET_MSG(msg)) {
      case kC_COMMAND:
         switch (GET_SUBMSG(msg)) {
            case kCM_BUTTON:
               switch (parm1) {
                  case 1:
                     string = fBSearch->GetString();
                     if (fType->fBuffer) 
                        delete [] fType->fBuffer;
                     fType->fBuffer = StrDup(string);
                     gLastSearchString = string;
                     *fRetCode = kTRUE;
                     TextEntered(string);
                     fCombo->ReturnPressed();
                     if (fType->fClose) CloseWindow();
                     break;
                  case 2:
                     *fRetCode = kFALSE;
                     CloseWindow();
                     break;
               }
               break;

            case kCM_CHECKBUTTON:
               fType->fCaseSensitive = !fType->fCaseSensitive;
               break;

            case kCM_RADIOBUTTON:
               switch (parm1) {
                  case 1:
                     fType->fDirection = kTRUE;
                     fDirectionRadio[1]->SetState(kButtonUp);
                     break;
                  case 2:
                     fType->fDirection = kFALSE;
                     fDirectionRadio[0]->SetState(kButtonUp);
                     break;
               }
               break;

            default:
               break;
         }
         break;

      case kC_TEXTENTRY:
         switch (GET_SUBMSG(msg)) {
            case kTE_TEXTCHANGED:
               string = fBSearch->GetString();
               if (strlen(string) == 0) {
                  fSearchButton->SetState(kButtonDisabled);
               } else {
                  fSearchButton->SetState(kButtonUp);
               }
               break;
            case kTE_ENTER:
               string = fBSearch->GetString();
               if (fType->fBuffer) 
                  delete [] fType->fBuffer;
               fType->fBuffer = StrDup(string);
               gLastSearchString = string;
               *fRetCode = kTRUE;
               TextEntered(string);
               if (fType->fClose) CloseWindow();
               break;
            default:
               break;
         }
         break;

      default:
         break;
   }

   return kTRUE;
}

//______________________________________________________________________________
TGSearchDialog *&TGSearchDialog::SearchDialog()
{
   // Return global search dialog.

   return fgSearchDialog;
}


//______________________________________________________________________________
TGPrintDialog::TGPrintDialog(const TGWindow *p, const TGWindow *main,
                             UInt_t w, UInt_t h, char **printerName,
                             char **printProg, Int_t *ret_code,
                             UInt_t options) :
   TGTransientFrame(p, main, w, h, options)
{
   // Create the printer dialog box. Returns kTRUE in ret_code when
   // printerName and printProg have been set and cancel was not pressed,
   // kFALSE otherwise.

   if (!p && !main) {
      MakeZombie();
      return;
   }
   fPrinter      = printerName;
   fPrintCommand = printProg;
   fRetCode      = ret_code;
   fEditDisabled = kEditDisable;

   ChangeOptions((GetOptions() & ~kVerticalFrame) | kHorizontalFrame);

   fF1 = new TGCompositeFrame(this, 60, 20, kVerticalFrame | kFixedWidth);
   fF5 = new TGCompositeFrame(this, 60, 20, kHorizontalFrame);
   fF2 = new TGCompositeFrame(fF5,  60, 20, kVerticalFrame);
   fF3 = new TGCompositeFrame(fF2,  60, 20, kHorizontalFrame);
   fF4 = new TGCompositeFrame(fF2,  60, 20, kHorizontalFrame);

   fPrintButton  = new TGTextButton(fF1, new TGHotString("&Print"), 1);
   fCancelButton = new TGTextButton(fF1, new TGHotString("&Cancel"), 2);
   fF1->Resize(fPrintButton->GetDefaultWidth()+40, GetDefaultHeight());

   fPrintButton->Associate(this);
   fCancelButton->Associate(this);

   fL1 = new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 2, 2);
   fL2 = new TGLayoutHints(kLHintsTop | kLHintsRight | kLHintsExpandX,
                           2, 5, 0, 2);
   fL3 = new TGLayoutHints(kLHintsTop | kLHintsRight, 2, 2, 4, 4);
   fL5 = new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 3, 5, 0, 0);
   fL6 = new TGLayoutHints(kLHintsRight | kLHintsCenterY, 0, 2, 0, 0);
   fL7 = new TGLayoutHints(kLHintsLeft, 10, 10, 10, 10);

   fL21 = new TGLayoutHints(kLHintsTop | kLHintsRight, 2, 5, 10, 0);

   fF1->AddFrame(fPrintButton, fL1);
   fF1->AddFrame(fCancelButton, fL1);
   AddFrame(fF1, fL3);


   fLPrintCommand = new TGLabel(fF3, new TGHotString("Print command:"));
   fBPrintCommand = new TGTextBuffer(50);
   fBPrintCommand->AddText(0, *printProg);
   fPrintCommandEntry = new TGTextEntry(fF3, fBPrintCommand);
   fPrintCommandEntry->Associate(this);
   fPrintCommandEntry->Resize(100, fPrintCommandEntry->GetDefaultHeight());

   fF3->AddFrame(fLPrintCommand, fL5);
   fF3->AddFrame(fPrintCommandEntry, fL6);

   fLPrinter = new TGLabel(fF4, new TGHotString("Printer:"));
   fBPrinter = new TGTextBuffer(20);
   fBPrinter->AddText(0, *printerName);
   fPrinterEntry = new TGTextEntry(fF4, fBPrinter);
   fPrinterEntry->Associate(this);
   fPrinterEntry->Resize(100, fPrinterEntry->GetDefaultHeight());
   fF4->AddFrame(fLPrinter, fL5);
   fF4->AddFrame(fPrinterEntry, fL6);

   fF2->AddFrame(fF3, fL1);
   fF2->AddFrame(fF4, fL1);

   const TGPicture *printerPicture = fClient->GetPicture("printer_s.xpm");
   if (!printerPicture) {
      Error("TGPrintDialog", "printer_s.xpm not found");
      fPrinterIcon = 0;
   } else {
      fPrinterIcon = new TGIcon(fF5, printerPicture, 32, 32);
      fF5->AddFrame(fPrinterIcon, fL7);
   }
   fF5->AddFrame(fF2, fL1);
   AddFrame(fF5, fL1);

   MapSubwindows();
   Resize(GetDefaultSize());

   CenterOnParent();

   SetWindowName("Print");
   SetIconName("Print");

   SetMWMHints(kMWMDecorAll | kMWMDecorMaximize | kMWMDecorMenu,
               kMWMFuncAll | kMWMFuncMaximize | kMWMFuncResize,
               kMWMInputModeless);

   fPrinterEntry->RequestFocus();
   MapWindow();
   fClient->WaitFor(this);
}

//______________________________________________________________________________
TGPrintDialog::~TGPrintDialog()
{
   // Clean up print dialog.

   if (IsZombie()) return;
   delete fPrinterIcon;
   delete fPrintButton;
   delete fCancelButton;
   delete fPrinterEntry;       // deletes also fBPrinter
   delete fPrintCommandEntry;  // deletes also fBPrintCommand
   delete fLPrinter; delete fLPrintCommand;
   delete fF1; delete fF2; delete fF3; delete fF4; delete fF5;
   delete fL1; delete fL2; delete fL3; delete fL5; delete fL6; delete fL7;
   delete fL21;
}

//______________________________________________________________________________
void TGPrintDialog::CloseWindow()
{
   // Close the dialog. On close the dialog will be deleted and cannot be
   // re-used.

   DeleteWindow();
}

//______________________________________________________________________________
Bool_t TGPrintDialog::ProcessMessage(Long_t msg, Long_t parm1, Long_t)
{
   // Process print dialog widget messages.

   const char *string, *txt;

   switch (GET_MSG(msg)) {
      case kC_COMMAND:
         switch (GET_SUBMSG(msg)) {
            case kCM_BUTTON:
               switch (parm1) {
                  case 1:
                     *fRetCode = kTRUE;
                     string = fBPrinter->GetString();
                     delete [] *fPrinter;
                     *fPrinter = new char[strlen(string)+1];
                     strlcpy(*fPrinter, string, strlen(string)+1);

                     string = fBPrintCommand->GetString();
                     delete [] *fPrintCommand;
                     *fPrintCommand = new char[strlen(string)+1];
                     strlcpy(*fPrintCommand, string, strlen(string)+1);

                     if (fBPrintCommand->GetTextLength() == 0) {
                        txt = "Please provide print command or use \"Cancel\"";
                        new TGMsgBox(fClient->GetRoot(), GetMainFrame(),
                                     "Missing Print Parameters", txt, kMBIconExclamation,
                                      kMBOk);
                        return kTRUE;
                     }
                     CloseWindow();
                     break;
                  case 2:
                     *fRetCode = kFALSE;
                     CloseWindow();
                     break;
               }
               break;
         }
         break;

      default:
         break;
   }

   return kTRUE;
}

//______________________________________________________________________________
TGGotoDialog::TGGotoDialog(const TGWindow *p, const TGWindow *main,
                           UInt_t w, UInt_t h, Long_t *ret_code,
                           UInt_t options) :
   TGTransientFrame(p, main, w, h, options)
{
   // Create a dialog to GoTo a specific line number. Returns -1 in
   // ret_code in case no valid line number was given or in case
   // cancel was pressed. If on input *ret_code is > 0 then this value
   // will be used as default value.

   if (!p && !main) {
      MakeZombie();
      return;
   }
   fRetCode = ret_code;
   fEditDisabled = kEditDisable;

   ChangeOptions((GetOptions() & ~kVerticalFrame) | kHorizontalFrame);

   fF1 = new TGCompositeFrame(this, 60, 20, kVerticalFrame | kFixedWidth);
   fF2 = new TGCompositeFrame(this, 60, 20, kHorizontalFrame);

   fGotoButton = new TGTextButton(fF1, new TGHotString("&Goto"), 1);
   fCancelButton = new TGTextButton(fF1, new TGHotString("&Cancel"), 2);
   fF1->Resize(fGotoButton->GetDefaultWidth()+40, GetDefaultHeight());

   fGotoButton->Associate(this);
   fCancelButton->Associate(this);

   fL1 = new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0);
   fL21 = new TGLayoutHints(kLHintsTop | kLHintsRight, 2, 5, 10, 0);

   fF1->AddFrame(fGotoButton, fL1);
   fF1->AddFrame(fCancelButton, fL1);
   AddFrame(fF1, fL21);

   fLGoTo = new TGLabel(fF2, new TGHotString("&Goto Line:"));

   fBGoTo = new TGTextBuffer(50);
   if (*fRetCode > 0) {
      char curline[32];
      snprintf(curline, 32, "%ld", *fRetCode);
      fBGoTo->AddText(0, curline);
   } else
      fGotoButton->SetState(kButtonDisabled);
   fGoTo = new TGTextEntry(fF2, fBGoTo);
   fGoTo->Associate(this);
   fGoTo->Resize(220, fGoTo->GetDefaultHeight());
   fGoTo->SelectAll();

   fL5 = new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 3, 5, 0, 0);
   fL6 = new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 0, 2, 0, 0);

   fF2->AddFrame(fLGoTo, fL5);
   fF2->AddFrame(fGoTo, fL5);
   AddFrame(fF2, fL1);

   MapSubwindows();
   Resize(GetDefaultSize());

   CenterOnParent();

   SetWindowName("Goto");
   SetIconName("Print");

   SetMWMHints(kMWMDecorAll | kMWMDecorMaximize | kMWMDecorMenu,
               kMWMFuncAll | kMWMFuncMaximize | kMWMFuncResize,
               kMWMInputModeless);

   MapWindow();
   fGoTo->RequestFocus();
   fClient->WaitFor(this);
}

//______________________________________________________________________________
TGGotoDialog::~TGGotoDialog()
{
   // Clean up goto dialog

   if (IsZombie()) return;
   delete fGotoButton;
   delete fCancelButton;
   delete fGoTo;
   delete fLGoTo;
   delete fF1; delete fF2;
   delete fL1; delete fL5; delete fL6; delete fL21;
}

//______________________________________________________________________________
void TGGotoDialog::CloseWindow()
{
   // Close the dialog. On close the dialog will be deleted and cannot be
   // re-used.

   DeleteWindow();
}

//______________________________________________________________________________
Bool_t TGGotoDialog::ProcessMessage(Long_t msg, Long_t parm1, Long_t)
{
   // Process goto dialog widget messages.

   const char *string;

   switch (GET_MSG(msg)) {
      case kC_COMMAND:
         switch (GET_SUBMSG(msg)) {
            case kCM_BUTTON:
               switch (parm1) {
                  case 1:
                     string = fBGoTo->GetString();
                     *fRetCode = (Long_t) atof(string);
                     CloseWindow();
                     break;
                  case 2:
                     *fRetCode = -1;
                     CloseWindow();
                     break;
               }
               break;

            default:
               break;
         }
         break;

      case kC_TEXTENTRY:
         switch (GET_SUBMSG(msg)) {
            case kTE_TEXTCHANGED:
               string = fBGoTo->GetString();
               if (strlen(string) == 0)
                  fGotoButton->SetState(kButtonDisabled);
               else
                  fGotoButton->SetState(kButtonUp);
               break;
            case kTE_ENTER:
               string = fBGoTo->GetString();
               *fRetCode = (Long_t) atof(string);
               CloseWindow();
               break;
            default:
               break;
         }
         break;

      default:
         break;
   }

   return kTRUE;
}

