    // @(#)root/gui:$Name:  $:$Id: TGButtonGroup.cxx,v 1.22 2005/02/07 22:47:43 brun Exp $
    // Author: Valeriy Onuchin & Fons Rademakers   16/10/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.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // The TGButtonGroup widget organizes TGButton widgets in a group.      //
    //                                                                      //
    // A button group widget makes it easier to deal with groups of buttons.//
    // A button in a button group is associated with a unique identifier.   //
    // The button group emits a Clicked() signal with this identifier when  //
    // the button is clicked. Thus, a button group is an ideal solution     //
    // when you have several similar buttons and want to connect all their  //
    // Clicked() signals, for example, to one slot.                         //
    //                                                                      //
    // An exclusive button group switches off all toggle buttons except     //
    // the one that was clicked. A button group is by default non-exclusive.//
    // All radio buttons that are inserted, will be mutually exclusive even //
    // if the button group is non-exclusive.                                //
    //                                                                      //
    //                                                                      //
    // There are two ways of using a button group:                          //
    //                                                                      //
    //    The button group is a parent widget of a number of buttons,       //
    //    i.e. the button group is the parent argument in the button        //
    //    constructor. The buttons are assigned identifiers 1, 2, 3 etc.    //
    ///   in the order they are created or you can specify button id in     //
    //    the button constructor. A TGButtonGroup can display a frame and   //
    //    a title because it inherits from TGGroupFrame.                    //
    //                                                                      //
    // Example:                                                             //
    //                                                                      //
    //    // vertical frame without border and title                        //
    //    TGVButtonGroup *bg = new TGVButtonGroup(main_frame);              //
    //                                                                      //
    //    // create text button with id=1                                   //
    //    TGTextButton *button1 = new TGTextButton(bg,"some text");         //
    //                                                                      //
    //    // create another text button with id=2                           //
    //    TGTextButton *button2 = new TGTextButton(bg,"another text");      //
    //                                                                      //
    //    // map all buttons                                                //
    //    bg->Show();                                                       //
    //                                                                      //
    // NOTE: there is no need to call AddFrame() since the buttons are      //
    // automatically added with a default layout hint to their parent,      //
    // i.e. the buttongroup. To override the default layout hints use the   //
    // SetLayoutHints() method.                                             //
    //                                                                      //
    //  ButtonGroup Signals:                                                //
    //                                                                      //
    //    Pressed(Int_t id)  -->  is emitted when a button in the group is  //
    //                            pressed down. The id argument is the      //
    //                            button's identifier.                      //
    //    Released(Int_t id) -->  is emitted when a button in the group is  //
    //                            released. The id argument is the button's //
    //                            identifier.                               //
    //    Clicked(Int_t id)  -->  is emitted when a button in the group is  //
    //                            clicked. The id argument is the button's  //
    //                            identifier.                               //
    //                                                                      //
    //                                                                      //
    // The TGHButtonGroup widget organizes TGButton widgets in a group      //
    // with one horizontal row. TGHButtonGroup is a convenience class that  //
    // offers a thin layer on top of TGButtonGroup. It inherits from        //
    // TGButtonGroup.                                                       //
    //                                                                      //
    // The TGVButtonGroup widget organizes TGButton widgets in a group      //
    // with one vertical column. TGVButtonGroup is a convenience class that //
    // offers a thin layer on top of TGButtonGroup. It inherits from        //
    // TGButtonGroup.                                                       //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TGButtonGroup.h"
    #include "TGButton.h"
    #include "TClass.h"
    #include "TGLayout.h"
    #include "TList.h"
    #include "TGResourcePool.h"
    #include "Riostream.h"

    ClassImp(TGButtonGroup)
    ClassImp(TGHButtonGroup)
    ClassImp(TGVButtonGroup)

    //______________________________________________________________________________
    TGButtonGroup::TGButtonGroup(const TGWindow *parent,
                                 const TString &title,
                                 UInt_t options,
                                 GContext_t norm,
                                 FontStruct_t font,
                                 ULong_t back) :
       TGGroupFrame(parent, new TGString(title), options, norm, font, back)
    {
       // Constructor. Layout 1 row or 1 column.

       Init();
       if (options & kVerticalFrame) {
          SetLayoutManager(new TGVerticalLayout(this));
       } else {
          SetLayoutManager(new TGHorizontalLayout(this));
       }

       fDrawBorder = !title.IsNull();
    }

    //______________________________________________________________________________
    TGButtonGroup::TGButtonGroup(const TGWindow *parent,
                                 UInt_t r, UInt_t c,
                                 Int_t s, Int_t h,
                                 const TString &title,
                                 GContext_t norm ,
                                 FontStruct_t font ,
                                 ULong_t back) :
       TGGroupFrame(parent, new TGString(title), 0, norm, font, back)
    {
       // Constructor. Layout defined by TGMatrixLayout:
       //    r = number of rows
       //    c = number of columns
       //    s = interval between frames
       //    h = layout hints

       Init();
       fDrawBorder = !title.IsNull();
       SetLayoutManager(new TGMatrixLayout(this,r,c,s,h));
    }

    //______________________________________________________________________________
    void TGButtonGroup::Init()
    {
       // Default init.

       fMapOfButtons = new TMap();  // map of button/id pairs
       fExclGroup    = kFALSE;
       fRadioExcl    = kFALSE;
       fDrawBorder   = kTRUE;
       SetWindowName();
    }

    //______________________________________________________________________________
    TGButtonGroup::~TGButtonGroup()
    {
       // Destructor, we do not delete the buttons.

       TIter next(fMapOfButtons);
       register TGButton *item = 0;

       while ((item = (TGButton*)next())) {
          item->SetGroup(0);
       }

       SafeDelete(fMapOfButtons);
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetBorderDrawn(Bool_t enable)
    {
       // Makes border to be visible/invisible.

       if (enable != IsBorderDrawn()) {
          fDrawBorder = enable;
          ChangedBy("SetBorderDrawn");        // emit signal
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetExclusive(Bool_t enable)
    {
       // Sets the button group to be exclusive if enable is kTRUE,
       // or to be non-exclusive if enable is kFALSE.
       // An exclusive button group switches off all other toggle buttons when
       // one is switched on. This is ideal for groups of radio-buttons
       // A non-exclusive group allow many buttons to be switched on at the same
       // time. The default setting is kFALSE.

       if (enable != IsExclusive()) {
          fExclGroup = enable;
          ChangedBy("SetExclusive");  // emit signal
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetRadioButtonExclusive(Bool_t enable)
    {
       // If enable is kTRUE, this button group will treat radio buttons as
       // mutually exclusive, and other buttons according to IsExclusive().
       // This function is called automatically whenever a TGRadioButton
       // is inserted, so you should normally never have to call it.

       if (enable != IsRadioButtonExclusive()) {
          fRadioExcl = enable;
          ChangedBy("SetRadioButtonExclusive"); // emit signal
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetButton(Int_t id, Bool_t down)
    {
       // Sets the button with id to be on/down, and if this is an
       // exclusive group, all other button in the group to be off/up.

       TGButton *b = Find(id);

       if (b && (b->IsDown() != down)) {
          b->SetDown(down, kTRUE);
       }
    }

    //______________________________________________________________________________
    Int_t TGButtonGroup::Insert(TGButton *button, Int_t id)
    {
       // Inserts a button with the identifier id into the button group.
       // Returns the button identifier.
       //
       // It is not necessary to manually insert buttons that have this button
       // group as their parent widget. An exception is when you want custom
       // identifiers instead of the default 1, 2, 3 etc.
       //
       // The button is assigned the identifier id or an automatically
       // generated identifier.  It works as follows: If id > 0, this
       // identifier is assigned.  If id == -1 (default), the identifier is
       // equal to the number of buttons in the group+1.  If id is any other
       // negative integer, for instance -2, a unique identifier (negative
       // integer <= -2) is generated.
       //
       // Inserting several buttons with id = -1 assigns the identifiers 1,
       // 2, 3, etc.

       if (button->fGroup && button->fGroup != this)
          button->fGroup->Remove(button);

       if (button->fGroup == this) {
          if (id == -1)
             return GetId(button);    // the button is already in group
          else
             button->fGroup->Remove(button);  // want to set a new id
       }

       button->fGroup = this;

       static Int_t seq_no = -2;
       Long_t bid;

       if (id < -1)       bid = seq_no--;
       else if (id == -1) bid = GetCount()+1;
       else               bid = id;

       fMapOfButtons->Add(button, (TObject*)bid);
       AddFrame(button);

       SetRadioButtonExclusive(button->IsA()->InheritsFrom(TGRadioButton::Class()));

       Connect(button, "Pressed()" , "TGButtonGroup", this, "ReleaseButtons()");
       Connect(button, "Pressed()" , "TGButtonGroup", this, "ButtonPressed()");
       Connect(button, "Released()", "TGButtonGroup", this, "ButtonReleased()");
       Connect(button, "Clicked()" , "TGButtonGroup", this, "ButtonClicked()");

       return (Int_t) bid;
    }

    //______________________________________________________________________________
    void TGButtonGroup::Remove(TGButton *button)
    {
       // Removes a button from the button group.

       TGButton *item = (TGButton*) fMapOfButtons->Remove(button);
       if (item) {
          button->SetGroup(0);
          button->Disconnect(this);
       }

       RemoveFrame(button);
    }

    //______________________________________________________________________________
    TGButton *TGButtonGroup::Find(Int_t id) const
    {
       // Finds and returns a pointer to the button with the specified
       // identifier id. Returns null if the button was not found.

       TIter next(fMapOfButtons);
       register TGButton *item = 0;

       while ((item = (TGButton*)next())) {
          if ((Long_t)fMapOfButtons->GetValue(item) == id) break;   // found
       }

       return item;
    }

    //______________________________________________________________________________
    Int_t TGButtonGroup::GetId(TGButton *button) const
    {
       // Finds and returns the id of the button.
       // Returns -1 if the button is not a member of this group.

       TPair *a = (TPair*) fMapOfButtons->FindObject(button);
       if (a)
          return (Int_t)Long_t(a->Value());
       else
          return -1;
    }

    //______________________________________________________________________________
    void TGButtonGroup::ButtonPressed()
    {
       // This slot is activated when one of the buttons in the group emits the
       // Pressed() signal.

    #if 0
       // Is here for historical purposes and example. Now this is not needed
       // anymore since TGButton has has its own GetSender() method returning
       // the TGButton proper.

       // This is needed since gTQSender points to TQObject part of TGButton
       TGButton *btn = dynamic_cast<TGButton*>((TQObject*)gTQSender);

       if (!btn) {
          Error("ButtonPressed", "gTQSender not a TGButton");
          return;
       }
    #else
          TGButton *btn = (TGButton*)gTQSender;
    #endif

       TPair *a = (TPair*) fMapOfButtons->FindObject(btn);
       if (a) {
          Int_t id = (Int_t)Long_t(a->Value());
          Pressed(id);
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::ButtonReleased()
    {
       // This slot is activated when one of the buttons in the group emits the
       // Released() signal.

       TGButton *btn = (TGButton*)gTQSender;

       TPair *a = (TPair*) fMapOfButtons->FindObject(btn);
       if (a) {
          Int_t id = (Int_t)Long_t(a->Value());
          Released(id);
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::ButtonClicked()
    {
       // This slot is activated when one of the buttons in the group emits the
       // Clicked() signal.

       TGButton *btn = (TGButton*)gTQSender;

       TPair *a = (TPair*) fMapOfButtons->FindObject(btn);
       if (a) {
          Int_t id = (Int_t)Long_t(a->Value());
          Clicked(id);
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::ReleaseButtons()
    {
       // This slot is activated when one of the buttons in the
       // exclusive group emits the Pressed() signal.

       if (!fExclGroup && !fRadioExcl) return;

       TGButton *btn = (TGButton*)gTQSender;

       if (!fExclGroup && !btn)
          return;

       TIter next(fMapOfButtons);
       register TGButton *item = 0;

       while ((item = (TGButton*)next())) {    // loop over all buttons
          if (btn != item && item->IsToggleButton() && item->IsOn() &&
              (fExclGroup || item->IsA()->InheritsFrom(TGRadioButton::Class()))) {
             item->SetOn(kFALSE);
          }
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::Show()
    {
       // Show group of buttons.

       MapSubwindows();
       Resize();
       MapRaised();
       fClient->NeedRedraw(this);
    }

    //______________________________________________________________________________
    void TGButtonGroup::Hide()
    {
       // Hide group of buttons.

       UnmapWindow();
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetTitle(TGString *title)
    {
       // Set or change title.

       if (!title) {
          Error("SetTitle", "title cannot be 0, try \"\"");
          return;
       }

       if (strcmp(fText->GetString(), title->GetString())) {
          SetBorderDrawn(title->GetLength() ? kTRUE : kFALSE);
          TGGroupFrame::SetTitle(title);
          ChangedBy("SetTitle");
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetTitle(const char *title)
    {
       // Set or change title.

       if (!title) {
          Error("SetTitle", "title cannot be 0, try \"\"");
          return;
       }

       if (strcmp(fText->GetString(), title)) {
          SetBorderDrawn(title && strlen(title));
          TGGroupFrame::SetTitle(title);
          ChangedBy("SetTitle");
       }
    }

    //______________________________________________________________________________
    void TGButtonGroup::SetLayoutHints(TGLayoutHints *l, TGButton *button)
    {
       // Set layout hints for the specified button or if button=0 for all
       // buttons.

       TGFrameElement *el;
       TIter next(fList);

       while ((el = (TGFrameElement *)next())) {
          if ((el->fFrame==(TGFrame*)button) || !button) {
             el->fLayout = l ? l : fgDefaultHints;
          }
       }
       Layout();
    }

    //______________________________________________________________________________
    void TGButtonGroup::SavePrimitive(ofstream &out, Option_t *option)
    {
       // Save a button group widget as a C++ statement(s) on output stream out

       char quote ='"';

       // font + GC
       option = GetName()+5;         // unique digit id of the name
       char ParGC[50], ParFont[50];
       sprintf(ParFont,"%s::GetDefaultFontStruct()",IsA()->GetName());
       sprintf(ParGC,"%s::GetDefaultGC()()",IsA()->GetName());

       if ((GetDefaultFontStruct() != fFontStruct) || (GetDefaultGC()() != fNormGC)) {
          TGFont *ufont = gClient->GetResourcePool()->GetFontPool()->FindFont(fFontStruct);
          if (ufont) {
             ufont->SavePrimitive(out, option);
             sprintf(ParFont,"ufont->GetFontStruct()");
          }

          TGGC *userGC = gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
          if (userGC) {
             userGC->SavePrimitive(out, option);
             sprintf(ParGC,"uGC->GetGC()");
          }
       }

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

       out << endl << "   // buttongroup frame" << endl;

       out << "   TGButtonGroup *";
       out << GetName() << " = new TGButtonGroup(" << fParent->GetName()
           << ","<< quote << fText->GetString() << quote;

       if (fBackground == GetDefaultFrameBackground()) {
          if (fFontStruct == GetDefaultFontStruct()) {
             if (fNormGC == GetDefaultGC()()) {
                if (!GetOptions()) {
                   out <<");" << endl;
                } else {
                   out << "," << GetOptionString() <<");" << endl;
                }
             } else {
                out << "," << GetOptionString() << "," << ParGC <<");" << endl;
             }
          } else {
             out << "," << GetOptionString() << "," << ParGC << "," << ParFont <<");" << endl;
          }
       } else {
         out << "," << GetOptionString() << "," << ParGC << "," << ParFont << ",ucolor);" << endl;
       }

       // setting layout manager
       out << "   " << GetName() <<"->SetLayoutManager(";
       GetLayoutManager()->SavePrimitive(out, option);
       out << ");"<< endl;

       TGFrameElement *f;
       TIter next(GetList());
       while ((f = (TGFrameElement *)next())) {
          f->fFrame->SavePrimitive(out,option);
       }

       if (IsExclusive())
          out << "   " << GetName() <<"->SetExclusive(kTRUE);" << endl;

       if (IsRadioButtonExclusive())
          out << "   " << GetName() <<"->SetRadioButtonExclusive(kTRUE);" << endl;

       if (!IsBorderDrawn())
          out << "   " << GetName() <<"->SetBorderDrawn(kFALSE);" << endl;

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

       out << "   " << GetName() << "->Show();" << endl;
    }

    //______________________________________________________________________________
    void TGHButtonGroup::SavePrimitive(ofstream &out, Option_t *option)
    {
       // Save a button group widget as a C++ statement(s) on output stream out

       char quote ='"';

       // font + GC
       option = GetName()+5;         // unique digit id of the name
       char ParGC[50], ParFont[50];
       sprintf(ParFont,"%s::GetDefaultFontStruct()",IsA()->GetName());
       sprintf(ParGC,"%s::GetDefaultGC()()",IsA()->GetName());

       if ((GetDefaultFontStruct() != fFontStruct) || (GetDefaultGC()() != fNormGC)) {
          TGFont *ufont = gClient->GetResourcePool()->GetFontPool()->FindFont(fFontStruct);
          if (ufont) {
             ufont->SavePrimitive(out, option);
             sprintf(ParFont,"ufont->GetFontStruct()");
          }

          TGGC *userGC = gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
          if (userGC) {
             userGC->SavePrimitive(out, option);
             sprintf(ParGC,"uGC->GetGC()");
          }
       }

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

       out << endl << "   // horizontal buttongroup frame" << endl;

       out << "   TGHButtonGroup *";
       out << GetName() << " = new TGHButtonGroup(" << fParent->GetName()
           << "," << quote << fText->GetString() << quote;
       if (fBackground == GetDefaultFrameBackground()) {

          if (fFontStruct == GetDefaultFontStruct()) {

             if (fNormGC == GetDefaultGC()()) {
                out << ");" << endl;
             } else {
                out << "," << ParGC <<");" << endl;
             }
          } else {
             out << "," << ParGC << "," << ParFont <<");" << endl;
          }
       } else {
          out << "," << ParGC << "," << ParFont << ",ucolor);" << endl;
       }

       TGFrameElement *f;
       TIter next(GetList());
       while ((f = (TGFrameElement *)next())) {
          f->fFrame->SavePrimitive(out,option);
       }

       if (IsExclusive())
          out << "   " << GetName() <<"->SetExclusive(kTRUE);" << endl;

       if (IsRadioButtonExclusive())
          out << "   " << GetName() <<"->SetRadioButtonExclusive(kTRUE);" << endl;

       if (!IsBorderDrawn())
          out << "   " << GetName() <<"->SetBorderDrawn(kFALSE);" << endl;

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

       out << "   " << GetName() << "->Show();" << endl;
    }

    //______________________________________________________________________________
     void TGVButtonGroup::SavePrimitive(ofstream &out, Option_t *option)
    {
       // Save a button group widget as a C++ statement(s) on output stream out

       char quote ='"';

       // font + GC
       option = GetName()+5;         // unique digit id of the name
       char ParGC[50], ParFont[50];
       sprintf(ParFont,"%s::GetDefaultFontStruct()",IsA()->GetName());
       sprintf(ParGC,"%s::GetDefaultGC()()",IsA()->GetName());

       if ((GetDefaultFontStruct() != fFontStruct) || (GetDefaultGC()() != fNormGC)) {
          TGFont *ufont = gClient->GetResourcePool()->GetFontPool()->FindFont(fFontStruct);
          if (ufont) {
             ufont->SavePrimitive(out, option);
             sprintf(ParFont,"ufont->GetFontStruct()");
          }

          TGGC *userGC = gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
          if (userGC) {
             userGC->SavePrimitive(out, option);
             sprintf(ParGC,"uGC->GetGC()");
          }
       }

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

       out << endl << "   // vertical buttongroup frame" << endl;

       out << "   TGVButtonGroup *";
       out << GetName() << " = new TGVButtonGroup(" << fParent->GetName()
           << "," << quote << fText->GetString() << quote;

       if (fBackground == GetDefaultFrameBackground()) {
          if (fFontStruct == GetDefaultFontStruct()) {
             if (fNormGC == GetDefaultGC()()) {
                out <<");" << endl;
             } else {
                out << "," << ParGC <<");" << endl;
             }
          } else {
             out << "," << ParGC << "," << ParFont <<");" << endl;
          }
       } else {
          out << "," << ParGC << "," << ParFont << ",ucolor);" << endl;
       }

       TGFrameElement *f;
       TIter next(GetList());
       while ((f = (TGFrameElement *)next())) {
          f->fFrame->SavePrimitive(out,option);
       }

       if (IsExclusive())
          out << "   " << GetName() <<"->SetExclusive(kTRUE);" << endl;

       if (IsRadioButtonExclusive())
          out << "   " << GetName() <<"->SetRadioButtonExclusive(kTRUE);" << endl;

       if (!IsBorderDrawn())
          out << "   " << GetName() <<"->SetBorderDrawn(kFALSE);" << endl;

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

       out << "   " << GetName() << "->Show();" << endl;
    }