// @(#)root/asimage:$Name:  $:$Id: TASPaletteEditor.cxx,v 1.3 2003/07/08 16:39:44 rdm Exp $
// Author: Reiner Rohlfs   24/03/2002

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  TASPaletteEditor                                                    //
//                                                                      //
//  This is a GUI window to edit a color palette.                       //
//  It is called by a pull down menu item of TASImage.                  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TASImage.h"
#include "TRootEmbeddedCanvas.h"
#include "TCanvas.h"
#include "TH1.h"
#include "TASPaletteEditor.h"
#include "TGXYLayout.h"
#include "TGButton.h"
#include "TGComboBox.h"
#include "TGFileDialog.h"
#include "TDirectory.h"
#include "TFile.h"
#include "TLine.h"

extern "C" {
#   include <afterbase.h>
#   include <afterimage.h>
}


static const char *gFileTypes[] = {
   "ROOT palette file",  "*.pal.root",
   "ASCII palette file", "*.pal.txt",
   0,                    0
};

static UShort_t gRedRainbow[12] = {
   0x0000, 0x7000, 0x0000, 0x0000, 0x0000,
   0xffff, 0xffff, 0x7000, 0x8000, 0xffff
};
static UShort_t gGreenRainbow[12] = {
   0x0000, 0x0000, 0x0000, 0xffff, 0xffff,
   0xffff, 0x0000, 0x0000, 0x8000, 0xffff
};
static UShort_t gBlueRainbow[12] = {
   0x0000, 0x7000, 0xffff, 0xffff, 0x0000,
   0x0000, 0x0000, 0x0000, 0xa000, 0xffff
};


ClassImp(TASPaletteEditor)

