//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1998 by Bidus Yura, All Rights Reserved
//
/// \file
/// Implementation of the TCoolBar class
//----------------------------------------------------------------------------
#include <owl/pch.h>
 
#include <owl/coolbar.h>
#include <owl/imagelst.h>
#include <owl/tooltip.h>
#include <owl/functor.h>
#include <stdio.h>
 
namespace owl {
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlCommCtrl);        // CommonCtrl Diagnostic group
 
static uint32
SizeOfReBarBandInfo(REBARBANDINFO& info)
{
  info.cbSize = sizeof(REBARBANDINFO);
  return info.cbSize;
}
 
//
// Constructor of TCoolBar control
//
TCoolBar::TCoolBar(TWindow* parent, int id, int x, int y, int w, int h,
                   TModule* module)
:
  TControl(parent, id, _T(""), x, y, w, h, module),
  BgColor(GETPUTOBJMFUNCTOR(TColor,TCoolBar,GetBgColor,SetBgColor)),
  TextColor(GETPUTOBJMFUNCTOR(TColor,TCoolBar,GetTextColor,SetTextColor)),
  ColorScheme(GETPUTOBJMFUNCTOR(TColorScheme,TCoolBar,GetColorScheme,SetColorScheme)),
  Palette(FPalette,PUTMFUNCTOR(TPalette*,TCoolBar,SetPalette)),
  Buddy(FBuddy,PUTMFUNCTOR(TWindow*,TCoolBar,SetBuddy)),
  Tooltip(FTooltip,PUTMFUNCTOR(TTooltip*,TCoolBar,SetTooltip)),
  Unicode(GETPUTMFUNCTOR(bool,TCoolBar,GetUnicode,SetUnicode)),
  Bands(GETOBJMFUNCTOR(TCoolBandArray,TCoolBar,GetBands))
{
  // Modify the style to some reasonable defaults...
  // ... this comes from the Microsoft example by Nancy Winnick Cluts
  Attr.Style = WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|
               CCS_NODIVIDER|CCS_NOPARENTALIGN|
               RBS_VARHEIGHT     | // This style is trashed by TDecoratedFrame::Insert
               RBS_BANDBORDERS;    // This style is trashed by TDecoratedFrame::Insert
               //WS_BORDER;        // This style must be added after TDecoratedFrame::Insert
  Init();
}
 
 
TCoolBar::TCoolBar(TWindow* parent, int resourceId, TModule* module)
:
  TControl(parent, resourceId, module),
  BgColor(GETPUTOBJMFUNCTOR(TColor,TCoolBar,GetBgColor,SetBgColor)),
  TextColor(GETPUTOBJMFUNCTOR(TColor,TCoolBar,GetTextColor,SetTextColor)),
  ColorScheme(GETPUTOBJMFUNCTOR(TColorScheme,TCoolBar,GetColorScheme,SetColorScheme)),
  Palette(FPalette,PUTMFUNCTOR(TPalette*,TCoolBar,SetPalette)),
  Buddy(FBuddy,PUTMFUNCTOR(TWindow*,TCoolBar,SetBuddy)),
  Tooltip(FTooltip,PUTMFUNCTOR(TTooltip*,TCoolBar,SetTooltip)),
  Unicode(GETPUTMFUNCTOR(bool,TCoolBar,GetUnicode,SetUnicode)),
  Bands(GETOBJMFUNCTOR(TCoolBandArray,TCoolBar,GetBands))
{
  Init();
}
 
//
//
// Destructor
TCoolBar::~TCoolBar()
{
  if(ShouldDelete)
    delete FBandImages;
  delete FColorScheme;
  delete FPalette;
  delete FBands;
  //delete FTooltip; //???????????????????
}
 
//
//
//
void
TCoolBar::Init()
{
  InitializeCommonControls(ICC_COOL_CLASSES);
 
  // Initialise the image list pointer
  FBands        = new TCoolBandArray;
  FBandImages   = 0;
  FBuddy        = 0;
  FPalette       = 0;
  FColorScheme  = 0;
  FBgColor        = TColor::None;
  FTextColor     = TColor::None;
  FTooltip      = 0;
}
 
// Return the proper class name.
// Windows class: REBARCLASSNAME is defined in commctrl.h
auto TCoolBar::GetWindowClassName() -> TWindowClassName
{
  return TWindowClassName{REBARCLASSNAME};
}
 
