    // @(#)root/gui:$Name:  $:$Id: TGGC.cxx,v 1.8 2004/04/06 21:24:06 rdm Exp $
    // Author: Fons Rademakers   20/9/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.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TGGC and TGGCPool                                                    //
    //                                                                      //
    // Encapsulate a graphics context used in the low level graphics.       //
    // TGGCPool provides a pool of graphics contexts.                       //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TGClient.h"
    #include "TGGC.h"
    #include "TVirtualX.h"
    #include "THashTable.h"
    #include "TColor.h"
    #include "TROOT.h"
    #include "Riostream.h"
    #include <string.h>

    ClassImp(TGGC)

    //______________________________________________________________________________
     TGGC::TGGC(GCValues_t *values, Bool_t)
    {
       // Create a graphics context (only called via TGGCPool::GetGC()).

       if (values) {
          fValues = *values;
          fContext = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), values);
          if (values->fMask & kGCDashList) {
             if (values->fDashLen > (Int_t)sizeof(fValues.fDashes))
                Warning("TGGC", "dash list can have only up to %d elements",
                        sizeof(fValues.fDashes));
             fValues.fDashLen = TMath::Min(values->fDashLen, (Int_t)sizeof(fValues.fDashes));
             gVirtualX->SetDashes(fContext, fValues.fDashOffset, fValues.fDashes,
                                  fValues.fDashLen);
          }
       } else {
          memset(&fValues, 0, sizeof(GCValues_t));
          fContext = 0;
       }
       SetRefCount(1);
    }

    //______________________________________________________________________________
     TGGC::TGGC(GCValues_t *values)
    {
       // Create a graphics context, registers GC in GCPool.

       // case of default ctor at program startup before gClient exists
       if (!values) {
          memset(&fValues, 0, sizeof(GCValues_t));
          fContext = 0;
          SetRefCount(1);
          return;
       }

       if (gClient)
          gClient->GetGC(values, kTRUE);
       else {
          fContext = 0;
          Error("TGGC", "TGClient not yet initialized, should never happen");
       }
    }

    //______________________________________________________________________________
     TGGC::TGGC(const TGGC &g) : TObject(g), TRefCnt()
    {
       // Copy a graphics context.

       fValues = g.fValues;
       if (g.fContext) {
          fContext = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &fValues);
          if (fValues.fMask & kGCDashList)
             gVirtualX->SetDashes(fContext, fValues.fDashOffset, fValues.fDashes,
                                  fValues.fDashLen);
       } else
          fContext = 0;
       SetRefCount(1);

       if (gClient)
          gClient->GetGCPool()->fList->Add(this);
    }

    //______________________________________________________________________________
     TGGC::~TGGC()
    {
       // Delete graphics context.

       if (gClient)
          gClient->GetGCPool()->ForceFreeGC(this);

       if (fContext)
          gVirtualX->DeleteGC(fContext);
    }

    //______________________________________________________________________________
    TGGC &TGGC::operator=(const TGGC &rhs)
    {
       // Graphics context assignment operator.

       if (this != &rhs) {
          if (!fContext && gClient) {
             TGGC *gc = gClient->GetGCPool()->FindGC(this);
             if (!gc)
                gClient->GetGCPool()->fList->Add(this);
          }
          if (fContext)
             gVirtualX->DeleteGC(fContext);
          TObject::operator=(rhs);
          fValues  = rhs.fValues;
          fContext = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &fValues);
          if (fValues.fMask & kGCDashList)
             gVirtualX->SetDashes(fContext, fValues.fDashOffset, fValues.fDashes,
                                  fValues.fDashLen);
       }
       return *this;
    }

    //______________________________________________________________________________
    GContext_t TGGC::operator()() const
    {
       // Not inline due to a bug in g++ 2.96 20000731 (Red Hat Linux 7.0).

       return fContext;
    }

    //______________________________________________________________________________
     void TGGC::UpdateValues(GCValues_t *values)
    {
       // Update values + mask.

       fValues.fMask |= values->fMask;

       for (Mask_t bit = 1; bit <= fValues.fMask; bit <<= 1) {
          switch (bit & values->fMask) {
             default:
             case 0:
                continue;
             case kGCFunction:
                fValues.fFunction = values->fFunction;
                break;
             case kGCPlaneMask:
                fValues.fPlaneMask = values->fPlaneMask;
                break;
             case kGCForeground:
                fValues.fForeground = values->fForeground;
                break;
             case kGCBackground:
                fValues.fBackground = values->fBackground;
                break;
             case kGCLineWidth:
                fValues.fLineWidth = values->fLineWidth;
                break;
             case kGCLineStyle:
                fValues.fLineStyle = values->fLineStyle;
                break;
             case kGCCapStyle:
                fValues.fCapStyle = values->fCapStyle;
                break;
             case kGCJoinStyle:
                fValues.fJoinStyle = values->fJoinStyle;
                break;
             case kGCFillStyle:
                fValues.fFillStyle = values->fFillStyle;
                break;
             case kGCFillRule:
                fValues.fFillRule = values->fFillRule;
                break;
             case kGCTile:
                fValues.fTile = values->fTile;
                break;
             case kGCStipple:
                fValues.fStipple = values->fStipple;
                break;
             case kGCTileStipXOrigin:
                fValues.fTsXOrigin = values->fTsXOrigin;
                break;
             case kGCTileStipYOrigin:
                fValues.fTsYOrigin = values->fTsYOrigin;
                break;
             case kGCFont:
                fValues.fFont = values->fFont;
                break;
             case kGCSubwindowMode:
                fValues.fSubwindowMode = values->fSubwindowMode;
                break;
             case kGCGraphicsExposures:
                fValues.fGraphicsExposures = values->fGraphicsExposures;
                break;
             case kGCClipXOrigin:
                fValues.fClipXOrigin = values->fClipXOrigin;
                break;
             case kGCClipYOrigin:
                fValues.fClipYOrigin = values->fClipYOrigin;
                break;
             case kGCClipMask:
                fValues.fClipMask = values->fClipMask;
                break;
             case kGCDashOffset:
                fValues.fDashOffset = values->fDashOffset;
                break;
             case kGCDashList:
                if (values->fDashLen > (Int_t)sizeof(fValues.fDashes))
                   Warning("UpdateValues", "dash list can have only up to %d elements",
                           sizeof(fValues.fDashes));
                fValues.fDashLen = TMath::Min(values->fDashLen, (Int_t)sizeof(fValues.fDashes));
                memcpy(fValues.fDashes, values->fDashes, fValues.fDashLen);
                break;
             case kGCArcMode:
                fValues.fArcMode = values->fArcMode;
                break;
          }
       }
    }

    //______________________________________________________________________________
     void TGGC::SetAttributes(GCValues_t *values)
    {
       // Set attributes as specified in the values structure.

       if (!fContext && gClient) {
          TGGC *gc = gClient->GetGCPool()->FindGC(this);
          if (!gc)
             gClient->GetGCPool()->fList->Add(this);
       }

       if (fContext)
          gVirtualX->ChangeGC(fContext, values);
       else
          fContext = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), values);
       UpdateValues(values);
       if (values->fMask & kGCDashList)
          gVirtualX->SetDashes(fContext, fValues.fDashOffset, fValues.fDashes,
                               fValues.fDashLen);
    }

    //______________________________________________________________________________
     void TGGC::SetFunction(EGraphicsFunction v)
    {
       // Set graphics context drawing function.

       GCValues_t values;
       values.fFunction = v;
       values.fMask     = kGCFunction;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetPlaneMask(ULong_t v)
    {
       // Set plane mask.

       GCValues_t values;
       values.fPlaneMask = v;
       values.fMask      = kGCPlaneMask;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetForeground(ULong_t v)
    {
       // Set foreground color.

       GCValues_t values;
       values.fForeground = v;
       values.fMask       = kGCForeground;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetBackground(ULong_t v)
    {
       // Set background color.

       GCValues_t values;
       values.fBackground = v;
       values.fMask       = kGCBackground;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetLineWidth(Int_t v)
    {
       // Set line width.

       GCValues_t values;
       values.fLineWidth = v;
       values.fMask      = kGCLineWidth;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetLineStyle(Int_t v)
    {
       // Set line style (kLineSolid, kLineOnOffDash, kLineDoubleDash).

       GCValues_t values;
       values.fLineStyle = v;
       values.fMask      = kGCLineStyle;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetCapStyle(Int_t v)
    {
       // Set cap style (kCapNotLast, kCapButt, kCapRound, kCapProjecting).

       GCValues_t values;
       values.fCapStyle = v;
       values.fMask     = kGCCapStyle;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetJoinStyle(Int_t v)
    {
       // Set line join style (kJoinMiter, kJoinRound, kJoinBevel).

       GCValues_t values;
       values.fJoinStyle = v;
       values.fMask      = kGCJoinStyle;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetFillStyle(Int_t v)
    {
       // Set fill style (kFillSolid, kFillTiled, kFillStippled,
       // kFillOpaeueStippled).

       GCValues_t values;
       values.fFillStyle = v;
       values.fMask      = kGCFillStyle;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetFillRule(Int_t v)
    {
       // Set fill rule (kEvenOddRule, kWindingRule).

       GCValues_t values;
       values.fFillRule = v;
       values.fMask     = kGCFillRule;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetTile(Pixmap_t v)
    {
       // Set tile pixmap for tiling operations.

       GCValues_t values;
       values.fTile = v;
       values.fMask = kGCTile;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetStipple(Pixmap_t v)
    {
       // Set 1 plane pixmap for stippling.

       GCValues_t values;
       values.fStipple = v;
       values.fMask    = kGCStipple;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetTileStipXOrigin(Int_t v)
    {
       // X offset for tile or stipple operations.

       GCValues_t values;
       values.fTsXOrigin = v;
       values.fMask      = kGCTileStipXOrigin;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetTileStipYOrigin(Int_t v)
    {
       // Y offset for tile or stipple operations.

       GCValues_t values;
       values.fTsYOrigin = v;
       values.fMask      = kGCTileStipYOrigin;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetFont(FontH_t v)
    {
       // Set font.

       GCValues_t values;
       values.fFont = v;
       values.fMask = kGCFont;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetSubwindowMode(Int_t v)
    {
       // Set sub window mode (kClipByChildren, kIncludeInferiors).

       GCValues_t values;
       values.fSubwindowMode = v;
       values.fMask          = kGCSubwindowMode;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetGraphicsExposures(Bool_t v)
    {
       // True if graphics exposure should be generated.

       GCValues_t values;
       values.fGraphicsExposures = v;
       values.fMask              = kGCGraphicsExposures;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetClipXOrigin(Int_t v)
    {
       // X origin for clipping.

       GCValues_t values;
       values.fClipXOrigin = v;
       values.fMask        = kGCClipXOrigin;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetClipYOrigin(Int_t v)
    {
       // Y origin for clipping.

       GCValues_t values;
       values.fClipYOrigin = v;
       values.fMask        = kGCClipYOrigin;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetClipMask(Pixmap_t v)
    {
       // Bitmap for clipping.

       GCValues_t values;
       values.fClipMask = v;
       values.fMask     = kGCClipMask;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetDashOffset(Int_t v)
    {
       // Patterned/dashed line offset.

       GCValues_t values;
       values.fDashOffset = v;
       values.fMask       = kGCDashOffset;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetDashList(const char v[], Int_t len)
    {
       // Set dash pattern. First use SetDashOffset() if not 0.

       GCValues_t values;
       if (len > (Int_t)sizeof(values.fDashes))
          Warning("SetDashList", "dash list can have only up to %d elements",
                  sizeof(values.fDashes));
       values.fDashLen = TMath::Min(len, (Int_t)sizeof(values.fDashes));
       memcpy(values.fDashes, v, values.fDashLen);
       values.fMask    = kGCDashList;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::SetArcMode(Int_t v)
    {
       // Set arc mode (kArcChord, kArcPieSlice).

       GCValues_t values;
       values.fArcMode = v;
       values.fMask    = kGCArcMode;
       SetAttributes(&values);
    }

    //______________________________________________________________________________
     void TGGC::Print(Option_t *) const
    {
       // Print graphics contexts info.

       Printf("TGGC: mask = %lx, handle = %lx, ref cnt = %u", fValues.fMask,
              fContext, References());
    }

    //______________________________________________________________________________
     TString TGGC::GetMaskString() const
    {
       // Returns GC mask as a string - used in SavePrimitive().

       TString mask;

       Mask_t fmask = GetMask();

       if (fmask & kGCFunction) {
          if (mask.Length() == 0) mask  = "kGCFunction";
          else                    mask += " | kGCFunction";
       }
       if (fmask & kGCPlaneMask) {
          if (mask.Length() == 0) mask  = "kGCPlaneMask";
          else                    mask += " | kGCPlaneMask";
       }
       if (fmask & kGCForeground) {
          if (mask.Length() == 0) mask  = "kGCForeground";
          else                    mask += " | kGCForeground";
       }
       if (fmask & kGCBackground) {
          if (mask.Length() == 0) mask  = "kGCBackground";
          else                    mask += " | kGCBackground";
       }
       if (fmask & kGCLineWidth) {
          if (mask.Length() == 0) mask  = "kGCLineWidth";
          else                    mask += " | kGCLineWidth";
       }
       if (fmask & kGCLineStyle) {
          if (mask.Length() == 0) mask  = "kGCLineStyle";
          else                    mask += " | kGCLineStyle";
       }
       if (fmask & kGCCapStyle) {
          if (mask.Length() == 0) mask  = "kGCCapStyle";
          else                    mask += " | kGCCapStyle";
       }
       if (fmask & kGCJoinStyle) {
          if (mask.Length() == 0) mask  = "kGCJoinStyle";
          else                    mask += " | kGCJoinStyle";
       }
       if (fmask & kGCFillStyle) {
          if (mask.Length() == 0) mask  = "kGCFillStyle";
          else                    mask += " | kGCFillStyle";
       }
       if (fmask & kGCFillRule) {
          if (mask.Length() == 0) mask  = "kGCFillRule";
          else                    mask += " | kGCFillRule";
       }
       if (fmask & kGCTile) {
          if (mask.Length() == 0) mask  = "kGCTile";
          else                    mask += " | kGCTile";
       }
       if (fmask & kGCStipple) {
          if (mask.Length() == 0) mask  = "kGCStipple";
          else                    mask += " | kGCStipple";
       }
       if (fmask & kGCTileStipXOrigin) {
          if (mask.Length() == 0) mask  = "kGCTileStipXOrigin";
          else                    mask += " | kGCTileStipXOrigin";
       }
       if (fmask & kGCTileStipYOrigin) {
          if (mask.Length() == 0) mask  = "kGCTileStipYOrigin";
          else                    mask += " | kGCTileStipYOrigin";
       }
       if (fmask & kGCFont) {
          if (mask.Length() == 0) mask  = "kGCFont";
          else                    mask += " | kGCFont";
       }
       if (fmask & kGCSubwindowMode) {
          if (mask.Length() == 0) mask  = "kGCSubwindowMode";
          else                    mask += " | kGCSubwindowMode";
       }
       if (fmask & kGCGraphicsExposures) {
          if (mask.Length() == 0) mask  = "kGCGraphicsExposures";
          else                    mask += " | kGCGraphicsExposures";
       }
       if (fmask & kGCClipXOrigin) {
          if (mask.Length() == 0) mask  = "kGCClipXOrigin";
          else                    mask += " | kGCClipXOrigin";
       }
       if (fmask & kGCClipYOrigin) {
          if (mask.Length() == 0) mask  = "kGCClipYOrigin";
          else                    mask += " | kGCClipYOrigin";
       }
       if (fmask & kGCClipMask) {
          if (mask.Length() == 0) mask  = "kGCClipMask";
          else                    mask += " | kGCClipMask";
       }
       if (fmask & kGCDashOffset) {
          if (mask.Length() == 0) mask  = "kGCDashOffset";
          else                    mask += " | kGCDashOffset";
       }
       if (fmask & kGCDashList) {
          if (mask.Length() == 0) mask  = "kGCDashList";
          else                    mask += " | kGCDashList";
       }
       if (fmask & kGCArcMode) {
          if (mask.Length() == 0) mask  = "kGCArcMode";
          else                    mask += " | kGCArcMode";
       }
       return mask;
    }

    //______________________________________________________________________________
     void TGGC::SavePrimitive(ofstream &out, Option_t *option)
    {
       // Save graphics context info as a C++ statement(s) on output stream out

       if (gROOT->ClassSaved(TGGC::Class())) {
           out << endl;
       } else {
          //  declare graphics context object to reflect required user changes
          out << endl;
          out << "   TGGC   *uGC;           // will reflect user GC changes" << endl;
       }

       Mask_t fmask = GetMask();

       const char *colorname;
       char valname[10];
       char quote ='"';
       ULong_t color;

       sprintf(valname,"val%s",option);

       out << "   // graphics context changes" << endl;
       //out << "   TGGC *uGC" << option << ";" << endl;
       out << "   GCValues_t " << valname << ";" << endl;
       out << "   " << valname << ".fMask = " << GetMaskString() << ";" << endl;

       for (Mask_t bit = 1; bit <= fmask; bit <<= 1) {
          switch (bit & fmask) {
             default:
             case 0:
                continue;
             case kGCFunction:
                out << "   " << valname << ".fFunction = ";
                switch (GetFunction()) {
                   case kGXclear:
                      out << "kGXclear";
                      break;
                   case kGXand:
                      out << "kGXand";
                      break;
                   case kGXandReverse:
                      out << "kGXandReverse";
                      break;
                   case kGXcopy:
                      out << "kGXcopy";
                      break;
                   case kGXandInverted:
                      out << "kGXandInverted";
                      break;
                   case kGXnoop:
                      out << "kGXnoop";
                      break;
                   case kGXxor:
                      out << "kGXxor";
                      break;
                   case kGXor:
                      out << "kGXor";
                      break;
                   case kGXnor:
                      out << "kGXnor";
                      break;
                   case kGXequiv:
                      out << "kGXequiv";
                      break;
                   case kGXinvert:
                      out << "kGXinvert";
                      break;
                   case kGXorReverse:
                      out << "kGXorReverse";
                      break;
                   case kGXcopyInverted:
                      out << "kGXcopyInverted";
                      break;
                   case kGXorInverted:
                      out << "kGXorInverted";
                      break;
                   case kGXnand:
                      out << "kGXnand";
                      break;
                   case kGXset:
                      out << "kGXset";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCPlaneMask:
                out << "   " << valname << ".fPlaneMask = " << GetPlaneMask() << ";" << endl;
                break;
             case kGCForeground:
                color = GetForeground();
                colorname = TColor::PixelAsHexString(color);
                out << "   gClient->GetColorByName(" << quote << colorname << quote
                    << "," << valname << ".fForeground);" << endl;
                break;
             case kGCBackground:
                color = GetBackground();
                colorname = TColor::PixelAsHexString(color);
                out << "   gClient->GetColorByName(" << quote << colorname << quote
                    << "," << valname << ".fBackground);" << endl;
                break;
             case kGCLineWidth:
                out << "   " << valname << ".fLineWidth = " << GetLineWidth() << ";" << endl;
                break;
             case kGCLineStyle:
                out << "   " << valname << ".fLineStyle = ";
                switch (GetLineStyle()) {
                   case kLineSolid:
                      out << "kLineSolid";
                      break;
                   case kLineOnOffDash:
                      out << "kLineOnOffDash";
                      break;
                   case kLineDoubleDash:
                      out << "kLineDoubleDash";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCCapStyle:
                out << "   " << valname << ".fCapStyle = ";
                switch (GetCapStyle()) {
                   case kCapNotLast:
                      out << "kCapNotLast";
                      break;
                   case kCapButt:
                      out << "kCapButt";
                      break;
                   case kCapRound:
                      out << "kCapRound";
                      break;
                   case kCapProjecting:
                      out << "kCapProjecting";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCJoinStyle:
                out << "   " << valname << ".fJoinStyle = ";
                switch (GetJoinStyle()) {
                   case kJoinMiter:
                      out << "kJoinMiter";
                      break;
                   case kJoinRound:
                      out << "kJoinRound";
                      break;
                   case kJoinBevel:
                      out << "kJoinBevel";
                      break;
                }
                 out << ";" << endl;
                break;
             case kGCFillStyle:
                out << "   " << valname << ".fFillStyle = ";
                switch (GetFillStyle()) {
                   case kFillSolid:
                      out << "kFillSolid";
                      break;
                   case kFillTiled:
                      out << "kFillTiled";
                      break;
                   case kFillStippled:
                      out << "kFillStippled";
                      break;
                   case kFillOpaqueStippled:
                      out << "kFillOpaqueStippled";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCFillRule:
                out << "   " << valname << ".fFillRule = ";
                switch (GetFillRule()) {
                   case kEvenOddRule:
                      out << "kEvenOddRule";
                      break;
                   case kWindingRule:
                      out << "kWindingRule";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCTile:
                out << "   " << valname << ".fTile = " << GetTile() << ";" << endl;
                break;
             case kGCStipple:
                out << "   " << valname << ".fStipple = " << GetStipple() << ";" << endl;
                break;
             case kGCTileStipXOrigin:
                out << "   " << valname << ".fTsXOrigin = " << GetTileStipXOrigin() << ";" << endl;
                break;
             case kGCTileStipYOrigin:
                out << "   " << valname << ".fTsYOrigin = " << GetTileStipYOrigin() << ";" << endl;
                break;
             case kGCFont:
                out << "   " << valname << ".fFont = ufont->GetFontHandle();" << endl;
                break;
             case kGCSubwindowMode:
                out << "   " << valname << ".fSubwindowMode = ";
                switch (GetSubwindowMode()) {
                   case kClipByChildren:
                      out << "kClipByChildren";
                      break;
                   case kIncludeInferiors:
                      out << "kIncludeInferiors";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCGraphicsExposures:
                out << "   " << valname << ".fGraphicsExposures = ";
                switch (GetGraphicsExposures()) {
                   case kTRUE:
                      out << "kTRUE";
                      break;
                   case kFALSE:
                      out << "kFALSE";
                      break;
                }
                out << ";" << endl;
                break;
             case kGCClipXOrigin:
                out << "   " << valname << ".fClipXOrigin = " << GetClipXOrigin() << ";" << endl;
                break;
             case kGCClipYOrigin:
                out << "   " << valname << ".fClipYOrigin = " << GetClipYOrigin() << ";" << endl;
                break;
             case kGCClipMask:
                out << "   " << valname << ".fClipMask = " << GetClipMask() << ";" << endl;
                break;
             case kGCDashOffset:
                out << "   " << valname << ".fDashOffset = " << GetDashOffset() << ";" << endl;
                break;
             case kGCDashList:
                if (GetDashLen() > (Int_t)sizeof(GetDashes()))
                   Warning("TGGC::SavePrimitive", "dash list can have only up to %d elements",
                           sizeof(GetDashes()));
                out << "   " << valname << ".fDashLen = "
                    << TMath::Min(GetDashLen(),(Int_t)sizeof(GetDashes())) << ";" << endl;
                out << "   memcpy(GetDashes()," << valname << ".fDashes,"
                                                << valname << ".fDashLen);" << endl;
                break;
             case kGCArcMode:
                out << "   " << valname << ".fArcMode = ";
                switch (GetArcMode()) {
                   case kArcChord:
                      out << "kArcChord";
                      break;
                   case kArcPieSlice:
                      out << "kArcPieSlice";
                      break;
                }
                out << ";" << endl;
                break;
          }
       }
       out << "   uGC = gClient->GetGC(&" << valname << ", kTRUE);" << endl;
    }

    ClassImp(TGGCPool)

    //______________________________________________________________________________
    TGGCPool::TGGCPool(TGClient *client)
    {
       // Create graphics context pool.

       fClient = client;
       fList   = new THashTable;
       fList->SetOwner();
    }

    //______________________________________________________________________________
    TGGCPool::~TGGCPool()
    {
       // Delete graphics context pool.

       delete fList;
    }

    //______________________________________________________________________________
    void TGGCPool::ForceFreeGC(const TGGC *gct)
    {
       // Force remove graphics context from list. Is only called via ~TGGC().

       TGGC *gc = (TGGC *) fList->FindObject(gct);

       if (gc) {
          if (gc->References() > 1)
             Error("ForceFreeGC", "removed a shared graphics contextn",
                   "best to use graphics contexts via the TGGCPool()");
          fList->Remove(gc);
       }
    }

    //______________________________________________________________________________
    void TGGCPool::FreeGC(const TGGC *gct)
    {
       // Delete graphics context if it is not used anymore.

       TGGC *gc = (TGGC *) fList->FindObject(gct);

       if (gc) {
          if (!gc->RemoveReference() == 0) {
             fList->Remove(gc);
             delete gc;
          }
       }
    }

    //______________________________________________________________________________
    void TGGCPool::FreeGC(GContext_t gct)
    {
       // Delete graphics context if it is not used anymore.

       TIter next(fList);

       while (TGGC *gc = (TGGC *) next()) {
          if (gc->fContext == gct) {
             if (gc->RemoveReference() == 0) {
                fList->Remove(gc);
                delete gc;
                return;
             }
          }
       }
    }

    //______________________________________________________________________________
    TGGC *TGGCPool::FindGC(const TGGC *gct)
    {
       // Find graphics context. Returns 0 in case gc is not found.

       return (TGGC*) fList->FindObject(gct);
    }

    //______________________________________________________________________________
    TGGC *TGGCPool::FindGC(GContext_t gct)
    {
       // Find graphics context based on its GContext_t handle. Returns 0
       // in case gc is not found.

       TIter next(fList);

       while (TGGC *gc = (TGGC *) next()) {
          if (gc->fContext == gct)
             return gc;
       }
       return 0;
    }

    //______________________________________________________________________________
    TGGC *TGGCPool::GetGC(GCValues_t *values, Bool_t rw)
    {
       // Get the best matching graphics context depending on values.
       // If rw is false only a readonly, not modifiable graphics context
       // is returned. If rw is true a new modifiable graphics context is
       // returned.

       TGGC *gc, *best_match = 0;
       Int_t matching_bits, best_matching_bits = -1;
       Bool_t exact = kFALSE;

       if (!values)
          rw = kTRUE;

       if (!rw) {

          // First, try to find an exact matching GC.
          // If no one found, then use the closest one.

          TIter next(fList);

          while ((gc = (TGGC *) next())) {
             matching_bits = MatchGC(gc, values);
             if (matching_bits > best_matching_bits) {
                best_matching_bits = matching_bits;
                best_match = gc;
                if ((gc->fValues.fMask & values->fMask) == values->fMask) {
                   exact = kTRUE;
                   break;
                }
             }
          }

          if (best_match) {
             if (gDebug > 0)
                Printf("<TGGCPool::GetGC>: %smatching GC foundn", exact ? "exact " : "");
             best_match->AddReference();
             if (!exact) {
                // add missing values to the best_match'ing GC...
                UpdateGC(best_match, values);
             }
             return best_match;
          }
       }

       gc = new TGGC(values, kTRUE);

       fList->Add(gc);

       return gc;
    }

    //______________________________________________________________________________
    Int_t TGGCPool::MatchGC(const TGGC *gc, GCValues_t *values)
    {
       // Try to find matching graphics context. On success returns the amount
       // of matching bits (which may be zero if masks have no common bits),
       // -1 on failure (when there are common bits but not a single match).

       Mask_t bit, common_bits;
       Int_t  matching_bits = -1;
       Bool_t match = kFALSE;
       const GCValues_t *gcv = &gc->fValues;

       common_bits = values->fMask & gcv->fMask;

       if (common_bits == 0) return 0;  // no common bits, a possible
                                        // candidate anyway.

       // Careful, check first the tile and stipple mask bits, as these
       // influence nearly all other GC functions... (do the same for
       // some other such bits as GCFunction, etc...). Perhaps we should
       // allow only exact GC matches.

       if (gcv->fMask & kGCTile)
          if ((gcv->fTile != kNone) && !(values->fMask & kGCTile)) return -1;
       if (values->fMask & kGCTile)
          if ((values->fTile != kNone) && !(gcv->fMask & kGCTile)) return -1;
       if (gcv->fMask & kGCStipple)
          if ((gcv->fStipple != kNone) && !(values->fMask & kGCStipple)) return -1;
       if (values->fMask & kGCStipple)
          if ((values->fStipple != kNone) && !(gcv->fMask & kGCStipple)) return -1;

       for (bit = 1; bit <= common_bits; bit <<= 1) {
          switch (bit & common_bits) {
             default:
             case 0:
                continue;
             case kGCFunction:
                match = (values->fFunction == gcv->fFunction);
                break;
             case kGCPlaneMask:
                match = (values->fPlaneMask == gcv->fPlaneMask);
                break;
             case kGCForeground:
                match = (values->fForeground == gcv->fForeground);
                break;
             case kGCBackground:
                match = (values->fBackground == gcv->fBackground);
                break;
             case kGCLineWidth:
                match = (values->fLineWidth == gcv->fLineWidth);
                break;
             case kGCLineStyle:
                match = (values->fLineStyle == gcv->fLineStyle);
                break;
             case kGCCapStyle:
                match = (values->fCapStyle == gcv->fCapStyle);
                break;
             case kGCJoinStyle:
                match = (values->fJoinStyle == gcv->fJoinStyle);
                break;
             case kGCFillStyle:
                match = (values->fFillStyle == gcv->fFillStyle);
                break;
             case kGCFillRule:
                match = (values->fFillRule == gcv->fFillRule);
                break;
             case kGCTile:
                match = (values->fTile == gcv->fTile);
                break;
             case kGCStipple:
                match = (values->fStipple == gcv->fStipple);
                break;
             case kGCTileStipXOrigin:
                match = (values->fTsXOrigin == gcv->fTsXOrigin);
                break;
             case kGCTileStipYOrigin:
                match = (values->fTsYOrigin == gcv->fTsYOrigin);
                break;
             case kGCFont:
                match = (values->fFont == gcv->fFont);
                break;
             case kGCSubwindowMode:
                match = (values->fSubwindowMode == gcv->fSubwindowMode);
                break;
             case kGCGraphicsExposures:
                match = (values->fGraphicsExposures == gcv->fGraphicsExposures);
                break;
             case kGCClipXOrigin:
                match = (values->fClipXOrigin == gcv->fClipXOrigin);
                break;
             case kGCClipYOrigin:
                match = (values->fClipYOrigin == gcv->fClipYOrigin);
                break;
             case kGCClipMask:
                match = (values->fClipMask == gcv->fClipMask);
                break;
             case kGCDashOffset:
                match = (values->fDashOffset == gcv->fDashOffset);
                break;
             case kGCDashList:
                if (values->fDashLen == gcv->fDashLen)
                   match = (strncmp(values->fDashes, gcv->fDashes, gcv->fDashLen) == 0);
                break;
             case kGCArcMode:
               match = (values->fArcMode == gcv->fArcMode);
               break;
          }
          if (!match)
             return -1;
          matching_bits++;
          match = kFALSE;
       }

       return matching_bits;
    }

    //______________________________________________________________________________
    void TGGCPool::UpdateGC(TGGC *gc, GCValues_t *values)
    {
       // Update graphics context with the values spcified in values->fMask.

       gc->SetAttributes(values);
    }

    //______________________________________________________________________________
    void TGGCPool::Print(Option_t *) const
    {
       // List all graphics contexts in the pool.

       fList->Print();
    }