//
/// \file
/// Implementation of class TSysLink
//
// Part of OWLNext - the next generation Object Windows Library
// Copyright (c) 2014 Vidar Hasfjord
//
// For more information, including license details, see
// http://owlnext.sourceforge.net
//
 
#include <owl/pch.h>
#include <owl/syslink.h>
#include <shellapi.h>
 
namespace owl
{
 
//
/// Constructs a SysLink from arguments.
/// The provided text can include HTML anchor tags, and if so, these will be rendered as links.
//
TSysLink::TSysLink(
  TWindow* parent,
  int id,
  const tstring& markupText,
  const TRect& r,
  TModule* module
  )
  : TControl(parent, id, markupText, r.Left(), r.Right(), r.Width(), r.Height(), module)
{
  InitializeCommonControls(ICC_LINK_CLASS);
}
 
//
/// Constructs a SysLink from resource.
//
TSysLink::TSysLink(TWindow* parent, int resourceId, TModule* module)
  : TControl(parent, resourceId, module)
{
  InitializeCommonControls(ICC_LINK_CLASS);
}
 
//
/// Constructs an alias.
//
TSysLink::TSysLink(THandle hWnd, TModule* module)
  : TControl(hWnd, module)
{
  InitializeCommonControls(ICC_LINK_CLASS);
}
 
//
/// Retrieves the preferred height of the control for the control's current width.
/// Returns the preferred height of the control, in pixels.
//
auto TSysLink::GetIdealHeight() -> int const
{
  const auto r = SendMessage(LM_GETIDEALHEIGHT);
  return static_cast<int>(r); // preferred height
}
 
//
/// Retrieves the ideal size of the control for a given maximum width.
/// Returns the preferred height of the control, in pixels.
//
auto TSysLink::GetIdealSize(int maxWidth, TSize& out) -> int const
{
  const auto p1 = static_cast<TParam1>(maxWidth);
  const auto p2 = reinterpret_cast<TParam2>(&out);
  const auto r = SendMessage(LM_GETIDEALSIZE, p1, p2);
  return static_cast<int>(r); // preferred height
}
 
//
/// Functional-style overload.
//
auto TSysLink::GetIdealSize(int maxWidth) -> TSize const
{
  auto s = TSize{};
  const auto h = GetIdealSize(maxWidth, s);
  CHECK(h == s.Y());
  return s;
}
 
//
/// Retrieves the states and attributes of the specified anchor item.
/// Note that you need to set the LIF_ITEMINDEX flag in LITEM::mask and provide the index of the
/// requested item in LITEM::iLink before calling the function. You also need to set LITEM::mask
/// and LITEM::stateMask to include the states and attributes you want to query. See MSDN:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760710.aspx
/// Returns `true` if successful.
//
auto TSysLink::GetItem(LITEM& inOut) -> bool const
{
  PRECONDITION(inOut.mask & LIF_ITEMINDEX);
  const auto p2 = reinterpret_cast<TParam2>(&inOut);
  const auto r = SendMessage(LM_GETITEM, 0, p2);
  return r != FALSE;
}
 
//
/// Functional-style overload.
/// Returns an LITEM with all available state and attribute information filled in.
//
auto TSysLink::GetItem(int index) -> LITEM const
{
  auto i = LITEM
  {
    0xFFFFFFFF, // mask
    index,
    0, // state
    0xFFFFFFFF // stateMask
  };
  const auto r = GetItem(i);
  if (!r) throw TXOwl(_T("TSysLink::GetItem failed"));
  return i;
}
 
//
/// Returns the ID attribute of the specified anchor item.
//
auto TSysLink::GetId(int index) -> tstring const
{
  _USES_CONVERSION_A;
  return _W2A_A(GetItem(index).szID);
}
 
//
/// Returns the HREF attribute of the specified anchor item.
//
auto TSysLink::GetUrl(int index) -> tstring const
{
  _USES_CONVERSION_A;
  return _W2A_A(GetItem(index).szUrl);
}
 
//
/// Sets the states and attributes of the specified anchor item.
/// Note that you need to set the LIF_ITEMINDEX flag in LITEM::mask and provide the index of the
/// requested item in LITEM::iLink before calling the function. You also need to set LITEM::mask
/// and LITEM::stateMask to include the states and attributes you want to modify. See MSDN:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760710.aspx
/// Returns `true` if successful.
//
auto TSysLink::SetItem(const LITEM& i) -> bool
{
  PRECONDITION(i.mask & LIF_ITEMINDEX);
  const auto p2 = reinterpret_cast<TParam2>(&i);
  const auto r = SendMessage(LM_SETITEM, 0, p2);
  return r != FALSE;
}
 
//
/// Performs a hit test on this control and returns information about the link hit, if any.
/// Note that you need to set LHITTESTINFO::pt with the location of the hit-test before calling
/// the function, and the location must be set in client coordinates (not screen coordinates).
/// See MSDN:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760708.aspx
/// Returns `true` if a link was hit.
//
auto TSysLink::HitTest(LHITTESTINFO& inOut) -> bool const
{
  const auto p2 = reinterpret_cast<TParam2>(&inOut);
  const auto r = SendMessage(LM_HITTEST, 0, p2);
  return r != FALSE;
}
 
//
/// Performs a hit-test on the given location.
/// If an anchor item is hit, information about the item is returned in a unique_ptr. Otherwise a
/// null-pointer is returned.
//
// TODO: When std::optional is available, use it instead of unique_ptr here.
//
auto TSysLink::HitTest(const TPoint& p) -> std::unique_ptr<LITEM> const
{
  auto i = LHITTESTINFO{p};
  const auto r = HitTest(i);
  return r ? std::make_unique<LITEM>(i.item) : nullptr;
}
 
//
/// Returns the class name for a SysLink control.
//
auto TSysLink::GetWindowClassName() -> TWindowClassName
{
  PRECONDITION(WC_LINK == std::wstring{L"SysLink"});
  return TWindowClassName{_T("SysLink")};
}
 
//-------------------------------------------------------------------------------------------------
 
DEFINE_RESPONSE_TABLE1(TAutoSysLink, TSysLink)
  EV_NM_CLICK(UINT_MAX, EvFollowLink),
  EV_NM_RETURN(UINT_MAX, EvFollowLink),
END_RESPONSE_TABLE;
 
//
/// Constructs a SysLink from arguments.
/// The provided text can include HTML anchor tags, and if so, these will be rendered as links.
//
TAutoSysLink::TAutoSysLink(
  TWindow* parent,
  int id,
  const tstring& markupText,
  const TRect& r,
  TModule* module
  )
  : TSysLink(parent, id, markupText, r, module)
{}
 
//
/// Constructs a SysLink from resource.
//
TAutoSysLink::TAutoSysLink(TWindow* parent, int resourceId, TModule* module)
  : TSysLink(parent, resourceId, module)
{}
 
//
/// Constructs an alias.
//
TAutoSysLink::TAutoSysLink(THandle hWnd, TModule* module)
  : TSysLink(hWnd, module)
{}
 
void TAutoSysLink::EvFollowLink()
{
  auto a = reinterpret_cast<PNMLINK>(GetApplication()->GetCurrentEvent().Param2);
  CHECK(a);
  ShellExecuteW(nullptr, L"open", a->item.szUrl, nullptr, nullptr, SW_SHOW);
}
 
} // OWL namespace
 

V1004 The 'a' pointer was used unsafely after it was verified against nullptr. Check lines: 230, 231.