//
//
//
void
TCoolBar::SetupWindow()
{
  ModifyStyle(0,CCS_NODIVIDER|CCS_NOPARENTALIGN|RBS_VARHEIGHT|RBS_BANDBORDERS|WS_BORDER);
 
  TControl::SetupWindow();
 
  // Initialise the rebar.
  // This would typically be used to associate an image list with
  // the rebar. It is not clear yet whether the rebar cleans up this
  // image list itself. If I use a TImageList for this who deletes it?
  // If me is the handle still valid? My treatment seems to work.
 
  Info.fMask &= ~RBIM_IMAGELIST;
  Info.himl = 0;
  if(FBandImages)
    Info.SetImageList(*FBandImages);
  SendMessage(RB_SETBARINFO, 0, TParam2(&Info));
  if(FColorScheme)
    SendMessage(RB_SETCOLORSCHEME,0,TParam2(FColorScheme));
  if(FPalette)
    SendMessage(RB_SETPALETTE,0,TParam2(FPalette->GetHandle()));
  if(FBgColor != TColor::None)
    SendMessage(RB_SETBKCOLOR, 0, TParam2((COLORREF)FBgColor));
  if(FTextColor != TColor::None)
    SendMessage(RB_SETTEXTCOLOR, 0, TParam2((COLORREF)FTextColor));
  if(FBuddy && FBuddy->GetHandle())
    SendMessage(RB_SETPARENT, TParam1(FBuddy->GetHandle()));
  if(FTooltip){
    FTooltip->SetParent(this);
    if(!FTooltip->GetHandle())
      FTooltip->Create();
    SendMessage(RB_SETTOOLTIPS, TParam1(FTooltip->GetHandle()));
  }
  for(int i = 0; i < (int)FBands->Size(); i++){
    REBARBANDINFO info;
    (*FBands)[i]->SetupData(this, info);
    SendMessage(RB_INSERTBAND, i, TParam2(&info));
  }
}
 
//#define RB_INSERTBANDA  (WM_USER +  1)
//#define RB_INSERTBANDW  (WM_USER +  10)
//Message      RB_INSERTBAND
//Description  Inserts a band into a rebar.
//wParam       UINT - the zero-based index of the place to insert the band; -1 signifies
//             that the band will be added to the end
//lParam       LPREBARINFO [???] - pointer to a structure containing information about the band
//Returns      0 if successful; non-zero otherwise
bool
TCoolBar::Insert(TCoolBand* band, uint index)
{
  uint idx = index;
  if(index == static_cast<uint>(-1)) //JJH added static_cast
    idx = FBands->Add(band);
  else
    FBands->AddAt(band, index);
  if (GetHandle()){
    REBARBANDINFO info;
    (*FBands)[idx]->SetupData(this, info);
    return SendMessage(RB_INSERTBAND, index, TParam2(&info)) == 0;
  }
  return true;
}
 
 
//#define RB_DELETEBAND   (WM_USER +  2)
//Message      RB_DELETEBAND
//Description  Removes a band from the rebar.
//wParam       UINT - the zero-based index of the band to delete
//lParam       0 (not used)
//Returns      0 if successful; non-zero otherwise
bool
TCoolBar::Delete(uint index)
{
  FBands->Destroy(index);
  if (GetHandle())
    return SendMessage(RB_DELETEBAND, index)==0;
  return true;
}
 
//#define RB_GETBARINFO   (WM_USER +  3)
//Message      RB_GETBARINFO
//Description  Retrieves information about the rebar control and the image list it may use.
//wParam       0 (not used)
//lParam       LPREBARINFO - pointer to a structure containing rebar information
//Returns      0 if successful; non-zero otherwise
bool
TCoolBar::GetInfo(TRebarInfo& info)
{
  if (GetHandle())
    return SendMessage(RB_GETBARINFO, 0, TParam2(&info))==0;
  info = Info;
  return true;
}
 
//#define RB_SETBARINFO   (WM_USER +  4)
//Message      RB_SETBARINFO
//Description  Sets information about the rebar.
//wParam       0
//lParam       LPREBARINFO - pointer to a structure containing new rebar information
//Returns      0 if successful; non-zero otherwise
bool
TCoolBar::SetInfo(TRebarInfo& info)
{
  Info = info;
  Info.fMask &= ~RBIM_IMAGELIST;
  Info.himl = 0;
  if(FBandImages)
    Info.SetImageList(*FBandImages); // Sets NULL aswell to use no images.
  if (GetHandle())
    return SendMessage(RB_SETBARINFO, 0, TParam2(&Info))==0;
  return true;
}
 