//______________________________________________________________________________
TASPaletteEditor::TASPaletteEditor(TAttImage *attImage, UInt_t w, UInt_t h)
   : TPaletteEditor(attImage, w, h), TGMainFrame(gClient->GetRoot(), w, h)
{
   // Palette editor constructor.
   // The palette editor aloows the editing of the color palette of the image.

   SetLayoutManager(new TGXYLayout(this));
   fHisto        = 0;
   fLimitLine[0] = 0;
   fLimitLine[1] = 0;
   fRampFactor   = 0;
   fImagePad     = gPad;

   fPaletteList = new TList;
   fPaletteList->SetOwner();

   fPalette = new TImagePalette(attImage->GetPalette());
   fPaletteList->Add(fPalette);

   // buttons
   TGTextButton *button;

   button = new TGTextButton(this, "&Apply", 1);
   button->SetToolTipText("Apply the palette to the image");
   AddFrame(button, new TGXYLayoutHints(70, 1, 8, 1.8));

   button = new TGTextButton(this, "&Ok", 2);
   button->SetToolTipText("Same as Apply and Cancel button");
   AddFrame(button, new TGXYLayoutHints(70, 3, 8, 1.8));

   button = new TGTextButton(this, "&Cancel", 3);
   button->SetToolTipText("Close this window");
   AddFrame(button, new TGXYLayoutHints(70, 5, 8, 1.8));

   button = new TGTextButton(this, "&Save", 4);
   button->SetToolTipText("Save the palette in a ROOT or an ASCII file");
   AddFrame(button, new TGXYLayoutHints(70, 7.5, 8, 1.8));

   button = new TGTextButton(this, "O&pen", 5);
   button->SetToolTipText("Read a palette from a ROOT or an ASCII file");
   AddFrame(button, new TGXYLayoutHints(70, 9.5, 8, 1.8));

   button = new TGTextButton(this, "&New", 6);
   button->SetToolTipText("Create a new palette (not yet implemented)");
   button->SetState(kButtonDisabled);
   AddFrame(button, new TGXYLayoutHints(70, 12, 8, 1.8));

   button = new TGTextButton(this, "&Edit", 7);
   button->SetToolTipText("Edit a palette (not yet implemented)");
   button->SetState(kButtonDisabled);
   AddFrame(button, new TGXYLayoutHints(70, 14, 8, 1.8));

   fAutoUpdate = new TGCheckButton(this, "Auto Update", 13);
   fAutoUpdate->SetToolTipText("Automatic update of the image (without Apply button)");
   AddFrame(fAutoUpdate, new TGXYLayoutHints(50, 1, 20, 1.8));

   fUnDoButton = new TGTextButton(this, "&Undo", 20);
   fUnDoButton->SetToolTipText("Undo the last modification (repeatable)");
   AddFrame(fUnDoButton, new TGXYLayoutHints(50, 3, 8, 1.8));

   fReDoButton = new TGTextButton(this, "&Redo", 21);
   fReDoButton->SetToolTipText("Undo the last undo operation (repeatable)");
   AddFrame(fReDoButton, new TGXYLayoutHints(60, 3, 8, 1.8));

   button = new TGTextButton(this, "&Log", 8);
   button->SetToolTipText("Apply a log operation to the anchor points of the palette");
   AddFrame(button, new TGXYLayoutHints(50, 15, 8, 1.8));

   button = new TGTextButton(this, "E&xp", 9);
   button->SetToolTipText("Apply a exp operation to the anchor points of the palette");
   AddFrame(button, new TGXYLayoutHints(50, 17, 8, 1.8));

   button = new TGTextButton(this, "L&in", 10);
   button->SetToolTipText("Make the distance of all anchor points constant");
   AddFrame(button, new TGXYLayoutHints(50, 19, 8, 1.8));

   button = new TGTextButton(this, "In&vert", 11);
   button->SetToolTipText("Invert the order of the colors");
   AddFrame(button, new TGXYLayoutHints(60, 17, 8, 1.8));

   fStepButton = new TGCheckButton(this, "Step", 12);
   fStepButton->SetToolTipText("Apply a step function to the palette");
   AddFrame(fStepButton, new TGXYLayoutHints(60, 19, 8, 1.8));

   // ramp: 1, 2 or 4
   TGGroupFrame *rampFrame = new TGGroupFrame(this, "Ramps");
   AddFrame(rampFrame, new TGXYLayoutHints(50, 8.5, 14, 5.5,
            TGXYLayoutHints::kLRubberX | TGXYLayoutHints::kLRubberY |
            TGXYLayoutHints::kLRubberH | TGXYLayoutHints::kLRubberW));

   fRamps[0] = new TGRadioButton(this, "1", 1);
   fRamps[0]->SetToolTipText("Repeat the palette once");
   AddFrame(fRamps[0], new TGXYLayoutHints(52, 10, 5, 1.8));

   fRamps[1] = new TGRadioButton(this, "2", 2);
   fRamps[1]->SetToolTipText("Repeat the palette twice");
   AddFrame(fRamps[1], new TGXYLayoutHints(52, 12, 5, 1.8));

   fRamps[2] = new TGRadioButton(this, "4", 4);
   fRamps[2]->SetToolTipText("Repeat the palette four times");
   AddFrame(fRamps[2], new TGXYLayoutHints(58, 12, 5, 1.8));

   // the histogram of the data
   fHistCanvas = new TRootEmbeddedCanvas("data hist", this, 300, 50);
   AddFrame(fHistCanvas, new TGXYLayoutHints(1, 1, 48, 20,
            TGXYLayoutHints::kLRubberW | TGXYLayoutHints::kLRubberH));

   const ASImage *image = ((TASImage*)attImage)->GetImage();
   if (image && image->alt.vector) {
      Int_t pixel;
      Double_t *data = image->alt.vector;
      Int_t numPixel = image->width * image->height;
      Int_t numBins = numPixel / 20;
      numBins = (numBins < 10) ? 10 : (numBins > 200) ? 200 : numBins;

      // get min and max value of image
      fMinValue = fMaxValue = *image->alt.vector;
      for (pixel = 1; pixel < numPixel; pixel++) {
         if (fMinValue > *(data + pixel)) fMinValue = *(data + pixel);
         if (fMaxValue < *(data + pixel)) fMaxValue = *(data + pixel);
      }

      fHisto = new TH1D("Statistics", "Pixel histogram of unzoomed image    ",
                        numBins, fMinValue, fMaxValue);
      for (pixel = 0; pixel < numPixel; pixel++)
         fHisto->Fill(*(data + pixel));

      fHisto->Draw("HIST");
      fHisto->GetXaxis()->SetLabelFont(63);
      fHisto->GetXaxis()->SetLabelSize(10);
      fHisto->GetYaxis()->SetLabelFont(63);
      fHisto->GetYaxis()->SetLabelSize(10);

      fLimitLine[0] = new LimitLine(fMinValue + fPalette->fPoints[1] * (fMaxValue - fMinValue),
                                    0, fHisto->GetMaximum(), this);
      fLimitLine[0]->Draw();
      fLimitLine[1] = new LimitLine(fMinValue + fPalette->fPoints[fPalette->fNumPoints - 2] *
                                    (fMaxValue - fMinValue), 0, fHisto->GetMaximum(), this);
      fLimitLine[1]->Draw();
   }

   // the combobox of different palettes
   fComboBox = new TGComboBox(this, 100);
   AddFrame(fComboBox, new TGXYLayoutHints(50, 6, 14, 2));

   fComboBox->AddEntry("Rainbow", 0);
   fComboBox->AddEntry("Grey", 1);
   fComboBox->AddEntry("Hot",  2);
   fComboBox->AddEntry("Cold", 3);
   fComboBox->AddEntry("Bowlerhat", 4);
   fComboBox->AddEntry("", 5);


   // the palette
   fPaletteCanvas = new TRootEmbeddedCanvas("palette", this, 300, 50);
   AddFrame(fPaletteCanvas, new TGXYLayoutHints(1, 22, 78, 2.5,
            TGXYLayoutHints::kLRubberW | TGXYLayoutHints::kLRubberY));

   fPaintPalette = new PaintPalette(&fPalette, attImage);
   fPaintPalette->Draw();

   MapSubwindows();
   Layout();

   SetWindowName("Palette Editor");
   SetIconName("Palette Editor");

   MapWindow();

   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
TASPaletteEditor::~TASPaletteEditor()
{
   // Palette editor destructor. Deletes all frames and their layout hints.

   TGFrameElement *ptr;

   // delete all frames and layout hints
   if (fList) {
      TIter next(fList);
      while ((ptr = (TGFrameElement *) next())) {
         if (ptr->fLayout)
            delete ptr->fLayout;
         if (ptr->fFrame)
            delete ptr->fFrame;
      }
   }

   delete fHisto;
   delete fPaintPalette;
   delete fLimitLine[0];
   delete fLimitLine[1];
   delete fPaletteList;
}

//______________________________________________________________________________
void TASPaletteEditor::CloseWindow()
{
   // Close editor.

   TPaletteEditor::CloseWindow();
   delete this;
}

//______________________________________________________________________________
Bool_t TASPaletteEditor::ProcessMessage(Long_t msg, Long_t param1, Long_t param2)
{
   // Process all editor mouse events

   switch (GET_MSG(msg)) {

      case kC_COMMAND:
         switch (GET_SUBMSG(msg)) {

            case kCM_COMBOBOX:
               NewPalette(param2);
               break;

            case kCM_RADIOBUTTON:
               SetRamp(param1);
               break;

            case kCM_CHECKBUTTON:
               if (param1 == 12)
                  SetStep();
               break;

            case kCM_BUTTON:
               switch (param1) {

                  case 1 :  // Apply
                     fAttImage->SetPalette(fPalette);
                     fImagePad->Modified();
                     fImagePad->Update();
                     break;

                  case 2 :  // OK
                     fAttImage->SetPalette(fPalette);
                     fImagePad->Modified();
                     fImagePad->Update();
                     CloseWindow();
                     break;

                  case 3 : // Cancel
                     CloseWindow();
                     break;

                  case 4 : // Save
                     Save();
                     break;

                  case 5 : // Open
                     Open();
                     break;

                  case 8: // log
                     LogPalette();
                     break;

                  case 9: // exp
                     ExpPalette();
                     break;

                  case 10: // lin
                     LinPalette();
                     break;

                  case 11: // invert
                     InvertPalette();
                     break;


                  case 20: // undo
                     fPalette = (TImagePalette*)(fPaletteList->Before(fPalette));
                     if (fAutoUpdate->GetState() == kButtonDown) {
                        fAttImage->SetPalette(fPalette);
                        fImagePad->Modified();
                        fImagePad->Update();
                     }
                     UpdateScreen(kTRUE);
                     break;

                  case 21: // redo
                     fPalette = (TImagePalette*)(fPaletteList->After(fPalette));
                     if (fAutoUpdate->GetState() == kButtonDown) {
                        fAttImage->SetPalette(fPalette);
                        fImagePad->Modified();
                        fImagePad->Update();
                     }
                     UpdateScreen(kTRUE);
                     break;

                  default: ;
               }
               break;

            default: ;
         }
         break;

      default: ;
   }

   return kTRUE;
}

//______________________________________________________________________________
void TASPaletteEditor::InsertNewPalette(TImagePalette *newPalette)
{
   // The newPalette is inserted in the list of palettes (fPaletteList) and
   // fPalette is set to the newPalette. Protected method,

   // first remove all palettes in the list which are behind the
   // current palette
   TObject *obj;
   while ((obj = fPaletteList->After(fPalette)) != 0)
      delete fPaletteList->Remove(obj);

   // add new palette and make it to the current palette
   fPaletteList->Add(newPalette);
   fPalette = newPalette;

   // update the image
   if (fAutoUpdate->GetState() == kButtonDown) {
      fAttImage->SetPalette(fPalette);
      fImagePad->Modified();
      fImagePad->Update();
   }
}

//______________________________________________________________________________
void TASPaletteEditor::Save()
{
   // Saves the current palette either into a ROOT file or in an ASCII file.
   // It is called by the Save - button. Protected method.

   TGFileInfo fi;
   fi.fFileTypes = gFileTypes;

   new TGFileDialog(gClient->GetRoot(), this, kFDSave, &fi);
   if (fi.fFilename == 0)
      return;

   if (strcmp(".pal.txt", fi.fFilename + strlen(fi.fFilename) - 8) == 0) {
      // write into an ASCII file
      FILE *fl = fopen(fi.fFilename, "w");
      if (!fl) return;
      fprintf(fl, "%u\n", fPalette->fNumPoints);
      for (Int_t pt = 0; pt < Int_t(fPalette->fNumPoints); pt++)
         fprintf(fl, "%10.9f %04hx %04hx %04hx %04hx\n",
                 fPalette->fPoints[pt],
                 fPalette->fColorRed[pt],
                 fPalette->fColorGreen[pt],
                 fPalette->fColorBlue[pt],
                 fPalette->fColorAlpha[pt] );
      fclose(fl);
   } else {
      // write into a ROOT file
      char fn[512];
      if (strcmp(".pal.root", fi.fFilename + strlen(fi.fFilename) - 9) != 0)
         sprintf(fn, "%s%s", fi.fFilename, ".pal.root");
      else
         strcpy(fn, fi.fFilename);
      TDirectory *dirsav = gDirectory;
      TFile *fsave = new TFile(fn, "RECREATE");
      if (!fsave->IsOpen()) {
         delete fsave;
         return;
      }

      fPalette->Write();
      fsave->Close();
      delete fsave;
      if (dirsav)
         dirsav->cd();
   }
}

//______________________________________________________________________________
void TASPaletteEditor::Open()
{
   // Opens either a ROOT file or an ASCII file and reads a palette.
   // It is called by the Open - button. Protected method.

   TGFileInfo fi;
   fi.fFileTypes = gFileTypes;

   new TGFileDialog(gClient->GetRoot(), this, kFDOpen, &fi);
   if (fi.fFilename == 0)
      return;

   TImagePalette *newPalette;

   if (strcmp(".pal.txt", fi.fFilename + strlen(fi.fFilename) - 8) == 0) {
      FILE *fl = fopen(fi.fFilename, "r");
      if (!fl) return;
      UInt_t numPoints;
      fscanf(fl, "%u\n", &numPoints);
      newPalette = new TImagePalette(numPoints);
      for (Int_t pt = 0; pt < Int_t(numPoints); pt++)
         fscanf(fl, "%lf %hx %hx %hx %hx\n",
                newPalette->fPoints + pt,
                newPalette->fColorRed + pt,
                newPalette->fColorGreen + pt,
                newPalette->fColorBlue + pt,
                newPalette->fColorAlpha + pt );
      fclose(fl);
   } else {
      // read from a ROOT file
      char fn[512];
      if (strcmp(".pal.root", fi.fFilename + strlen(fi.fFilename) - 9) != 0)
         sprintf(fn, "%s%s", fi.fFilename, ".pal.root");
      else
         strcpy(fn, fi.fFilename);
      TDirectory *dirsav = gDirectory;

      TFile *fsave = new TFile(fn, "READ");
      if (!fsave->IsOpen()) {
         delete fsave;
         return;
      }

      newPalette = (TImagePalette*)fsave->Get("TImagePalette");
      delete fsave;
      if (dirsav) dirsav->cd();
      if (!newPalette)
         return;
   }

   InsertNewPalette(newPalette);
   UpdateScreen(kTRUE);

   fComboBox->Select(5);  // empty entry
}

//______________________________________________________________________________
void TASPaletteEditor::UpdateScreen(Bool_t histoUpdate)
{
   // All widgeds of the screen are updated with the current palette.
   // Protected method.

   // update the color palette
   fPaletteCanvas->GetCanvas()->Modified();
   fPaletteCanvas->GetCanvas()->Update();

   if (histoUpdate) {
      // update the limit lines
      Double_t xPos = fMinValue + fPalette->fPoints[1] * (fMaxValue - fMinValue);
      fLimitLine[0]->SetX1(xPos);
      fLimitLine[0]->SetX2(xPos);

      xPos = fMinValue + fPalette->fPoints[fPalette->fNumPoints - 2] * (fMaxValue - fMinValue);
      fLimitLine[1]->SetX1(xPos);
      fLimitLine[1]->SetX2(xPos);

      fHistCanvas->GetCanvas()->Modified();
      fHistCanvas->GetCanvas()->Update();
   }

   // update undo / redo button
   fUnDoButton->SetState(fPalette == fPaletteList->First() ? kButtonDisabled : kButtonUp);
   fReDoButton->SetState(fPalette == fPaletteList->Last()  ? kButtonDisabled : kButtonUp);

   // test if it is a step palette
   EButtonState step = kButtonDown;

   Int_t pt;
   for (pt = 2; pt < Int_t(fPalette->fNumPoints - 2); pt += 2)
      if (TMath::Abs(fPalette->fPoints[pt] - fPalette->fPoints[pt + 1])  > 0.0001 ||
          fPalette->fColorRed[pt]   != fPalette->fColorRed[pt-1]   ||
          fPalette->fColorGreen[pt] != fPalette->fColorGreen[pt-1] ||
          fPalette->fColorBlue[pt]  != fPalette->fColorBlue[pt-1])
         step = kButtonUp;
   fStepButton->SetState(step);

   // find the ramp factor
   fRampFactor = 4;
   Int_t off = (fPalette->fNumPoints - 2) / 4;
   for (pt = 0; pt < Int_t(fPalette->fNumPoints - 2) / 4 * 3; pt++)
      if (fPalette->fColorRed[pt + 1]   != fPalette->fColorRed[pt + 1 + off]   ||
          fPalette->fColorGreen[pt + 1] != fPalette->fColorGreen[pt + 1 + off] ||
          fPalette->fColorBlue[pt + 1]  != fPalette->fColorBlue[pt + 1 + off]  ||
          fPalette->fColorAlpha[pt + 1] != fPalette->fColorAlpha[pt + 1 + off]) {
         fRampFactor = 2;
         break;
      }

   off = (fPalette->fNumPoints - 2) / 2;
   for (pt = 0; pt < Int_t(fPalette->fNumPoints - 2) / 2; pt++)
      if (fPalette->fColorRed[pt + 1]   != fPalette->fColorRed[pt + 1 + off]   ||
          fPalette->fColorGreen[pt + 1] != fPalette->fColorGreen[pt + 1 + off] ||
          fPalette->fColorBlue[pt + 1]  != fPalette->fColorBlue[pt + 1 + off]  ||
          fPalette->fColorAlpha[pt + 1] != fPalette->fColorAlpha[pt + 1 + off]) {
         fRampFactor = 1;
         break;
      }

   fRamps[0]->SetState(fRampFactor == 1 ? kButtonDown : kButtonUp);
   fRamps[1]->SetState(fRampFactor == 2 ? kButtonDown : kButtonUp);
   fRamps[2]->SetState(fRampFactor == 4 ? kButtonDown : kButtonUp);
}

//______________________________________________________________________________
void TASPaletteEditor::LogPalette()
{
   // The anchor points are rescaled by a log operation.
   // It is called by the log - button. Protected method.

   TImagePalette *newPalette = new TImagePalette(*fPalette);

   Double_t delta = fPalette->fPoints[fPalette->fNumPoints-2] - fPalette->fPoints[1];

   for (Int_t pt = 2; pt < Int_t(fPalette->fNumPoints - 2); pt++)
      newPalette->fPoints[pt] = fPalette->fPoints[1] +
         TMath::Log(fPalette->fPoints[pt] - fPalette->fPoints[1] + 1) /
         TMath::Log(delta + 1) * delta;

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::ExpPalette()
{
   // The anchor points are rescaled by a exp operation.
   // It is called by the exp - button. Protected method.

   TImagePalette *newPalette = new TImagePalette(*fPalette);

   Double_t delta = fPalette->fPoints[fPalette->fNumPoints-2] - fPalette->fPoints[1];

   for (Int_t pt = 2; pt < Int_t(fPalette->fNumPoints - 2); pt++)
      newPalette->fPoints[pt] = fPalette->fPoints[1] +
         TMath::Exp((fPalette->fPoints[pt] - fPalette->fPoints[1]) *
         TMath::Log(delta + 1) / delta) - 1;

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::LinPalette()
{
   // The anchor points are rescaled to be linar.
   // It is called by the lin - button. Protected method.

   TImagePalette *newPalette = new TImagePalette(*fPalette);

   Double_t delta = fPalette->fPoints[fPalette->fNumPoints-2] - fPalette->fPoints[1];
   if (fStepButton->GetState() == kButtonUp) {
      for (Int_t pt = 2; pt < Int_t(fPalette->fNumPoints - 2); pt++)
         newPalette->fPoints[pt] = fPalette->fPoints[1] +
            delta * (pt - 1) / (fPalette->fNumPoints - 3);
   } else {
      for (Int_t pt = 0; pt < Int_t(fPalette->fNumPoints - 4); pt += 2) {
         newPalette->fPoints[pt + 3] = fPalette->fPoints[1] + delta * (pt + 2) /
                                       (fPalette->fNumPoints - 2) ;
         newPalette->fPoints[pt + 2] = newPalette->fPoints[pt + 3];
      }
   }

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::InvertPalette()
{
   // The palette is inverted.
   // It is called by the invert - button. Protected method.

   TImagePalette *newPalette = new TImagePalette(*fPalette);

   Int_t pt;
   for (pt = 0; pt < Int_t(fPalette->fNumPoints); pt++)  {
      newPalette->fColorRed[pt]   = fPalette->fColorRed[fPalette->fNumPoints - 1 - pt];
      newPalette->fColorGreen[pt] = fPalette->fColorGreen[fPalette->fNumPoints - 1 - pt];
      newPalette->fColorBlue[pt]  = fPalette->fColorBlue[fPalette->fNumPoints - 1 - pt];
      newPalette->fColorAlpha[pt] = fPalette->fColorAlpha[fPalette->fNumPoints - 1 - pt];
   }

   for (pt = 2; pt < Int_t(fPalette->fNumPoints - 2); pt++)
      newPalette->fPoints[pt] = fPalette->fPoints[1] +
         fPalette->fPoints[fPalette->fNumPoints - 2] -
         fPalette->fPoints[fPalette->fNumPoints - 1 - pt];

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::NewPalette(Long_t id)
{
   // A new palette is created, depending on the id.
   // It is called by the combo box. Protected method.

   if (id == 5) // empty entry
      return;

   TImagePalette *newPalette;

   Double_t delta = fPalette->fPoints[fPalette->fNumPoints-2] - fPalette->fPoints[1];
   UInt_t   numPt;

   numPt = id == 0 ? 12 : 13;
   newPalette = new TImagePalette(numPt);
   Int_t pt;
   for (pt = 1; pt < Int_t(numPt - 1); pt++) {
      newPalette->fPoints[pt] = fPalette->fPoints[1] + (pt - 1) * delta / (numPt - 3);
      newPalette->fColorAlpha[pt] = 0xffff;
   }

   switch (id) {
      case 0:  // rainbow
         memcpy(newPalette->fColorRed + 1,   gRedRainbow,   12 * sizeof(UShort_t));
         memcpy(newPalette->fColorGreen + 1, gGreenRainbow, 12 * sizeof(UShort_t));
         memcpy(newPalette->fColorBlue + 1,  gBlueRainbow,  12 * sizeof(UShort_t));
         break;

      case 1:  // gray
         for (pt = 1; pt < Int_t(numPt - 1); pt++) {
            newPalette->fColorRed[pt]   = 0xffff * (pt - 1) / (numPt - 3);
            newPalette->fColorGreen[pt] = 0xffff * (pt - 1) / (numPt - 3);
            newPalette->fColorBlue[pt]  = 0xffff * (pt - 1) / (numPt - 3);
         }
         break;

      case 2:  // hot (red)
         for (pt = 1; pt < Int_t(numPt - 1) / 2; pt++) {
            newPalette->fColorRed[pt]   = 0xffff * (pt - 1) / ((numPt - 3) / 2);
            newPalette->fColorGreen[pt] = 0;
            newPalette->fColorBlue[pt]  = 0;
         }

         for (; pt < Int_t(numPt - 1); pt++) {
            newPalette->fColorRed[pt]   = 0xffff;
            newPalette->fColorGreen[pt] = 0xffff * (pt - (numPt - 1) / 2) / ((numPt - 3) / 2);
            newPalette->fColorBlue[pt]  = 0xffff * (pt - (numPt - 1) / 2) / ((numPt - 3) / 2);
         }
         break;

      case 3:  // cold (blue)
         for (pt = 1; pt < Int_t(numPt - 1) / 2; pt++) {
            newPalette->fColorRed[pt]   = 0;
            newPalette->fColorGreen[pt] = 0;
            newPalette->fColorBlue[pt]  = 0xffff * (pt - 1) / ((numPt - 3) / 2);
         }

         for (; pt < Int_t(numPt - 1); pt++) {
            newPalette->fColorRed[pt]   = 0xffff * (pt - (numPt - 1) / 2) / ((numPt - 3) / 2);
            newPalette->fColorGreen[pt] = 0xffff * (pt - (numPt - 1) / 2) / ((numPt - 3) / 2);
            newPalette->fColorBlue[pt]  = 0xffff;
         }
         break;

      case 4:  // bolwerhat
         for (pt = 1; pt < Int_t(numPt + 1) / 2; pt++) {
            newPalette->fColorRed[pt]   = newPalette->fColorRed[numPt - pt - 1]
                                        = 0xffff * (pt - 1) / ((numPt - 3) / 2);
            newPalette->fColorGreen[pt] = newPalette->fColorGreen[numPt - pt - 1]
                                        = 0xffff * (pt - 1) / ((numPt - 3) / 2);
            newPalette->fColorBlue[pt]  = newPalette->fColorBlue[numPt - pt - 1]
                                        = 0xffff * (pt - 1) / ((numPt - 3) / 2);
         }
         break;
   }

   newPalette->fPoints[0]     = 0;
   newPalette->fColorRed[0]   = newPalette->fColorRed[1];
   newPalette->fColorGreen[0] = newPalette->fColorGreen[1];
   newPalette->fColorBlue[0]  = newPalette->fColorBlue[1];
   newPalette->fColorAlpha[0] = newPalette->fColorAlpha[1];

   newPalette->fPoints[newPalette->fNumPoints-1]     = 1.0;
   newPalette->fColorRed[newPalette->fNumPoints-1]   = newPalette->fColorRed[newPalette->fNumPoints-2];
   newPalette->fColorGreen[newPalette->fNumPoints-1] = newPalette->fColorGreen[newPalette->fNumPoints-2];
   newPalette->fColorBlue[newPalette->fNumPoints-1]  = newPalette->fColorBlue[newPalette->fNumPoints-2];
   newPalette->fColorAlpha[newPalette->fNumPoints-1] = newPalette->fColorAlpha[newPalette->fNumPoints-2];

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::SetStep()
{
   // Create a step palette. This is called by the step - check button.
   // Protected method.

   TImagePalette *newPalette;

   if (fStepButton->GetState() == kButtonDown) {
      // change colors in steps
      newPalette = new TImagePalette(fPalette->fNumPoints * 2 - 2);
      Double_t fkt = (Double_t)(fPalette->fNumPoints - 3) / (fPalette->fNumPoints - 2);
      for (Int_t pt = 1; pt < Int_t(fPalette->fNumPoints - 1); pt++) {
         newPalette->fPoints[pt * 2 - 1] = fPalette->fPoints[1] + (fPalette->fPoints[pt] - fPalette->fPoints[1]) * fkt;
         newPalette->fPoints[pt * 2] = fPalette->fPoints[1] + (fPalette->fPoints[pt + 1] - fPalette->fPoints[1]) * fkt;
         newPalette->fColorRed[pt * 2 - 1]   = newPalette->fColorRed[pt * 2]   = fPalette->fColorRed[pt];
         newPalette->fColorGreen[pt * 2 - 1] = newPalette->fColorGreen[pt * 2] = fPalette->fColorGreen[pt];
         newPalette->fColorBlue[pt * 2 - 1]  = newPalette->fColorBlue[pt * 2]  = fPalette->fColorBlue[pt];
         newPalette->fColorAlpha[pt * 2 - 1] = newPalette->fColorAlpha[pt * 2] = fPalette->fColorAlpha[pt];
      }
   } else {
      // continuous change of colors
      newPalette = new TImagePalette(fPalette->fNumPoints / 2 + 1);
      Double_t fkt = (Double_t) (fPalette->fPoints[fPalette->fNumPoints - 2] - fPalette->fPoints[1]) /
                                (fPalette->fPoints[fPalette->fNumPoints - 3] - fPalette->fPoints[1]);
      for (Int_t pt = 1; pt < Int_t(newPalette->fNumPoints - 1); pt++) {
         newPalette->fPoints[pt] = fPalette->fPoints[pt * 2 -1] * fkt;
         newPalette->fColorRed[pt]   = fPalette->fColorRed[pt * 2 - 1];
         newPalette->fColorGreen[pt] = fPalette->fColorGreen[pt * 2 - 1];
         newPalette->fColorBlue[pt]  = fPalette->fColorBlue[pt * 2 - 1];
         newPalette->fColorAlpha[pt] = fPalette->fColorAlpha[pt * 2 - 1];
      }
   }

   newPalette->fPoints[0]     = fPalette->fPoints[0];
   newPalette->fColorRed[0]   = fPalette->fColorRed[0];
   newPalette->fColorGreen[0] = fPalette->fColorGreen[0];
   newPalette->fColorBlue[0]  = fPalette->fColorBlue[0];
   newPalette->fColorAlpha[0] = fPalette->fColorAlpha[0];

   newPalette->fPoints[newPalette->fNumPoints-2]     = fPalette->fPoints[fPalette->fNumPoints-2];
   newPalette->fPoints[newPalette->fNumPoints-1]     = fPalette->fPoints[fPalette->fNumPoints-1];
   newPalette->fColorRed[newPalette->fNumPoints-1]   = fPalette->fColorRed[fPalette->fNumPoints-1];
   newPalette->fColorGreen[newPalette->fNumPoints-1] = fPalette->fColorGreen[fPalette->fNumPoints-1];
   newPalette->fColorBlue[newPalette->fNumPoints-1]  = fPalette->fColorBlue[fPalette->fNumPoints-1];
   newPalette->fColorAlpha[newPalette->fNumPoints-1] = fPalette->fColorAlpha[fPalette->fNumPoints-1];

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::SetRamp(Long_t ramp)
{
   // The palette is repeated up to 4 times.
   // This is called by one of the ramp radio buttons. Protected method.

   if (ramp == fRampFactor)
      return;

   Int_t ptPerRamp = (fPalette->fNumPoints - 2) / fRampFactor;
   TImagePalette *newPalette = new TImagePalette(ptPerRamp * ramp + 2);

   Double_t delta = fPalette->fPoints[fPalette->fNumPoints-2] - fPalette->fPoints[1];
   for (Int_t rp = 0; rp < ramp; rp++) {
      for (Int_t pt = 0; pt < ptPerRamp; pt++) {
         newPalette->fPoints[1 + pt + rp * ptPerRamp] = fPalette->fPoints[1] +
              delta / ramp * rp +
              (fPalette->fPoints[1+pt] - fPalette->fPoints[1]) * fRampFactor / ramp;
         newPalette->fColorRed  [1 + pt + rp * ptPerRamp] = fPalette->fColorRed  [1 + pt];
         newPalette->fColorGreen[1 + pt + rp * ptPerRamp] = fPalette->fColorGreen[1 + pt];
         newPalette->fColorBlue [1 + pt + rp * ptPerRamp] = fPalette->fColorBlue [1 + pt];
         newPalette->fColorAlpha[1 + pt + rp * ptPerRamp] = fPalette->fColorAlpha[1 + pt];
      }
   }

   newPalette->fPoints[0]     = fPalette->fPoints[0];
   newPalette->fColorRed[0]   = fPalette->fColorRed[0];
   newPalette->fColorGreen[0] = fPalette->fColorGreen[0];
   newPalette->fColorBlue[0]  = fPalette->fColorBlue[0];
   newPalette->fColorAlpha[0] = fPalette->fColorAlpha[0];

   newPalette->fPoints[newPalette->fNumPoints-2]     = fPalette->fPoints[fPalette->fNumPoints-2];
   newPalette->fPoints[newPalette->fNumPoints-1]     = fPalette->fPoints[fPalette->fNumPoints-1];
   newPalette->fColorRed[newPalette->fNumPoints-1]   = fPalette->fColorRed[fPalette->fNumPoints-1];
   newPalette->fColorGreen[newPalette->fNumPoints-1] = fPalette->fColorGreen[fPalette->fNumPoints-1];
   newPalette->fColorBlue[newPalette->fNumPoints-1]  = fPalette->fColorBlue[fPalette->fNumPoints-1];
   newPalette->fColorAlpha[newPalette->fNumPoints-1] = fPalette->fColorAlpha[fPalette->fNumPoints-1];

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}

//______________________________________________________________________________
void TASPaletteEditor::UpdateRange()
{
   // Updates the range of the palette.
   // This is called after the blue limit lines were moved to define
   // a new range.

   if (fMaxValue == fMinValue)
      return;

   TImagePalette *newPalette = new TImagePalette(*fPalette);

   Double_t l0 = fLimitLine[0]->GetX1();
   Double_t l1 = fLimitLine[1]->GetX1();
   l0 = (l0 < fMinValue) ? fMinValue : ((l0 > fMaxValue) ?  fMaxValue : l0);
   l1 = (l1 < fMinValue) ? fMinValue : ((l1 > fMaxValue) ?  fMaxValue : l1);
   if (l0 > l1) {
      Double_t tmp = l0;
      l0 = l1;
      l1 = tmp;
   }

   Double_t oldDelta = fPalette->fPoints[fPalette->fNumPoints - 2] - fPalette->fPoints[1];
   Double_t newDelta = (l1 - l0) / (fMaxValue - fMinValue);
   Double_t newOff = (l0 - fMinValue) / (fMaxValue - fMinValue);

   if (newDelta < 0.001 || oldDelta < 0.001)
      return;

   for (Int_t pt = 1; pt < Int_t(fPalette->fNumPoints - 1); pt++)
      newPalette->fPoints[pt] = newOff +
            (fPalette->fPoints[pt] - fPalette->fPoints[1]) * newDelta / oldDelta;

   InsertNewPalette(newPalette);
   UpdateScreen(kFALSE);
}


//______________________________________________________________________________
void TASPaletteEditor::PaintPalette::Paint(Option_t *)
{
   // Actually paint the paletter.

   // get geometry of pad
   Int_t to_w = TMath::Abs(gPad->XtoPixel(gPad->GetX2()) -
                           gPad->XtoPixel(gPad->GetX1()));
   Int_t to_h = TMath::Abs(gPad->YtoPixel(gPad->GetY2()) -
                           gPad->YtoPixel(gPad->GetY1()));

   ASGradient grad;

   grad.npoints = (*fPalette)->fNumPoints - 2;
   grad.type = GRADIENT_Left2Right;
   grad.color = new ARGB32[grad.npoints];
   grad.offset = new double[grad.npoints];
   for (Int_t pt = 0; pt < grad.npoints; pt++) {
      grad.offset[pt] = ((*fPalette)->fPoints[pt + 1] - (*fPalette)->fPoints[1]) /
                        ((*fPalette)->fPoints[(*fPalette)->fNumPoints - 2] - (*fPalette)->fPoints[1]);
      grad.color[pt] = (((ARGB32)((*fPalette)->fColorBlue[pt + 1]   & 0xff00)) >>  8) |
                        (((ARGB32)((*fPalette)->fColorGreen[pt + 1] & 0xff00))      ) |
                        (((ARGB32)((*fPalette)->fColorRed[pt + 1]   & 0xff00)) <<  8) |
                        (((ARGB32)((*fPalette)->fColorAlpha[pt + 1] & 0xff00)) << 16);
   }

   ASImage * grad_im = make_gradient((ASVisual*)TASImage::GetVisual(), &grad , to_w, to_h,
                                     SCL_DO_COLOR, ASA_ASImage, 0,
                                     fAttImage->GetImageQuality());
   delete [] grad.color;
   delete [] grad.offset;

   Display *dpy = (Display*)gVirtualX->GetDisplay();
   Pixmap pxmap = asimage2pixmap((ASVisual*)TASImage::GetVisual(), DefaultRootWindow(dpy),
                                 grad_im, 0, kTRUE);
   Int_t wid = gVirtualX->AddWindow(pxmap, to_w, to_h);
   gPad->cd();
   gVirtualX->CopyPixmap(wid, 0, 0);
   gVirtualX->RemoveWindow(wid);
   gVirtualX->DeletePixmap(pxmap);
   destroy_asimage(&grad_im);
}


//______________________________________________________________________________
TASPaletteEditor::LimitLine::LimitLine(Coord_t x, Coord_t y1, Coord_t y2,
                                       TASPaletteEditor *gui)
   : TLine(x, y1, x, y2)
{
   // The blue limit line in the pixel value histogram.

   fGui = gui;
   SetLineColor(4);
   SetLineWidth(2);
}

//______________________________________________________________________________
void TASPaletteEditor::LimitLine::Paint(Option_t *option)
{
   // Paint the limit lines.

   fY1 = gPad->GetUymin();
   fY2 = gPad->GetUymax();

   TLine::Paint(option);
}

//______________________________________________________________________________
void TASPaletteEditor::LimitLine::ExecuteEvent(Int_t event,
                                               Int_t px, Int_t /*py*/)
{
   static Int_t oldX;

   switch(event) {
      case kMouseMotion:
         gPad->SetCursor(kMove);
         break;

      case kButton1Down:
         gVirtualX->SetLineColor(-1);
         TAttLine::Modify();  //Change line attributes only if necessary
         oldX = gPad->XtoAbsPixel(fX1);
         break;

      case kButton1Motion:
         gVirtualX->DrawLine(oldX, gPad->YtoPixel(fY1), oldX, gPad->YtoPixel(fY2));
         oldX = px;
         gVirtualX->DrawLine(oldX, gPad->YtoPixel(fY1), oldX, gPad->YtoPixel(fY2));
         break;

      case kButton1Up:
         gVirtualX->SetLineColor(-1);
         TAttLine::Modify();  //Change line attributes only if necessary
         fX1 = fX2 = gPad->AbsPixeltoX(oldX);
         fGui->UpdateRange();
         gPad->Modified(kTRUE);
         gPad->Update();
         break;

      default:
         break;
   }
}
