    // @(#)root/gui:$Name:  $:$Id: TGPicture.cxx,v 1.10 2004/07/09 12:47:02 brun Exp $
    // Author: Fons Rademakers   01/01/98

    /*************************************************************************
     * 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.

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

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TGPicture & TGPicturePool                                            //
    //                                                                      //
    // The TGPicture class implements pictures and icons used in the        //
    // different GUI elements and widgets. The TGPicturePool class          //
    // implements a TGPicture cache. TGPictures are created, managed and    //
    // destroyed by the TGPicturePool.                                      //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TGPicture.h"
    #include "TGResourcePool.h"
    #include "THashTable.h"
    #include "TSystem.h"
    #include "TGWindow.h"
    #include "TVirtualX.h"

    TGGC *TGSelectedPicture::fgSelectedGC = 0;

    ClassImp(TGPicture)
    ClassImp(TGSelectedPicture)
    ClassImp(TGPicturePool)

    //______________________________________________________________________________
    const TGPicture *TGPicturePool::GetPicture(const char *name)
    {
       // Get a picture from the picture pool. Picture must be freed using
       // TGPicturePool::FreePicture(). If picture is not found 0 is returned.

       if (!fPicList)
          fPicList = new THashTable(50);

       TString pname = name;

       if (pname.EndsWith(".xpm")) {
          pname = gSystem->ExpandPathName(gSystem->UnixPathName(pname.Data()));
       }
       TGPicture *pic;

       pic = (TGPicture *)fPicList->FindObject(pname.Data());
       if (pic && !pic->IsScaled()) {
          if (pic->fPic == kNone)
             return 0;
          pic->AddReference();
          return pic;
       }

       pic = new TGPicture(pname.Data());
       pic->fAttributes.fColormap  = fClient->GetDefaultColormap();
       pic->fAttributes.fCloseness = 40000; // Allow for "similar" colors
       pic->fAttributes.fMask      = kPASize | kPAColormap | kPACloseness;

       char *picnam = gSystem->Which(fPath.Data(), pname.Data(), kReadPermission);
       if (!picnam) {
          fPicList->Add(pic);
          return 0;
       }

       if (gVirtualX->CreatePictureFromFile(fClient->GetDefaultRoot()->GetId(), picnam,
                                        pic->fPic, pic->fMask,
                                        pic->fAttributes)) {
          fPicList->Add(pic);
       } else {
          delete pic;
          pic = new TGPicture(name);
          fPicList->Add(pic);
          pic = 0;
       }

       delete [] picnam;

       return pic;
    }

    //______________________________________________________________________________
    const TGPicture *TGPicturePool::GetPicture(const char *name,
                                               UInt_t new_width, UInt_t new_height)
    {
       // Get picture with specified size from pool (picture will be scaled if
       // necessary). Picture must be freed using TGPicturePool::FreePicture(). If
       // picture is not found 0 is returned.

       if (!fPicList)
          fPicList = new THashTable(50);

       TString pname = name;

       if (pname.EndsWith(".xpm")) {
          pname = gSystem->ExpandPathName(gSystem->UnixPathName(pname.Data()));
       }

       TGPicture *pic;

       const char *hname = TGPicture::HashName(pname.Data(), new_width, new_height);
       pic = (TGPicture *)fPicList->FindObject(hname);
       if (pic && pic->GetWidth() == new_width && pic->GetHeight() == new_height) {
          if (pic->fPic == kNone)
             return 0;
          pic->AddReference();
          return pic;
       }

       pic = new TGPicture(hname, kTRUE);
       pic->fAttributes.fColormap  = fClient->GetDefaultColormap();
       pic->fAttributes.fCloseness = 40000; // Allow for "similar" colors
       pic->fAttributes.fMask      = kPASize | kPAColormap | kPACloseness;

       char *picnam = gSystem->Which(fPath.Data(), pname.Data(), kReadPermission);
       if (!picnam) {
          pic->fAttributes.fWidth  = new_width;
          pic->fAttributes.fHeight = new_height;
          fPicList->Add(pic);
          return 0;
       }

       Bool_t retc = kFALSE;
       if (!gVirtualX->InheritsFrom("TGX11")) {
          // case of win32gdk and qt drivers
          retc = gVirtualX->CreatePictureFromFile(fClient->GetDefaultRoot()->GetId(),
                                                  picnam, pic->fPic, pic->fMask,
                                                  pic->fAttributes);
          delete [] picnam;
       } else {
          // case supported by X11 driver
          char **data;
          if (!gVirtualX->ReadPictureDataFromFile(picnam, &data)) {
             delete pic;
             delete [] picnam;
             return 0;
          }
          delete [] picnam;

          Int_t    colors, chars, headersize, totalheight;
          UInt_t   width, height;
          Double_t xscale, yscale;

          sscanf(data[0], "%u %u %d %d", &width, &height, &colors, &chars);
          headersize = colors + 1;
          yscale = (Double_t) new_height / (Double_t) height;
          xscale = (Double_t) new_width / (Double_t) width;
          totalheight = (colors + new_height + 1);

          if ((width != new_width) || (height != new_height)) {
             char **smalldata;
             Int_t    i, x1, y1, pixels;
             Double_t x, y;

             smalldata = new char* [totalheight + 1];

             smalldata[0] = new char[30];
             for (i = 1; i < headersize; i++)
                smalldata[i] = new char [strlen(data[i]) + 1];

             for (i = headersize; i < totalheight + 1; i++)
                smalldata[i] = new char[(new_width * chars) + 1];

             sprintf(smalldata[0], "%u %u %d %d", new_width, new_height, colors, chars);

             for (i = 1; i < headersize; i++) strcpy(smalldata[i], data[i]);

             y = headersize;
             for (y1 = headersize; y1 < (Int_t)new_height + headersize; y1++) {
                x = 0;
                for (x1 = 0; x1 < (Int_t)new_width; x1++) {
                   for (pixels = 0; pixels < chars; pixels++)
                      smalldata[y1][x1+pixels] = data[(Int_t)y][(Int_t)x + pixels];
                   x += 1.0 / xscale;
                }
                smalldata[y1][x1] = '\0';
                y += 1.0 / yscale;
             }

             retc = gVirtualX->CreatePictureFromData(fClient->GetDefaultRoot()->GetId(), smalldata,
                                                     pic->fPic, pic->fMask,
                                                     pic->fAttributes);

             for (i = 0; i < totalheight + 1; i++)
                delete [] smalldata[i];
             delete [] smalldata;

          } else {
             retc = gVirtualX->CreatePictureFromData(fClient->GetDefaultRoot()->GetId(), data,
                                                     pic->fPic, pic->fMask,
                                                     pic->fAttributes);
          }

          gVirtualX->DeletePictureData(data);
       }

       if (!retc) {
          delete pic;
          return 0;
       }

       fPicList->Add(pic);
       return pic;
    }

    //______________________________________________________________________________
    void TGPicturePool::FreePicture(const TGPicture *fpic)
    {
       // Remove picture from cache if nobody is using it anymore.

       if (!fPicList) return;

       TGPicture *pic = (TGPicture *)fPicList->FindObject(fpic);
       if (pic) {
          if (pic->RemoveReference() == 0) {
             fPicList->Remove(pic);
             delete pic;
          }
       }
    }

    //______________________________________________________________________________
    TGPicturePool::~TGPicturePool()
    {
       // Delete picture cache.

       if (fPicList) {
          fPicList->Delete();
          delete fPicList;
       }
    }

    //______________________________________________________________________________
    void TGPicturePool::Print(Option_t *) const
    {
       // List all pictures in the pool.

       if (fPicList)
          fPicList->Print();
       else
          Info("Print", "no pictures in picture pool");
    }

    //______________________________________________________________________________
     void TGPicture::Draw(Handle_t id, GContext_t gc, Int_t x, Int_t y) const
    {
       // Draw a picture.

       GCValues_t gcv;

       gcv.fMask = kGCClipMask | kGCClipXOrigin | kGCClipYOrigin;
       gcv.fClipMask = fMask;
       gcv.fClipXOrigin = x;
       gcv.fClipYOrigin = y;
       gVirtualX->ChangeGC(gc, &gcv);
       gVirtualX->CopyArea(fPic, id, gc, 0, 0, fAttributes.fWidth, fAttributes.fHeight,
                      x, y);
       gcv.fMask = kGCClipMask;
       gcv.fClipMask = kNone;
       gVirtualX->ChangeGC(gc, &gcv);
    }

    //______________________________________________________________________________
     TGPicture::~TGPicture()
    {
       // Delete picture object.

       if (fPic != kNone)
          gVirtualX->DeletePixmap(fPic);
       if (fMask != kNone)
          gVirtualX->DeletePixmap(fMask);
       if (fAttributes.fPixels)
          delete [] fAttributes.fPixels;
    }

    //______________________________________________________________________________
     const char *TGPicture::HashName(const char *name, Int_t width, Int_t height)
    {
       // Static function returning a unique name used to look up a picture.
       // The unique name has the form "name__widthxheight".

       static char hashName[256];

       sprintf(hashName, "%s__%dx%d", name, width, height);

       return hashName;
    }

    //______________________________________________________________________________
     void TGPicture::Print(Option_t *) const
    {
       // Print picture info.

       Printf("TGPicture: %s,%sref cnt = %u", GetName(),
              fScaled ? " scaled, " : " ", References());
    }

    //______________________________________________________________________________
    TGSelectedPicture::TGSelectedPicture(const TGClient *client, const TGPicture *p) :
       TGPicture("")
    {
       // Create a "selected" looking picture based on the original TGPicture.

       GCValues_t gcv;
       UInt_t     w, h;

       fClient = client;
       Window_t root  = fClient->GetDefaultRoot()->GetId();

       w = p->GetWidth();
       h = p->GetHeight();

       fPic  = gVirtualX->CreatePixmap(root, w, h);
       fMask = p->GetMask();

       fAttributes.fWidth  = w;
       fAttributes.fHeight = h;

       gVirtualX->CopyArea(p->GetPicture(), fPic, GetSelectedGC()(), 0, 0, w, h, 0, 0);

       gcv.fMask = kGCClipMask | kGCClipXOrigin | kGCClipYOrigin;
       gcv.fClipMask = p->GetMask();
       gcv.fClipXOrigin = 0;
       gcv.fClipYOrigin = 0;
       GetSelectedGC().SetAttributes(&gcv);

       gVirtualX->FillRectangle(fPic, GetSelectedGC()(), 0, 0, w, h);

       GetSelectedGC().SetClipMask(kNone);
    }

    //______________________________________________________________________________
    TGSelectedPicture::~TGSelectedPicture()
    {
       // Delete selected picture.

       // fMask was borrowed so should not be deleted by ~TGPicture.
       fMask = kNone;
    }

    //______________________________________________________________________________
    TGGC &TGSelectedPicture::GetSelectedGC()
    {
       if (!fgSelectedGC) {
          fgSelectedGC = new TGGC(*gClient->GetResourcePool()->GetFrameGC());
          fgSelectedGC->SetForeground(gClient->GetResourcePool()->GetSelectedBgndColor());
          fgSelectedGC->SetBackground(gClient->GetResourcePool()->GetBlackColor());
          fgSelectedGC->SetFillStyle(kFillStippled);
          fgSelectedGC->SetStipple(gClient->GetResourcePool()->GetCheckeredBitmap());
       }
       return *fgSelectedGC;
    }