//#define RB_SETPARENT    (WM_USER +  7)
//Message      RB_SETPARENT
//Description  Sets the window that will receive rebar notification messages. Note that this
//             message does not actually set the parent of the Rebar control. When you call
//             GetParent, you will still get the handle to the original parent window that was
//             specified when the Rebar control was created.
//wParam       HWND - handle to the new parent window
//lParam       0
//Returns      HWND - handle to the previous parent window; NULL if there is no previous parent
void
TCoolBar::SetBuddy(TWindow* buddy)
{
  FBuddy = buddy;
  if(FBuddy && FBuddy->GetHandle() && GetHandle())
    SendMessage(RB_SETPARENT, TParam1(FBuddy->GetHandle()));
}
 
void
TCoolBar::SetTooltip(TTooltip* tip)
{
  if(GetHandle())
    SendMessage(RB_SETTOOLTIPS, TParam1(tip->GetHandle()));
  delete FTooltip;
  FTooltip = tip;
}
 
//#define RB_GETBANDCOUNT (WM_USER +  12)
//Message      RB_GETBANDCOUNT
//Description  Returns the number of bands in the rebar.
//wParam       0 (not used)
//lParam       0 (not used)
//Returns      UINT - Number of bands in the rebar
uint
TCoolBar::GetBandCount()
{
  if(GetHandle())
    return (uint)SendMessage(RB_GETBANDCOUNT);
  return FBands->Size();
}
 
//
//
//
void
TCoolBar::SetImageList(TImageList* images, TAutoDelete del)
{
  // This is intended to change the imagelist after creation...
  // ... as yet untested.
  // Delete the old image list and assign the new one.
  if(ShouldDelete)
    delete FBandImages;
  Info.fMask &= ~RBIM_IMAGELIST;
  Info.himl = 0;
  FBandImages = images;
  ShouldDelete = del == AutoDelete;
  if(FBandImages)
    Info.SetImageList(*FBandImages); // Sets NULL aswell to use no images.
 
  if (GetHandle())
    SendMessage(RB_SETBARINFO, 0, TParam2(&Info));
}
 
//
//
//
bool
TCoolBar::ToggleBandBorders()
{
  // Check the existing style
  bool result = GetStyle() & (uint32)RBS_BANDBORDERS;
  if(result)
    ModifyStyle(RBS_BANDBORDERS, 0, 0);
  else
    ModifyStyle(0, RBS_BANDBORDERS, 0);
 
  // Set the window position to force a redraw - the "+ 1" seems to do the trick
  if(GetHandle()){
    TRect rect = GetWindowRect();
    SetWindowPos(0,0,0,rect.Width(),rect.Height()+1,SWP_NOZORDER|SWP_NOCOPYBITS);
  }
 
  // Return the new setting
  return !result;
}
 
 
//
//
//
bool
TCoolBar::ToggleVarHeight()
{
  // Check the existing style
  bool result = GetStyle() & (uint32)RBS_VARHEIGHT;
  if(result)
    ModifyStyle(RBS_VARHEIGHT, 0, 0);
  else
    ModifyStyle(0, RBS_VARHEIGHT, 0);
 
  // Set the window position to force a full redraw. The "+ 1" seems to do the trick
  if(GetHandle()){
    TRect rec = GetWindowRect();
    SetWindowPos(0,0,0,rec.Width(),rec.Height()+1,SWP_NOZORDER|SWP_NOCOPYBITS);
  }
  // Return the new setting
  return !result;
}
 
 
//
//
//
bool
TCoolBar::ToggleBorder()
{
  // Check the existing style
  bool result = GetStyle() & (uint32)WS_BORDER;
  if (result)
    ModifyStyle(WS_BORDER, 0, 0);
  else
    ModifyStyle(0, WS_BORDER, 0);
 
  // Set the window position to force a full redraw. The "+ 1" seems to do the trick
  if(GetHandle()){
    TRect rec = GetWindowRect();
    SetWindowPos(0,0,0,rec.Width(),rec.Height()+1,SWP_NOZORDER|SWP_NOCOPYBITS);
    // Tell the parent window to layout again - I don't know why it does not
    // do this itself re the border
    if(Parent){
      TNotify notify(GetHandle(), GetId(), RBN_HEIGHTCHANGE);
      Parent->HandleMessage(WM_NOTIFY, GetId(), reinterpret_cast<TParam2>(&notify));
    }
  }
  // Return the new setting
  return !result;
}
 
