    // @(#)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, "%un", fPalette->fNumPoints);
          for (Int_t pt = 0; pt < Int_t(fPalette->fNumPoints); pt++)
             fprintf(fl, "%10.9f %04hx %04hx %04hx %04hxn",
                     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, "%un", &numPoints);
          newPalette = new TImagePalette(numPoints);
          for (Int_t pt = 0; pt < Int_t(numPoints); pt++)
             fscanf(fl, "%lf %hx %hx %hx %hxn",
                    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;
       }
    }