//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
//
//   Implementation of Window Menu encapsulation class
//----------------------------------------------------------------------------
#include <owl/owlpch.h>
#include <owl/menu.h>

DIAG_DECLARE_GROUP(OwlWin);        // General window diagnostic group

//
// Construct a menu using CreateMenu()
//
TMenu::TMenu(TAutoDelete autoDelete)
:
  Handle(::CreateMenu()),
  ShouldDelete(autoDelete)
{
  WARNX(OwlWin, !Handle, 0, "Cannot Create Menu");
  CheckValid();
}

//
// Construct a menu as a deep copy of an exisiting menu
//
TMenu::TMenu(const TMenu& original, TAutoDelete autoDelete)
:
  Handle(::CreateMenu()),
  ShouldDelete(autoDelete)
{
  WARNX(OwlWin, !Handle, 0, "Cannot Create Menu for Copy");
  CheckValid();
  DeepCopy(*this, original);
}

//
// Construct a menu as an alias for an existing handle
//
TMenu::TMenu(HMENU handle, TAutoDelete autoDelete)
:
  Handle(handle),
  ShouldDelete(autoDelete)
{
}

//
// Construct a menu alias for a given window's menu
//
TMenu::TMenu(HWND hWnd, TAutoDelete autoDelete)
:
  Handle(::GetMenu(hWnd)),
  ShouldDelete(autoDelete)
{
  PRECONDITION(hWnd);
  WARNX(OwlWin, !Handle, 0, "Cannot Get Menu from " << hex << (uint)hWnd);
  CheckValid();
}

//
// Construct a menu from a menu template struct
//
TMenu::TMenu(const void far* menuTemplate)
{
  PRECONDITION(menuTemplate);
  Handle = ::LoadMenuIndirect(menuTemplate);
  WARNX(OwlWin, !Handle, 0, "Cannot Load Menu Indirect " << hex <<
        uint32(menuTemplate));
  CheckValid();
  ShouldDelete = true;
}

//
// Construct a menu by loading it from resource
//
TMenu::TMenu(HINSTANCE resInstance, TResId resId)
{
  PRECONDITION(resInstance && resId);
  Handle = ::LoadMenu(resInstance, resId);
  WARNX(OwlWin, !Handle, 0, "Cannot Load Menu " << hex << (uint)resInstance <<
                            " " << resId);
  CheckValid();
  ShouldDelete = true;
}

//
// Copy an existing menu onto this menu, using DeepCopy
//
TMenu&
TMenu::operator =(const TMenu& original)
{
  // Delete all items and submenus
  // Look at possible alternatives for !ShouldDelete menus? Maybe use Remove
  // then?
  //
  while (GetMenuItemCount())
    DeleteMenu(0, MF_BYPOSITION);
  DeepCopy(*this, original);
  return *this;
}

//
// Destruct the menu by destroying the handle if appropriate.
//
TMenu::~TMenu()
{
  if (ShouldDelete && Handle)
    ::DestroyMenu(Handle);
}

//
// Get the menu ID. If it's a regular menu item just returns its id.
// Otherwise if it's a popup menu use id of the first item in the popup menu.
//
uint
TMenu::GetMenuItemID(int pos) const
{
  uint  id = ::GetMenuItemID(Handle, pos);
  if (id != uint(-1))
    return id;

  // Maybe a submenu
  //
  TMenu subMenu(GetSubMenu(pos));
  if (subMenu.Handle == 0)
    return 0;

  return subMenu.GetMenuItemID(0) - 1;
}

#pragma warn -par  // resId param is never used in small model
void
TMenu::CheckValid(uint resId)
{
  if (!Handle)
    THROW( TXMenu(resId) );
}
#pragma warn .par

void
TMenu::MeasureItem(MEASUREITEMSTRUCT far&)
{
}

void
TMenu::DrawItem(DRAWITEMSTRUCT far&)
{
}

//
// Deep copy 'count' popup-menus or items from 'src' _appending_ to 'dst'
// menu starting at 'offset' position in this menu
//
void
TMenu::DeepCopy(TMenu& dst, const TMenu& src, int srcOff, int count)
{
  if (count < 0)
    count = src.GetMenuItemCount() - srcOff;

  for (int i = 0; i < count; i++) {
    uint  state = src.GetMenuState(srcOff+i, MF_BYPOSITION);
    if (state == uint(-1))
      return;

    char   str[256];
    src.GetMenuString(srcOff+i, str, sizeof(str), MF_BYPOSITION);

    // Currently does NOT support MF_BITMAP or MF_OWNERDRAW
    //
    if (state & MF_POPUP) {
      state &= (MF_STRING | MF_POPUP);  // strip off breaks, separators, etc
      TMenu subMenu(src.GetSubMenu(srcOff+i));
      TMenu newSubMenu(NoAutoDelete);
      DeepCopy(newSubMenu, subMenu);
      dst.AppendMenu(state, newSubMenu, str);
    }
    else {
      dst.AppendMenu(state, src.GetMenuItemID(srcOff+i), str);
    }
  }
}

//
// Deep copy 'count' popup-menus or items from 'src' to 'dst' menu
// starting at 'srcOff' position in src and inserting at 'dstOff' in the
// destination
//
void
TMenu::DeepCopy(TMenu& dst, int dstOff, const TMenu& src, int srcOff, int count)
{
  if (count < 0)
    count = src.GetMenuItemCount() - srcOff;

  for (int i = 0; i < count; i++) {
    uint  state = src.GetMenuState(srcOff+i, MF_BYPOSITION);
    if (state == uint(-1))
      return;

    char   str[256];
    src.GetMenuString(srcOff+i, str, sizeof(str), MF_BYPOSITION);

    // Currently does NOT support MF_BITMAP or MF_OWNERDRAW
    //
    if (state & MF_POPUP) {
      state &= (MF_STRING | MF_POPUP);  // strip off breaks, separators, etc
      TMenu subMenu(src.GetSubMenu(srcOff+i));
      TMenu newSubMenu(NoAutoDelete);
      DeepCopy(newSubMenu, subMenu);
      if (dstOff >= 0)
        dst.InsertMenu(dstOff, state|MF_BYPOSITION, newSubMenu, str);
      else
        dst.AppendMenu(state, newSubMenu, str);
    }
    else {
      if (dstOff >= 0)
        dst.InsertMenu(dstOff, state|MF_BYPOSITION, src.GetMenuItemID(srcOff+i), str);
      else
        dst.AppendMenu(state, src.GetMenuItemID(srcOff+i), str);
    }
  }
}

TSystemMenu::TSystemMenu(HWND wnd, bool revert)
:
  TMenu(::GetSystemMenu(wnd, revert), NoAutoDelete)
{
}

TPopupMenu::TPopupMenu(TAutoDelete autoDelete)
:
  TMenu(::CreatePopupMenu(), autoDelete)
{
}

TPopupMenu::TPopupMenu(HMENU handle, TAutoDelete autoDelete)
:
  TMenu(handle, autoDelete)
{
}

TMenu::TXMenu::TXMenu(unsigned resId) : TXOwl(resId)
{
}

TXOwl*
TMenu::TXMenu::Clone()
{
  return new TXMenu(*this);
}

void
TMenu::TXMenu::Throw()
{
  THROW( *this );
}
