<commit_before>// @(#)root/gui:$Name:  $:$Id: TGButton.cxx,v 1.14 2003/08/13 11:38:46 brun Exp $
// Author: <NAME>   06/01/98

/*************************************************************************
 * Copyright (C) 1995-2000, <NAME> and <NAME>.               *
 * 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 <NAME>, <NAME>, <NAME>.

    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.

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGButton, TGTextButton, TGPictureButton, TGCheckButton and           //
// TGRadioButton                                                        //
//                                                                      //
// This header defines all GUI button widgets.                          //
//                                                                      //
// TGButton is a button abstract base class. It defines general button  //
// behaviour.                                                           //
//                                                                      //
// Selecting a text or picture button will generate the event:          //
// kC_COMMAND, kCM_BUTTON, button id, user data.                        //
//                                                                      //
// Selecting a check button will generate the event:                    //
// kC_COMMAND, kCM_CHECKBUTTON, button id, user data.                   //
//                                                                      //
// Selecting a radio button will generate the event:                    //
// kC_COMMAND, kCM_RADIOBUTTON, button id, user data.                   //
//                                                                      //
// If a command string has been specified (via SetCommand()) then this  //
// command string will be executed via the interpreter whenever a       //
// button is selected. A command string can contain the macros:         //
// $MSG   -- kC_COMMAND, kCM[CHECK|RADIO]BUTTON packed message          //
//           (use GET_MSG() and GET_SUBMSG() to unpack)                 //
// $PARM1 -- button id                                                  //
// $PARM2 -- user data pointer                                          //
// Before executing these macros are expanded into the respective       //
// Long_t's                                                             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGButton.h"
#include "TGWidget.h"
#include "TGPicture.h"
#include "TGToolTip.h"
#include "TGButtonGroup.h"
#include "TGResourcePool.h"


const TGGC *TGButton::fgHibckgndGC = 0;
const TGGC *TGButton::fgDefaultGC = 0;

const TGFont *TGTextButton::fgDefaultFont = 0;

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

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



ClassImpQ(TGButton)
ClassImpQ(TGTextButton)
ClassImpQ(TGPictureButton)
ClassImpQ(TGCheckButton)
ClassImpQ(TGRadioButton)


//______________________________________________________________________________
TGButton::TGButton(const TGWindow *p, Int_t id, GContext_t norm, UInt_t options)
    : TGFrame(p, 1, 1, options)
{
   // Create button base class part.

   fWidgetId    = id;
   fWidgetFlags = kWidgetWantFocus;
   fMsgWindow   = p;
   fUserData    = 0;
   fTip         = 0;
   fGroup       = 0;

   fNormGC   = norm;
   fState    = kButtonUp;
   fStayDown = kFALSE;

   if (p && p->IsA()->InheritsFrom(TGButtonGroup::Class())) {
      TGButtonGroup *bg = (TGButtonGroup*) p;
      bg->Insert(this, id);
   }

   gVirtualX->GrabButton(fId, kButton1, kAnyModifier,
                    kButtonPressMask | kButtonReleaseMask,
                    kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGButton::~TGButton()
{
   // Delete button.

   // remove from button group
   if (fGroup) {
      fGroup->Remove(this);
      fGroup = 0;
   }

   delete fTip;
}

//______________________________________________________________________________
void TGButton::SetState(EButtonState state)
{
   // Set button state.

   Bool_t was = !IsDown();   // kTRUE if button was off

   if (state != fState) {
      switch (state) {
         case kButtonEngaged:
         case kButtonDown:
            fOptions &= ~kRaisedFrame;
            fOptions |= kSunkenFrame;
            break;
         case kButtonDisabled:
         case kButtonUp:
            fOptions &= ~kSunkenFrame;
            fOptions |= kRaisedFrame;
            break;
      }
      fState = state;
      fClient->NeedRedraw(this);
   }

   Bool_t now = !IsDown();               // kTRUE if button now is off

   // emit signals
   if (was && !now) {
      Pressed();                          // emit Pressed  = was off , now on
      if (fStayDown) Clicked();           // emit Clicked
   }

   if (!was && now) {
      Released();                         // emit Released = was on , now off
      Clicked();                          // emit Clicked
   }

   if ((was != now) && IsToggleButton()) Toggled(!now); // emit Toggled  = was != now
}

//______________________________________________________________________________
void TGButton::SetGroup(TGButtonGroup *group)
{
   // Sets new button-group for this button.

   fGroup = group;
}

//______________________________________________________________________________
Bool_t TGButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   Bool_t click = kFALSE;

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      if (fState == kButtonEngaged) return kTRUE;
      SetState(kButtonDown);
   } else { // ButtonRelease
      if (fState == kButtonEngaged /*&& !allowRelease*/) {
         click = kTRUE;
      } else {
         click = (fState == kButtonDown);
         if (click && fStayDown)
           SetState(kButtonEngaged);
         else
           SetState(kButtonUp);
      }
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonEngaged || fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify)
      SetState(kButtonDown);
   else
      SetState(kButtonUp);

   return kTRUE;
}

//______________________________________________________________________________
void TGButton::SetToolTipText(const char *text, Long_t delayms)
{
   // Set tool tip text associated with this button. The delay is in
   // milliseconds (minimum 250). To remove tool tip call method with
   // text = 0.

   if (fTip) {
      delete fTip;
      fTip = 0;
   }

   if (text && strlen(text))
      fTip = new TGToolTip(fClient->GetRoot(), this, text, delayms);
}

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