void
TCoolBar::SetColorScheme(const TColorScheme& clr)
{
  if(!FColorScheme)
    FColorScheme = new TColorScheme;
  *FColorScheme  = clr;
  if(GetHandle())
    SendMessage(RB_SETCOLORSCHEME,0,TParam2(FColorScheme));
}
//
//
//
TColorScheme&
TCoolBar::GetColorScheme()
{
  if(!FColorScheme)
    FColorScheme = new TColorScheme;
  if(GetHandle())
    SendMessage(RB_GETCOLORSCHEME,0,TParam2(FColorScheme));
  return *FColorScheme;
}
//
//
//
void
TCoolBar::SetPalette(TPalette* palette)
{
  delete FPalette;
  FPalette = palette;
  if(GetHandle())
    SendMessage(RB_SETPALETTE,0,TParam2(FPalette->GetHandle()));
}
//
// TCoolBand
// ~~~~~~~~~
//
 
TCoolBand::TCoolBand()
{
  Init();
}
 
TCoolBand::TCoolBand(uint id, const tstring& str, TWindow* child, uint style)
{
  Init();
  Id = id;
  Text(str);
  Child = child;
  Style = style;
}
 
//
//
//
TCoolBand::~TCoolBand()
{
  //delete Bitmap;
}
 
//
//
//
void
TCoolBand::Init()
{
  Child  = 0;
  //Bitmap = 0;
  FgColor  = TColor::None;
  BgColor  = TColor::None;
  Style    = 0;
  Width    = 0;
  MinSize  = TSize(-1,-1);
  Id       = -1;
  ImageIndex = -1;
  Mask     = 0;
}
//
//
//
void
TCoolBand::SetupData(TCoolBar* parent, REBARBANDINFO& info)
{
  memset(&info, 0, sizeof(REBARBANDINFO));
  SizeOfReBarBandInfo(info);
 
  if(Mask)
    info.fMask = Mask;
 
  if(Child){
    //Child()->SetBkgndColor(TColor::Transparent);
    //Child()->ModifyExStyle(0,WS_EX_TRANSPARENT);
    if(!Child()->GetHandle())
      Child()->Create();
    info.fMask |= RBBIM_CHILD;
     info.hwndChild = Child()->GetHandle();
  }
  if(Bitmap){
    info.fMask |= RBBIM_BACKGROUND;
    info.hbmBack = Bitmap->GetHandle();
   }
  if(TColor::None != FgColor){
    info.fMask |= RBBIM_COLORS;
    info.clrFore = FgColor();
  }
  if(TColor::None != BgColor){
    info.fMask |= RBBIM_COLORS;
    info.clrBack = BgColor();
  }
  if(Style){
     info.fMask |= RBBIM_STYLE;
    info.fStyle = Style;
  }
  if(Width != uint(-1)){
    info.fMask |= RBBIM_SIZE;
    info.cx = Width;
  }
  if(MinSize() != TSize(-1,-1)){
    info.fMask |= RBBIM_CHILDSIZE;
    info.cxMinChild = MinSize().cx;
    info.cyMinChild = MinSize().cy;
  }
  if(Id != -1){
    info.fMask |= RBBIM_ID;
    info.wID = Id;
  }
  if(ImageIndex != -1){
    info.fMask |= RBBIM_IMAGE;
    info.iImage = ImageIndex;
  }
  if(!Text().empty()){
    info.fMask  |= RBBIM_TEXT;
    info.lpText = (tchar*)Text().c_str();
    info.cch    = static_cast<UINT>(Text().length());
  }
  Parent = parent;
}
} // OWL namespace
/* ========================================================================== */

V670 The uninitialized class member 'FPalette' is used to initialize the 'Palette' member. Remember that members are initialized in the order of their declarations inside a class.

V670 The uninitialized class member 'FBuddy' is used to initialize the 'Buddy' member. Remember that members are initialized in the order of their declarations inside a class.

V670 The uninitialized class member 'FTooltip' is used to initialize the 'Tooltip' member. Remember that members are initialized in the order of their declarations inside a class.

V670 The uninitialized class member 'FPalette' is used to initialize the 'Palette' member. Remember that members are initialized in the order of their declarations inside a class.

V670 The uninitialized class member 'FBuddy' is used to initialize the 'Buddy' member. Remember that members are initialized in the order of their declarations inside a class.

V670 The uninitialized class member 'FTooltip' is used to initialize the 'Tooltip' member. Remember that members are initialized in the order of their declarations inside a class.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: ShouldDelete.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: ShouldDelete.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: Parent.

V818 It is more efficient to use an initialization list 'Id(id), Child(child), Style(style)' rather than an assignment operator.

V832 It's better to use '= default;' syntax instead of empty destructor body.