    // @(#)root/gui:$Name:  $:$Id: TGFSComboBox.cxx,v 1.11 2004/04/20 14:48:01 brun Exp $
    // Author: Fons Rademakers   19/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.

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

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TGFSComboBox, TGTreeLBEntry                                          //
    //                                                                      //
    // This is a combo box that is used in the File Selection dialog box.   //
    // It will allow the file path selection.                               //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #ifdef HAVE_CONFIG
    #include "config.h"
    #endif

    #include "TGFSComboBox.h"
    #include "TGResourcePool.h"
    #include "TGPicture.h"
    #include "TMath.h"
    #include "TSystem.h"
    #include "Riostream.h"

    const TGFont *TGTreeLBEntry::fgDefaultFont = 0;
    TGGC         *TGTreeLBEntry::fgDefaultGC = 0;

    //--- this is temp here...

    struct lbc_t {
      const char *name;
      const char *path;
      const char *pixmap;
      Int_t       id, indent, flags;
    };

    static struct lbc_t gLbc[] = {
      { "Root",        "/",                     "hdisk_t.xpm",         1000, 0, 0 },
      { "Floppy",      "/floppy",               "fdisk_t.xpm",         2000, 1, 0 },
      { "CD-ROM",      "/cdrom",                "cdrom_t.xpm",         3000, 1, 0 },
      { "Home",        "$HOME",                 "home_t.xpm",          4000, 1, 0 },
    #ifndef ROOTPREFIX
      { "RootSys",     "$ROOTSYS",              "root_t.xpm",          5000, 1, 0 },
    #else
      { ROOTPREFIX,    ROOTPREFIX,              "root_t.xpm",          5000, 1, 0 },
    #endif
      { 0,             0,                       0,                     6000, 0, 0 }
    };

    ClassImp(TGTreeLBEntry)
    ClassImp(TGFSComboBox)

    //______________________________________________________________________________
    TGTreeLBEntry::TGTreeLBEntry(const TGWindow *p, TGString *text,
                                 const TGPicture *pic, Int_t id, TGString *path,
                                 GContext_t norm, FontStruct_t font, UInt_t options,
                                 ULong_t back) :
       TGLBEntry(p, id, options, back)
    {
       // Create a tree (i.e. entry can be indented) listbox entry.
       // The strings text and path are adopted by the entry.

       if (!pic)
          Error("TGTreeLBEntry", "icon not found for entry %s", text->GetString());

       fPic    = pic;
       fSelPic = 0;
       fText   = text;
       fPath   = path;

       fNormGC     = norm;
       fFontStruct = font;

       fActive = kFALSE;

       int max_ascent, max_descent;
       fTWidth = gVirtualX->TextWidth(fFontStruct, fText->GetString(), fText->GetLength());
       gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
       fTHeight = max_ascent + max_descent;
    }

    //______________________________________________________________________________
    TGTreeLBEntry::~TGTreeLBEntry()
    {
       // Delete tree listbox entry.

       delete fText;
       delete fPath;
       delete fSelPic;
    }

    //______________________________________________________________________________
    void TGTreeLBEntry::Activate(Bool_t a)
    {
       // Make entry active (highlight picture).

       if (fActive == a) return;
       fActive = a;

       if (fActive) {
          fSelPic = new TGSelectedPicture(fClient, fPic);
       } else {
          if (fSelPic) delete fSelPic;
          fSelPic = 0;
       }
       DoRedraw();
    }

    //______________________________________________________________________________
    void TGTreeLBEntry::DoRedraw()
    {
       // Redraw the tree listbox entry.

       int ix, iy, lx, ly;

       ix = 0;
       iy = (fHeight - fPic->GetHeight()) >> 1;
       lx = (int)(fPic->GetWidth() + 4);
       ly = (int)((fHeight - (fTHeight+1)) >> 1);

       if (fActive) {
          if (fSelPic) fSelPic->Draw(fId, fNormGC, ix, iy);
          gVirtualX->SetForeground(fNormGC, fgDefaultSelectedBackground);
          gVirtualX->FillRectangle(fId, fNormGC, lx, ly, fTWidth, fTHeight+1);
          gVirtualX->SetForeground(fNormGC, fClient->GetResourcePool()->GetSelectedFgndColor());
       } else {
          fPic->Draw(fId, fNormGC, ix, iy);
          gVirtualX->SetForeground(fNormGC, fgWhitePixel);
          gVirtualX->FillRectangle(fId, fNormGC, lx, ly, fTWidth, fTHeight+1);
          gVirtualX->SetForeground(fNormGC, fgBlackPixel);
       }

       int max_ascent, max_descent;
       gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);

       fText->Draw(fId, fNormGC, lx, ly + max_ascent);
    }

    //______________________________________________________________________________
    TGDimension TGTreeLBEntry::GetDefaultSize() const
    {
       // Return default size of tree listbox entry.

       TGDimension isize(fPic->GetWidth(), fPic->GetHeight());
       TGDimension lsize(fTWidth, fTHeight+1);

       return TGDimension(isize.fWidth + lsize.fWidth + 4,
                          TMath::Max(isize.fHeight, lsize.fHeight) + 2);
    }

    //______________________________________________________________________________
    void TGTreeLBEntry::Update(TGLBEntry *e)
    {
       // Update text and picture of a listbox entry.

       TGTreeLBEntry *te = (TGTreeLBEntry *) e;

       if (fText) delete fText;
       fText = new TGString(te->GetText());
       fPic = te->GetPicture();
       fTWidth = gVirtualX->TextWidth(fFontStruct, fText->GetString(), fText->GetLength());
       gVirtualX->ClearWindow(fId);
       fClient->NeedRedraw(this);
    }

    //______________________________________________________________________________
    FontStruct_t TGTreeLBEntry::GetDefaultFontStruct()
    {
       if (!fgDefaultFont)
          fgDefaultFont = gClient->GetResourcePool()->GetDefaultFont();
       return fgDefaultFont->GetFontStruct();
    }

    //______________________________________________________________________________
    const TGGC &TGTreeLBEntry::GetDefaultGC()
    {
       if (!fgDefaultGC)
          fgDefaultGC = new TGGC(*gClient->GetResourcePool()->GetFrameGC());
       return *fgDefaultGC;
    }

    //______________________________________________________________________________
     TGFSComboBox::TGFSComboBox(const TGWindow *parent, Int_t id, UInt_t options,
                               ULong_t back) :
       TGComboBox(parent, id, options | kOwnBackground, back)
    {
       // Create a file system combobox showing system directories.

       int   i, indent;
       const TGPicture *pic;
       char  *p;

       SetTopEntry(new TGTreeLBEntry(this, new TGString("Current dir"),
                   fClient->GetPicture("folder_t.xpm"), 0),
                   new TGLayoutHints(kLHintsLeft | kLHintsExpandX |
                                     kLHintsExpandY, 4, 0, 0, 0));

       fListBox->GetContainer()->AddInput(kButtonPressMask | kButtonReleaseMask |
                                          kPointerMotionMask);

       //--- first check for the existence of some directories...

       const char *homeDir = gSystem->Getenv("HOME");
    #ifndef ROOTPREFIX
       const char *rootSys = gSystem->Getenv("ROOTSYS");
    #else
       const char *rootSys = ROOTPREFIX;
    #endif

       for (i = 0; gLbc[i].path != 0; ++i) {
          if (strstr(gLbc[i].path, "$HOME") != 0) {
             if (homeDir) {
                int hlen = strlen(homeDir);
                p = new char[hlen + strlen(gLbc[i].path) - 3];
                strcpy(p, homeDir);
                strcat(p, &gLbc[i].path[5]);
                gLbc[i].path = p;
             } else {
                gLbc[i].flags = 0;
             }
          }
    #ifndef ROOTPREFIX
          if (strstr(gLbc[i].path, "$ROOTSYS") != 0) {
    #else
          if (strstr(gLbc[i].path, ROOTPREFIX) != 0) {
    #endif
             if (rootSys) {
                int hlen = strlen(rootSys);
                p = new char[hlen + strlen(gLbc[i].path) - 3];
                strcpy(p, rootSys);
                strcat(p, &gLbc[i].path[8]);
                gLbc[i].path = p;
             } else {
                gLbc[i].flags = 0;
             }
          }
          if (gSystem->AccessPathName(gLbc[i].path, kFileExists) == 0)
             gLbc[i].flags = 1;
       }

       //--- then init the contents...

       for (i = 0; gLbc[i].name != 0; ++i) {
          if (gLbc[i].flags) {
             indent = 4 + (gLbc[i].indent * 10);
             pic = fClient->GetPicture(gLbc[i].pixmap);
             if (!pic) Error("TGFSComboBox", "pixmap not found: %s", gLbc[i].pixmap);
             AddEntry(new TGTreeLBEntry(fListBox->GetContainer(),
                      new TGString(gLbc[i].name), pic, gLbc[i].id,
                      new TGString(gLbc[i].path)),
                      new TGLayoutHints(kLHintsLeft | kLHintsTop, indent, 0, 0, 0));
          }
       }
    }

    //______________________________________________________________________________
     void TGFSComboBox::Update(const char *path)
    {
       // Update file system combo box.

       char dirname[1024], mpath[1024];
       const char *tailpath = 0;
       int  i, indent_lvl = 0, afterID = -1, sel = -1;

       if (!path) return;

       for (i = 0; gLbc[i].path != 0; ++i)
          RemoveEntries(gLbc[i].id+1, gLbc[i+1].id-1);

       int len = 0;
       for (i = 0; gLbc[i].name != 0; ++i) {
          if (gLbc[i].flags) {
             int slen = strlen(gLbc[i].path);
             if (strncmp(path, gLbc[i].path, slen) == 0) {
                if (slen > len) {
                   sel = afterID = gLbc[i].id;
                   indent_lvl = gLbc[i].indent + 1;
                   tailpath = path + slen;
                   strcpy(mpath, gLbc[i].path);
                   len = slen;
                }
             }
          }
       }

       if (tailpath && *tailpath) {
          if (*tailpath == '/') ++tailpath;
          if (*tailpath)
             while (1) {
                const char *picname;
                const char *semi = strchr(tailpath, '/');
                if (semi == 0) {
                   strcpy(dirname, tailpath);
                   picname = "ofolder_t.xpm";
                } else {
                   strncpy(dirname, tailpath, semi-tailpath);
                   dirname[semi-tailpath] = 0;
                   picname = "folder_t.xpm";
                }
                if (mpath[strlen(mpath)-1] != '/') strcat(mpath, "/");
                strcat(mpath, dirname);
                int indent = 4 + (indent_lvl * 10);
                const TGPicture *pic = fClient->GetPicture(picname);
                if (!pic) Error("Update", "pixmap not found: %s", picname);
                InsertEntry(new TGTreeLBEntry(fListBox->GetContainer(),
                            new TGString(dirname), pic, afterID+1,
                            new TGString(mpath)),
                            new TGLayoutHints(kLHintsLeft | kLHintsTop,
                                              indent, 0, 0, 0),
                            afterID);
                 sel = ++afterID;
                 ++indent_lvl;
                 if (semi == 0) break;
                 tailpath = ++semi;
             }
       }
       if (sel > 0) Select(sel);
    }

    //______________________________________________________________________________
     void TGFSComboBox::SavePrimitive(ofstream &out, Option_t *option)
    {
       // Save a file system combo box as a C++ statement(s) on output stream out.

       if (fBackground != GetWhitePixel()) SaveUserColor(out, option);

       out << endl << "   // file system combo box" << endl;
       out << "   TGFSComboBox *";
       out << GetName() << " = new TGFSComboBox(" << fParent->GetName()
                                              << "," << fWidgetId;
       if (fBackground == GetWhitePixel()) {
          if (GetOptions() == (kHorizontalFrame | kSunkenFrame | kDoubleBorder)) {
             out <<");" << endl;
          } else {
             out << "," << GetOptionString() <<");" << endl;
          }
       } else {
          out << "," << GetOptionString() << ",ucolor);" << endl;
       }

       out << "   " << GetName() << "->Resize(" << GetWidth()  << ","
           << GetHeight() << ");" << endl;
       out << "   " << GetName() << "->Select(" << GetSelected() << ");" << endl;

    }