//______________________________________________________________________________
const TGGC &TGButton::GetHibckgndGC()
{
   if (!fgHibckgndGC) {
      GCValues_t gval;
      gval.fMask = kGCForeground | kGCBackground | kGCTile |
                   kGCFillStyle  | kGCGraphicsExposures;
      gval.fForeground = gClient->GetResourcePool()->GetFrameHiliteColor();
      gval.fBackground = gClient->GetResourcePool()->GetFrameBgndColor();
      gval.fFillStyle  = kFillTiled;
      gval.fTile       = gClient->GetResourcePool()->GetCheckeredPixmap();
      gval.fGraphicsExposures = kFALSE;
      fgHibckgndGC = gClient->GetGC(&gval, kTRUE);
   }
   return *fgHibckgndGC;
}


//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, TGHotString *s, Int_t id,
                           GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget. The hotstring will be adopted and deleted
   // by the text button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, const char *s, Int_t id,
                           GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, const char *s, const char *cmd,
                           Int_t id, GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget and set cmd string at same time.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGTextButton::Init()
{
   // Common initialization used by the different ctors.

   int hotchar, max_ascent, max_descent;

   fTMode = kTextCenterX | kTextCenterY;
   fHKeycode = 0;

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

   Resize(fTWidth + 8, fTHeight + 7);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }
}

//______________________________________________________________________________
TGTextButton::~TGTextButton()
{
   // Delete a text button widget.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGTextButton::SetText(TGHotString *new_label)
{
   // Set new button text.

   int hotchar;
   const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
   
   if (fLabel) {
      if (fHKeycode) 
         main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
      delete fLabel;
   }

   fLabel = new_label;
   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) 
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
   }
   
   int max_ascent, max_descent;
   fTWidth = gVirtualX->TextWidth(fFontStruct, fLabel->GetString(), fLabel->GetLength());
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   fTHeight = max_ascent + max_descent;

   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGTextButton::SetText(const TString &new_label)
{
   // Set new button text.

   SetText(new TGHotString(new_label));
}

//______________________________________________________________________________
void TGTextButton::DoRedraw()
{
   // Draw the text button.

   int x, y;
   int max_ascent, max_descent;

   TGFrame::DoRedraw();

   if (fTMode & kTextLeft)
      x = 4;
   else if (fTMode & kTextRight)
      x = fWidth - fTWidth - 4;
   else
      x = (fWidth - fTWidth) >> 1;

   if (fTMode & kTextTop)
      y = 3;
   else if (fTMode & kTextBottom)
      y = fHeight - fTHeight - 3;
   else
      y = (fHeight - fTHeight) >> 1;

   if (fState == kButtonDown || fState == kButtonEngaged) { ++x; ++y; }
   if (fState == kButtonEngaged) {
      gVirtualX->FillRectangle(fId, GetHibckgndGC()(), 2, 2, fWidth-4, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), 2, 2, fWidth-3, 2);
   }
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   if (fState == kButtonDisabled) {
      fLabel->Draw(fId, GetHilightGC()(), x+1, y+1 + max_ascent);
      fLabel->Draw(fId, GetShadowGC()(), x, y + max_ascent);
   } else {
      fLabel->Draw(fId, fNormGC, x, y + max_ascent);
   }
}

//______________________________________________________________________________
Bool_t TGTextButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   Bool_t click = kFALSE;

   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will
   // only allow fHotchar events
   if (event->fType == kGKeyPress) {
      if (fState == kButtonEngaged) return kTRUE;
      SetState(kButtonDown);
   } else { // KeyRelease
      if (fState == kButtonEngaged /*&& !allowRelease*/) return kTRUE;
      click = (fState == kButtonDown);
      if (click && fStayDown)
         SetState(kButtonEngaged);
      else
         SetState(kButtonUp);
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

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

//______________________________________________________________________________
void TGTextButton::SetFont(FontStruct_t font)
{
   // Changes text font

   if (font != fFontStruct) {
      FontH_t v = gVirtualX->GetFontHandle(font);
      if (!v) return;

      fFontStruct = font;
      TGGC normgc = *gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
      TGGC *gc = new TGGC(normgc); // copy
      gc->SetFont(v);
      fNormGC = gc->GetGC();
      Resize();
   }
}

//______________________________________________________________________________
void TGTextButton::SetFont(const char *fontName)
{
   // Changes text font specified by name

   TGFont *font = fClient->GetFont(fontName);
   if (font) {
      SetFont(font->GetFontStruct());
   }
}

//______________________________________________________________________________
void TGTextButton::SetTextColor(Pixel_t color)
{
   // Changes text color

   TGGC normgc = *gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
   TGGC *gc = new TGGC(normgc); // copy
   gc->SetForeground(color);
   fNormGC = gc->GetGC();
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const TGPicture *pic,
      Int_t id, GContext_t norm, UInt_t option) : TGButton(p, id, norm, option)
{
   // Create a picture button widget. The picture is not adopted and must
   // later be freed by the user once the picture button is deleted (a single
   // picture reference might be used by other buttons).

   if (!pic) {
      Error("TGPictureButton", "pixmap not found for button %d", id);
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else
      fPic = pic;

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const TGPicture *pic,
      const char *cmd, Int_t id, GContext_t norm, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a picture button widget and set action command. The picture is
   // not adopted and must later be freed by the user once the picture button
   // is deleted (a single picture reference might be used by other
   // buttons).

   if (!pic) {
      Error("TGPictureButton", "pixmap not found for button\n%s",
            cmd ? cmd : "");
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else {
      fPic = pic;
   }

   fCommand = cmd;

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const char* pic,
   Int_t id, GContext_t norm, UInt_t option ) : TGButton(p, id, norm, option)
{
   // Create a picture button

   if (!pic || !strlen(pic)) {
      Error("TGPictureButton", "pixmap not found for button");
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else {
      fPic = fClient->GetPicture(pic);
   }

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
void TGPictureButton::SetPicture(const TGPicture *new_pic)
{
   // Change a picture in a picture button. The picture is not adopted and
   // must later be freed by the user once the picture button is deleted
   // (a single picture reference might be used by other buttons).

   if (!new_pic) {
      Error("SetPicture", "pixmap not found for button %d\n%s",
            fWidgetId, fCommand.Data());
      return;
   }

   fPic = new_pic;

   fTWidth  = fPic->GetWidth();
   fTHeight = fPic->GetHeight();

   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGPictureButton::DoRedraw()
{
   // Redraw picture button.

   if (!fPic) {
      TGFrame::DoRedraw();
      return;
   }

   int x = (fWidth - fTWidth) >> 1;
   int y = (fHeight - fTHeight) >> 1;

   TGFrame::DoRedraw();
   if (fState == kButtonDown || fState == kButtonEngaged) { ++x; ++y; }
   if (fState == kButtonEngaged) {
      gVirtualX->FillRectangle(fId, GetHibckgndGC()(), 2, 2, fWidth-4, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), 2, 2, fWidth-3, 2);
   }
   fPic->Draw(fId, fNormGC, x, y);
}


//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, TGHotString *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a check button widget. The hotstring will be adopted and deleted
   // by the check button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, const char *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a check button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, const char *s, const char *cmd,
                             Int_t id, GContext_t norm, FontStruct_t font,
                             UInt_t option) : TGButton(p, id, norm, option)
{
   // Create a check button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGCheckButton::Init()
{
   // Common initialization.

   fPrevState =
   fState     = kButtonUp;
   fHKeycode = 0;

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

   Resize(fTWidth + 22, fTHeight + 2);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }
}

//______________________________________________________________________________
TGCheckButton::~TGCheckButton()
{
   // Delete a check button.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGCheckButton::PSetState(EButtonState state)
{
   // Set check button state.

   if (state != fState) {
      fState = state;

      // button signals
      if (fState == kButtonUp)   Released();            // emit Released
      if (fState == kButtonDown) Pressed();             // emit Pressed
      Clicked();                                        // emit Clicked
      Toggled(fState == kButtonDown);                   // emit Toggled

      fClient->NeedRedraw(this);
   }
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   Bool_t click = kFALSE;

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else { // ButtonRelease
      click = (fState != fPrevState);
      fPrevState = fState;
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON),
                           fWidgetId, (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else {
      PSetState(fPrevState);
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   Bool_t click = kFALSE;

   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will
   // only allow fHotchar events
   if (event->fType == kGKeyPress) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else { // KeyRelease
      click = (fState != fPrevState);
      fPrevState = fState;
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
void TGCheckButton::DoRedraw()
{
   // Draw the check button widget.

   int x, y, y0, cw;

   TGFrame::DoRedraw();

   cw = 13;
   y0 = (fHeight - cw) >> 1;

   gVirtualX->DrawLine(fId, GetShadowGC()(), 0, y0, cw-2, y0);
   gVirtualX->DrawLine(fId, GetShadowGC()(), 0, y0, 0, y0+cw-2);
   gVirtualX->DrawLine(fId, GetBlackGC()(), 1, y0+1, cw-3, y0+1);
   gVirtualX->DrawLine(fId, GetBlackGC()(), 1, y0+1, 1, y0+cw-3);

   gVirtualX->DrawLine(fId, GetHilightGC()(), 0, y0+cw-1, cw-1, y0+cw-1);
   gVirtualX->DrawLine(fId, GetHilightGC()(), cw-1, y0+cw-1, cw-1, y0);
   gVirtualX->DrawLine(fId, GetBckgndGC()(),  2, y0+cw-2, cw-2, y0+cw-2);
   gVirtualX->DrawLine(fId, GetBckgndGC()(),  cw-2, y0+2, cw-2, y0+cw-2);

   gVirtualX->FillRectangle(fId, GetWhiteGC()(), 2, y0+2, cw-4, cw-4);

   if (fState == kButtonDown) {
      Segment_t seg[6];

      int l = 2;
      int t = y0+2;

      seg[0].fX1 = 1+l; seg[0].fY1 = 3+t; seg[0].fX2 = 3+l; seg[0].fY2 = 5+t;
      seg[1].fX1 = 1+l; seg[1].fY1 = 4+t; seg[1].fX2 = 3+l; seg[1].fY2 = 6+t;
      seg[2].fX1 = 1+l; seg[2].fY1 = 5+t; seg[2].fX2 = 3+l; seg[2].fY2 = 7+t;
      seg[3].fX1 = 3+l; seg[3].fY1 = 5+t; seg[3].fX2 = 7+l; seg[3].fY2 = 1+t;
      seg[4].fX1 = 3+l; seg[4].fY1 = 6+t; seg[4].fX2 = 7+l; seg[4].fY2 = 2+t;
      seg[5].fX1 = 3+l; seg[5].fY1 = 7+t; seg[5].fX2 = 7+l; seg[5].fY2 = 3+t;

      gVirtualX->DrawSegments(fId, GetBlackGC()(), seg, 6);
   }

   x = 20;
   y = 1;

   int max_ascent, max_descent;
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   if (fState == kButtonDisabled) {
     fLabel->Draw(fId, GetHilightGC()(), x+1, y+1 + max_ascent);
     fLabel->Draw(fId, GetShadowGC()(), x, y + max_ascent);
   } else {
     fLabel->Draw(fId, fNormGC, x, y + max_ascent);
   }
}

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

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


//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, TGHotString *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a radio button widget. The hotstring will be adopted and deleted
   // by the radio button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, const char *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a radio button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}
//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, const char *s, const char *cmd,
                             Int_t id, GContext_t norm,
                             FontStruct_t font, UInt_t option)
    : TGButton(p, id, norm, option)
{
   // Create a radio button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGRadioButton::Init()
{
   // Common radio button initialization.

   fPrevState =
   fState     = kButtonUp;
   fHKeycode  = 0;

   fOn  = fClient->GetPicture("rbutton_on.xpm");
   fOff = fClient->GetPicture("rbutton_off.xpm");

   if (!fOn || !fOff)
      Error("TGRadioButton", "rbutton_*.xpm not found");

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

   Resize(fTWidth + 22, fTHeight + 2);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }

   if (fParent->IsA()->InheritsFrom(TGButtonGroup::Class())) {
      ((TGButtonGroup*)fParent)->SetRadioButtonExclusive(kTRUE);
   }
}

//______________________________________________________________________________
TGRadioButton::~TGRadioButton()
{
   // Delete a radio button.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fOn)  fClient->FreePicture(fOn);
   if (fOff) fClient->FreePicture(fOff);
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGRadioButton::PSetState(EButtonState state)
{
   // Set radio button state.

   if (state != fState) {
      fPrevState = fState = state;

      // button signals
      if (fState == kButtonUp)   Released();          // emit Released
      if (fState == kButtonDown) Pressed();           // emit Pressed
      Clicked();                                      // emit Clicked
      Toggled(fState == kButtonDown);                 // emit Toggled

      fClient->NeedRedraw(this);
   }
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      PSetState(kButtonDown);
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                           fWidgetId, (Long_t) fUserData);
   } else { // ButtonRelease
      fPrevState = fState;
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify) {
      PSetState(kButtonDown);
   } else {
      PSetState(fPrevState);
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will
   // only allow fHotchar events
   if (event->fType == kGKeyPress) {
      PSetState(kButtonDown);
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                           fWidgetId, (Long_t) fUserData);
   } else { // KeyRelease
      fPrevState = fState;
   }

   return kTRUE;
}

//______________________________________________________________________________
void TGRadioButton::DoRedraw()
{
   // Draw a radio button.

   int nlines, tx, ty, y0, pw;

   TGFrame::DoRedraw();
<commit_msg>From Ilka: there were no precise filters of keyboard events in HandleKey() button mods. This patch fixes HandleKey() methods of TGTextButton, TGCheckButton and TGRadioButton classes.<commit_after>// @(#)root/gui:$Name:  $:$Id: TGButton.cxx,v 1.15 2003/09/26 09:23:29 rdm Exp $
// Author: <NAME>   06/01/98

/*************************************************************************
 * Copyright (C) 1995-2000, <NAME> and <NAME>.               *
 * 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 <NAME>, <NAME>, <NAME>.

    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.

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGButton, TGTextButton, TGPictureButton, TGCheckButton and           //
// TGRadioButton                                                        //
//                                                                      //
// This header defines all GUI button widgets.                          //
//                                                                      //
// TGButton is a button abstract base class. It defines general button  //
// behaviour.                                                           //
//                                                                      //
// Selecting a text or picture button will generate the event:          //
// kC_COMMAND, kCM_BUTTON, button id, user data.                        //
//                                                                      //
// Selecting a check button will generate the event:                    //
// kC_COMMAND, kCM_CHECKBUTTON, button id, user data.                   //
//                                                                      //
// Selecting a radio button will generate the event:                    //
// kC_COMMAND, kCM_RADIOBUTTON, button id, user data.                   //
//                                                                      //
// If a command string has been specified (via SetCommand()) then this  //
// command string will be executed via the interpreter whenever a       //
// button is selected. A command string can contain the macros:         //
// $MSG   -- kC_COMMAND, kCM[CHECK|RADIO]BUTTON packed message          //
//           (use GET_MSG() and GET_SUBMSG() to unpack)                 //
// $PARM1 -- button id                                                  //
// $PARM2 -- user data pointer                                          //
// Before executing these macros are expanded into the respective       //
// Long_t's                                                             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGButton.h"
#include "TGWidget.h"
#include "TGPicture.h"
#include "TGToolTip.h"
#include "TGButtonGroup.h"
#include "TGResourcePool.h"


const TGGC *TGButton::fgHibckgndGC = 0;
const TGGC *TGButton::fgDefaultGC = 0;

const TGFont *TGTextButton::fgDefaultFont = 0;

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

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



ClassImpQ(TGButton)
ClassImpQ(TGTextButton)
ClassImpQ(TGPictureButton)
ClassImpQ(TGCheckButton)
ClassImpQ(TGRadioButton)


//______________________________________________________________________________
TGButton::TGButton(const TGWindow *p, Int_t id, GContext_t norm, UInt_t options)
    : TGFrame(p, 1, 1, options)
{
   // Create button base class part.

   fWidgetId    = id;
   fWidgetFlags = kWidgetWantFocus;
   fMsgWindow   = p;
   fUserData    = 0;
   fTip         = 0;
   fGroup       = 0;

   fNormGC   = norm;
   fState    = kButtonUp;
   fStayDown = kFALSE;

   if (p && p->IsA()->InheritsFrom(TGButtonGroup::Class())) {
      TGButtonGroup *bg = (TGButtonGroup*) p;
      bg->Insert(this, id);
   }

   gVirtualX->GrabButton(fId, kButton1, kAnyModifier,
                    kButtonPressMask | kButtonReleaseMask,
                    kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGButton::~TGButton()
{
   // Delete button.

   // remove from button group
   if (fGroup) {
      fGroup->Remove(this);
      fGroup = 0;
   }

   delete fTip;
}

//______________________________________________________________________________
void TGButton::SetState(EButtonState state)
{
   // Set button state.

   Bool_t was = !IsDown();   // kTRUE if button was off

   if (state != fState) {
      switch (state) {
         case kButtonEngaged:
         case kButtonDown:
            fOptions &= ~kRaisedFrame;
            fOptions |= kSunkenFrame;
            break;
         case kButtonDisabled:
         case kButtonUp:
            fOptions &= ~kSunkenFrame;
            fOptions |= kRaisedFrame;
            break;
      }
      fState = state;
      fClient->NeedRedraw(this);
   }

   Bool_t now = !IsDown();               // kTRUE if button now is off

   // emit signals
   if (was && !now) {
      Pressed();                          // emit Pressed  = was off , now on
      if (fStayDown) Clicked();           // emit Clicked
   }

   if (!was && now) {
      Released();                         // emit Released = was on , now off
      Clicked();                          // emit Clicked
   }

   if ((was != now) && IsToggleButton()) Toggled(!now); // emit Toggled  = was != now
}

//______________________________________________________________________________
void TGButton::SetGroup(TGButtonGroup *group)
{
   // Sets new button-group for this button.

   fGroup = group;
}

//______________________________________________________________________________
Bool_t TGButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   Bool_t click = kFALSE;

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      if (fState == kButtonEngaged) return kTRUE;
      SetState(kButtonDown);
   } else { // ButtonRelease
      if (fState == kButtonEngaged /*&& !allowRelease*/) {
         click = kTRUE;
      } else {
         click = (fState == kButtonDown);
         if (click && fStayDown)
           SetState(kButtonEngaged);
         else
           SetState(kButtonUp);
      }
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonEngaged || fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify)
      SetState(kButtonDown);
   else
      SetState(kButtonUp);

   return kTRUE;
}

//______________________________________________________________________________
void TGButton::SetToolTipText(const char *text, Long_t delayms)
{
   // Set tool tip text associated with this button. The delay is in
   // milliseconds (minimum 250). To remove tool tip call method with
   // text = 0.

   if (fTip) {
      delete fTip;
      fTip = 0;
   }

   if (text && strlen(text))
      fTip = new TGToolTip(fClient->GetRoot(), this, text, delayms);
}

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

//______________________________________________________________________________
const TGGC &TGButton::GetHibckgndGC()
{
   if (!fgHibckgndGC) {
      GCValues_t gval;
      gval.fMask = kGCForeground | kGCBackground | kGCTile |
                   kGCFillStyle  | kGCGraphicsExposures;
      gval.fForeground = gClient->GetResourcePool()->GetFrameHiliteColor();
      gval.fBackground = gClient->GetResourcePool()->GetFrameBgndColor();
      gval.fFillStyle  = kFillTiled;
      gval.fTile       = gClient->GetResourcePool()->GetCheckeredPixmap();
      gval.fGraphicsExposures = kFALSE;
      fgHibckgndGC = gClient->GetGC(&gval, kTRUE);
   }
   return *fgHibckgndGC;
}


//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, TGHotString *s, Int_t id,
                           GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget. The hotstring will be adopted and deleted
   // by the text button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, const char *s, Int_t id,
                           GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGTextButton::TGTextButton(const TGWindow *p, const char *s, const char *cmd,
                           Int_t id, GContext_t norm, FontStruct_t font,
                           UInt_t options) : TGButton(p, id, norm, options)
{
   // Create a text button widget and set cmd string at same time.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGTextButton::Init()
{
   // Common initialization used by the different ctors.

   int hotchar, max_ascent, max_descent;

   fTMode = kTextCenterX | kTextCenterY;
   fHKeycode = 0;

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

   Resize(fTWidth + 8, fTHeight + 7);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }
}

//______________________________________________________________________________
TGTextButton::~TGTextButton()
{
   // Delete a text button widget.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGTextButton::SetText(TGHotString *new_label)
{
   // Set new button text.

   int hotchar;
   const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
   
   if (fLabel) {
      if (fHKeycode) 
         main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
      delete fLabel;
   }

   fLabel = new_label;
   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) 
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
   }
   
   int max_ascent, max_descent;
   fTWidth = gVirtualX->TextWidth(fFontStruct, fLabel->GetString(), fLabel->GetLength());
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   fTHeight = max_ascent + max_descent;

   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGTextButton::SetText(const TString &new_label)
{
   // Set new button text.

   SetText(new TGHotString(new_label));
}

//______________________________________________________________________________
void TGTextButton::DoRedraw()
{
   // Draw the text button.

   int x, y;
   int max_ascent, max_descent;

   TGFrame::DoRedraw();

   if (fTMode & kTextLeft)
      x = 4;
   else if (fTMode & kTextRight)
      x = fWidth - fTWidth - 4;
   else
      x = (fWidth - fTWidth) >> 1;

   if (fTMode & kTextTop)
      y = 3;
   else if (fTMode & kTextBottom)
      y = fHeight - fTHeight - 3;
   else
      y = (fHeight - fTHeight) >> 1;

   if (fState == kButtonDown || fState == kButtonEngaged) { ++x; ++y; }
   if (fState == kButtonEngaged) {
      gVirtualX->FillRectangle(fId, GetHibckgndGC()(), 2, 2, fWidth-4, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), 2, 2, fWidth-3, 2);
   }
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   if (fState == kButtonDisabled) {
      fLabel->Draw(fId, GetHilightGC()(), x+1, y+1 + max_ascent);
      fLabel->Draw(fId, GetShadowGC()(), x, y + max_ascent);
   } else {
      fLabel->Draw(fId, fNormGC, x, y + max_ascent);
   }
}

//______________________________________________________________________________
Bool_t TGTextButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   Bool_t click = kFALSE;

   if (event->fType == kGKeyPress) 
      gVirtualX->SetKeyAutoRepeat(kFALSE);
   else 
      gVirtualX->SetKeyAutoRepeat(kTRUE);
  
   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will only 
   // allow fHotchar events if Alt button is pressed (kKeyMod1Mask)

   if ((event->fType == kGKeyPress) && (event->fState & kKeyMod1Mask)) {
      if (fState == kButtonEngaged) return kTRUE;
      SetState(kButtonDown);
   } else if ((event->fType == kKeyRelease) && (event->fState & kKeyMod1Mask)) { 
      if (fState == kButtonEngaged /*&& !allowRelease*/) return kTRUE;
      click = (fState == kButtonDown);
      if (click && fStayDown)
         SetState(kButtonEngaged);
      else
         SetState(kButtonUp);
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_BUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

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

//______________________________________________________________________________
void TGTextButton::SetFont(FontStruct_t font)
{
   // Changes text font

   if (font != fFontStruct) {
      FontH_t v = gVirtualX->GetFontHandle(font);
      if (!v) return;

      fFontStruct = font;
      TGGC normgc = *gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
      TGGC *gc = new TGGC(normgc); // copy
      gc->SetFont(v);
      fNormGC = gc->GetGC();
      Resize();
   }
}

//______________________________________________________________________________
void TGTextButton::SetFont(const char *fontName)
{
   // Changes text font specified by name

   TGFont *font = fClient->GetFont(fontName);
   if (font) {
      SetFont(font->GetFontStruct());
   }
}

//______________________________________________________________________________
void TGTextButton::SetTextColor(Pixel_t color)
{
   // Changes text color

   TGGC normgc = *gClient->GetResourcePool()->GetGCPool()->FindGC(fNormGC);
   TGGC *gc = new TGGC(normgc); // copy
   gc->SetForeground(color);
   fNormGC = gc->GetGC();
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const TGPicture *pic,
      Int_t id, GContext_t norm, UInt_t option) : TGButton(p, id, norm, option)
{
   // Create a picture button widget. The picture is not adopted and must
   // later be freed by the user once the picture button is deleted (a single
   // picture reference might be used by other buttons).

   if (!pic) {
      Error("TGPictureButton", "pixmap not found for button %d", id);
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else
      fPic = pic;

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const TGPicture *pic,
      const char *cmd, Int_t id, GContext_t norm, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a picture button widget and set action command. The picture is
   // not adopted and must later be freed by the user once the picture button
   // is deleted (a single picture reference might be used by other
   // buttons).

   if (!pic) {
      Error("TGPictureButton", "pixmap not found for button\n%s",
            cmd ? cmd : "");
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else {
      fPic = pic;
   }

   fCommand = cmd;

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
TGPictureButton::TGPictureButton(const TGWindow *p, const char* pic,
   Int_t id, GContext_t norm, UInt_t option ) : TGButton(p, id, norm, option)
{
   // Create a picture button

   if (!pic || !strlen(pic)) {
      Error("TGPictureButton", "pixmap not found for button");
      fPic = fClient->GetPicture("mb_question_s.xpm");
   } else {
      fPic = fClient->GetPicture(pic);
   }

   if (fPic) {
      fTWidth  = fPic->GetWidth();
      fTHeight = fPic->GetHeight();

      Resize(fTWidth  + (fBorderWidth << 1) + fBorderWidth + 1,
             fTHeight + (fBorderWidth << 1) + fBorderWidth); // *3
   }
}

//______________________________________________________________________________
void TGPictureButton::SetPicture(const TGPicture *new_pic)
{
   // Change a picture in a picture button. The picture is not adopted and
   // must later be freed by the user once the picture button is deleted
   // (a single picture reference might be used by other buttons).

   if (!new_pic) {
      Error("SetPicture", "pixmap not found for button %d\n%s",
            fWidgetId, fCommand.Data());
      return;
   }

   fPic = new_pic;

   fTWidth  = fPic->GetWidth();
   fTHeight = fPic->GetHeight();

   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGPictureButton::DoRedraw()
{
   // Redraw picture button.

   if (!fPic) {
      TGFrame::DoRedraw();
      return;
   }

   int x = (fWidth - fTWidth) >> 1;
   int y = (fHeight - fTHeight) >> 1;

   TGFrame::DoRedraw();
   if (fState == kButtonDown || fState == kButtonEngaged) { ++x; ++y; }
   if (fState == kButtonEngaged) {
      gVirtualX->FillRectangle(fId, GetHibckgndGC()(), 2, 2, fWidth-4, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), 2, 2, fWidth-3, 2);
   }
   fPic->Draw(fId, fNormGC, x, y);
}


//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, TGHotString *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a check button widget. The hotstring will be adopted and deleted
   // by the check button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, const char *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a check button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGCheckButton::TGCheckButton(const TGWindow *p, const char *s, const char *cmd,
                             Int_t id, GContext_t norm, FontStruct_t font,
                             UInt_t option) : TGButton(p, id, norm, option)
{
   // Create a check button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGCheckButton::Init()
{
   // Common initialization.

   fPrevState =
   fState     = kButtonUp;
   fHKeycode = 0;

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

   Resize(fTWidth + 22, fTHeight + 2);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }
}

//______________________________________________________________________________
TGCheckButton::~TGCheckButton()
{
   // Delete a check button.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGCheckButton::PSetState(EButtonState state)
{
   // Set check button state.

   if (state != fState) {
      fState = state;

      // button signals
      if (fState == kButtonUp)   Released();            // emit Released
      if (fState == kButtonDown) Pressed();             // emit Pressed
      Clicked();                                        // emit Clicked
      Toggled(fState == kButtonDown);                   // emit Toggled

      fClient->NeedRedraw(this);
   }
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   Bool_t click = kFALSE;

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else { // ButtonRelease
      click = (fState != fPrevState);
      fPrevState = fState;
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON),
                           fWidgetId, (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else {
      PSetState(fPrevState);
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGCheckButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   Bool_t click = kFALSE;

   if (event->fType == kGKeyPress) 
      gVirtualX->SetKeyAutoRepeat(kFALSE);
   else 
      gVirtualX->SetKeyAutoRepeat(kTRUE);
  
   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will only 
   // allow fHotchar events if Alt button is pressed (kKeyMod1Mask)

   if ((event->fType == kGKeyPress) && (event->fState & kKeyMod1Mask)) {
      PSetState((fPrevState == kButtonUp) ? kButtonDown : kButtonUp);
   } else if ((event->fType == kKeyRelease) && (event->fState & kKeyMod1Mask)) { 
      click = (fState != fPrevState);
      fPrevState = fState;
   }
   if (click) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON), fWidgetId,
                  (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_CHECKBUTTON), fWidgetId,
                           (Long_t) fUserData);
   }

   return kTRUE;
}

//______________________________________________________________________________
void TGCheckButton::DoRedraw()
{
   // Draw the check button widget.

   int x, y, y0, cw;

   TGFrame::DoRedraw();

   cw = 13;
   y0 = (fHeight - cw) >> 1;

   gVirtualX->DrawLine(fId, GetShadowGC()(), 0, y0, cw-2, y0);
   gVirtualX->DrawLine(fId, GetShadowGC()(), 0, y0, 0, y0+cw-2);
   gVirtualX->DrawLine(fId, GetBlackGC()(), 1, y0+1, cw-3, y0+1);
   gVirtualX->DrawLine(fId, GetBlackGC()(), 1, y0+1, 1, y0+cw-3);

   gVirtualX->DrawLine(fId, GetHilightGC()(), 0, y0+cw-1, cw-1, y0+cw-1);
   gVirtualX->DrawLine(fId, GetHilightGC()(), cw-1, y0+cw-1, cw-1, y0);
   gVirtualX->DrawLine(fId, GetBckgndGC()(),  2, y0+cw-2, cw-2, y0+cw-2);
   gVirtualX->DrawLine(fId, GetBckgndGC()(),  cw-2, y0+2, cw-2, y0+cw-2);

   gVirtualX->FillRectangle(fId, GetWhiteGC()(), 2, y0+2, cw-4, cw-4);

   if (fState == kButtonDown) {
      Segment_t seg[6];

      int l = 2;
      int t = y0+2;

      seg[0].fX1 = 1+l; seg[0].fY1 = 3+t; seg[0].fX2 = 3+l; seg[0].fY2 = 5+t;
      seg[1].fX1 = 1+l; seg[1].fY1 = 4+t; seg[1].fX2 = 3+l; seg[1].fY2 = 6+t;
      seg[2].fX1 = 1+l; seg[2].fY1 = 5+t; seg[2].fX2 = 3+l; seg[2].fY2 = 7+t;
      seg[3].fX1 = 3+l; seg[3].fY1 = 5+t; seg[3].fX2 = 7+l; seg[3].fY2 = 1+t;
      seg[4].fX1 = 3+l; seg[4].fY1 = 6+t; seg[4].fX2 = 7+l; seg[4].fY2 = 2+t;
      seg[5].fX1 = 3+l; seg[5].fY1 = 7+t; seg[5].fX2 = 7+l; seg[5].fY2 = 3+t;

      gVirtualX->DrawSegments(fId, GetBlackGC()(), seg, 6);
   }

   x = 20;
   y = 1;

   int max_ascent, max_descent;
   gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
   if (fState == kButtonDisabled) {
     fLabel->Draw(fId, GetHilightGC()(), x+1, y+1 + max_ascent);
     fLabel->Draw(fId, GetShadowGC()(), x, y + max_ascent);
   } else {
     fLabel->Draw(fId, fNormGC, x, y + max_ascent);
   }
}

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

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


//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, TGHotString *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a radio button widget. The hotstring will be adopted and deleted
   // by the radio button.

   fLabel = s;
   fFontStruct = font;

   Init();
}

//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, const char *s, Int_t id,
                             GContext_t norm, FontStruct_t font, UInt_t option)
   : TGButton(p, id, norm, option)
{
   // Create a radio button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;

   Init();
}
//______________________________________________________________________________
TGRadioButton::TGRadioButton(const TGWindow *p, const char *s, const char *cmd,
                             Int_t id, GContext_t norm,
                             FontStruct_t font, UInt_t option)
    : TGButton(p, id, norm, option)
{
   // Create a radio button widget.

   fLabel = new TGHotString(s);
   fFontStruct = font;
   fCommand = cmd;

   Init();
}

//______________________________________________________________________________
void TGRadioButton::Init()
{
   // Common radio button initialization.

   fPrevState =
   fState     = kButtonUp;
   fHKeycode  = 0;

   fOn  = fClient->GetPicture("rbutton_on.xpm");
   fOff = fClient->GetPicture("rbutton_off.xpm");

   if (!fOn || !fOff)
      Error("TGRadioButton", "rbutton_*.xpm not found");

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

   Resize(fTWidth + 22, fTHeight + 2);

   if ((hotchar = fLabel->GetHotChar()) != 0) {
      if ((fHKeycode = gVirtualX->KeysymToKeycode(hotchar)) != 0) {
         const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
         main->BindKey(this, fHKeycode, kKeyMod1Mask);
      }
   }

   if (fParent->IsA()->InheritsFrom(TGButtonGroup::Class())) {
      ((TGButtonGroup*)fParent)->SetRadioButtonExclusive(kTRUE);
   }
}

//______________________________________________________________________________
TGRadioButton::~TGRadioButton()
{
   // Delete a radio button.

   if (fHKeycode) {
      const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
      main->RemoveBind(this, fHKeycode, kKeyMod1Mask);
   }
   if (fOn)  fClient->FreePicture(fOn);
   if (fOff) fClient->FreePicture(fOff);
   if (fLabel) delete fLabel;
}

//______________________________________________________________________________
void TGRadioButton::PSetState(EButtonState state)
{
   // Set radio button state.

   if (state != fState) {
      fPrevState = fState = state;

      // button signals
      if (fState == kButtonUp)   Released();          // emit Released
      if (fState == kButtonDown) Pressed();           // emit Pressed
      Clicked();                                      // emit Clicked
      Toggled(fState == kButtonDown);                 // emit Toggled

      fClient->NeedRedraw(this);
   }
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleButton(Event_t *event)
{
   // Handle mouse button event.

   if (fTip) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the button number as GrabButton will
   // only allow button1 events
   if (event->fType == kButtonPress) {
      PSetState(kButtonDown);
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                           fWidgetId, (Long_t) fUserData);
   } else { // ButtonRelease
      fPrevState = fState;
   }

   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleCrossing(Event_t *event)
{
   // Handle mouse crossing event.

   if (fTip) {
      if (event->fType == kEnterNotify)
         fTip->Reset();
      else
         fTip->Hide();
   }

   if (fgDbw != event->fWindow) return kTRUE;

   if (!(event->fState & (kButton1Mask | kButton2Mask | kButton3Mask)))
      return kTRUE;

   if (fState == kButtonDisabled) return kTRUE;

   if (event->fType == kEnterNotify) {
      PSetState(kButtonDown);
   } else {
      PSetState(fPrevState);
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGRadioButton::HandleKey(Event_t *event)
{
   // Handle key event. This function will be called when the hotkey is hit.

   if (event->fType == kGKeyPress) 
      gVirtualX->SetKeyAutoRepeat(kFALSE);
   else 
      gVirtualX->SetKeyAutoRepeat(kTRUE);
  
   if (fTip && event->fType == kGKeyPress) fTip->Hide();

   if (fState == kButtonDisabled) return kTRUE;

   // We don't need to check the key number as GrabKey will only 
   // allow fHotchar events if Alt button is pressed (kKeyMod1Mask)

   if ((event->fType == kGKeyPress) && (event->fState & kKeyMod1Mask)) {
      PSetState(kButtonDown);
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                  fWidgetId, (Long_t) fUserData);
      fClient->ProcessLine(fCommand, MK_MSG(kC_COMMAND, kCM_RADIOBUTTON),
                           fWidgetId, (Long_t) fUserData);
   } else if ((event->fType == kKeyRelease) && (event->fState & kKeyMod1Mask)) { 
      fPrevState = fState;
   }

   return kTRUE;
}

//______________________________________________________________________________
void TGRadioButton::DoRedraw()
{
   // Draw a radio button.

   int nlines, tx, ty, y0, pw;

   TGFrame::DoRedraw();

{'fraction_non_alphanumeric': 0.12521866960676223, 'fraction_numerical': 0.008393972804116134, 'mean_word_length': 2.7270436116589964, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 363, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 84, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1315729', 'n_tokens_mistral': 21058, 'n_tokens_neox': 20076, 'n_words': 4860}