//
/// \file
/// Definition of the TTooltip class and helper classes
//
// Part of OWLNext - the next generation Object Windows Library
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
// Copyright (c) 2020 by Vidar Hasfjord
//
// For more information, including license details, see
// http://owlnext.sourceforge.net
//
 
#if !defined(OWL_TOOLTIP_H)
#define OWL_TOOLTIP_H
 
#include <owl/private/defs.h>
#if defined(BI_HAS_PRAGMA_ONCE)
# pragma once
#endif
 
#include <owl/defs.h>
#include <owl/commctrl.h>
#include <optional>
#include <variant>
 
namespace owl {
 
/// \cond NoSuppressDoxygenWarning
#include <owl/preclass.h>
/// \endcond
 
/// \addtogroup commctrl_group
/// \{
 
//#if defined(OWL5_COMPAT)
 
//
/// Encapsulates the Windows API TTTOOLINFO (a.k.a. TOOLINFO) parameter package.
///
/// \deprecated This class uses inheritance in a dangerous way prone to slicing issues, and it is
/// not implemented in accordance with modern C++ coding standards. Its use may lead to dangling
/// pointers, copy and assignment issues, and the like. If you need to use low-level API functions
/// that has a TTTOOLINFO parameter, then use that instead, and carefully set it up according to
/// the Windows API documentation. However, prefer to use higher-level functions in TTooltip that
/// do not require the use of TTTOOLINFO or this ill-conceived encapsulation.
 
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
/// TTTOOLINFO</a> in the Windows API.
//
class _OWLCLASS TToolInfo
  : public TTTOOLINFO
{
public:
  TToolInfo(bool allocCache = false);
  TToolInfo(HWND window, const TRect&, uint toolId, LPCTSTR txt = LPSTR_TEXTCALLBACK);
  TToolInfo(HWND window, const TRect&, uint toolId, const tstring& txt);
  TToolInfo(HWND window, const TRect&, uint toolId, int resId, HINSTANCE strResModule);
  TToolInfo(HWND parent, HWND toolHwnd, LPCTSTR txt = LPSTR_TEXTCALLBACK);
  TToolInfo(HWND parent, HWND toolHwnd, const tstring& txt);
  TToolInfo(HWND parent, HWND toolHwnd, int resId, HINSTANCE strResModule);
 
  TToolInfo(const TToolInfo&);
  auto operator =(const TToolInfo&) -> TToolInfo&;
 
  auto operator ==(const TToolInfo& ti) const -> bool;
 
  void SetToolInfo(HWND window, uint toolId, const TRect& rc);
  void SetToolInfo(HWND window, uint toolId);
  void SetToolInfo(HWND toolHwnd, HWND parent);
 
  void SetText(int resId, HINSTANCE strResModule);
  void SetText(LPCTSTR, bool copy = true);
  void SetText(const tstring& text) { SetText(text.c_str(), true); }
  void SetRect(const TRect&);
 
  void EnableSubclassing(bool enable = true);
  auto GetToolWindow() const -> HWND;
  void GetToolRect(TRect&) const;
  auto GetToolRect() const -> TRect;
  auto IsPointInTool(HWND, const TPoint&) const -> bool;
  auto GetCacheText() const -> LPCTSTR;
  void FlushCacheText();
 
private:
  enum { DefTipTextCacheSize = 128 }; // Default cache size of tip text
  tstring CacheText;
};
 
//
/// Alias for the TTHITTESTINFO structure.
///
/// This structure is used to determine whether a point is within the bounding rectangle of a
/// particular tool.
///
/// \deprecated This alias is deprecated; simply use TTHITTESTINFO instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tthittestinfoa">
/// TTHITTESTINFO</a> in the Windows API.
//
using TTooltipHitTestInfo = TTHITTESTINFO;
 
//#endif // OWL5_COMPAT
 
//
/// Forwarded along the command-chain to retrieve the tip text of a tool.
///
/// The class is not a true command enabler, i.e. invoking SetCheck or Enable does not modify the
/// state of the command associated with the tool. However, by using the command enabler mechanism
/// for retrieving the text of tools, command handlers are given the first crack at customizing the
/// tip text for tool buttons associated with commands.
//
class _OWLCLASS TTooltipEnabler : public TCommandEnabler
{
public:
  TTooltipEnabler(TTooltipText& tt, HWND hReceiver);
 
  // TCommandEnabler overrides
  //
  void SetText(LPCTSTR text) override;
  void SetCheck(int check) override;
 
  void SetText(const tstring& text) { SetText(text.c_str()); }
 
protected_data:
 
  /// Reference to the parameter package accompanying the TTN_GETDISPINFO notification message,
  /// requesting the text of a particular tool.
  ///
  /// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttn-getdispinfo">
  /// TTN_GETDISPINFO</a> in the Windows API.
  //
  TTooltipText& TipText;
};
 
//
/// Encapsulates the Tooltip control in the Windows API.
///
/// A tooltip is a small window that appears automatically (pops up) when the user hovers the mouse
/// pointer over a tool (a control element or designated rectangular area). It usually contains a
/// brief descriptive text for the associated tool. It may also include an icon and a title.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/tooltip-control-reference">
/// Tooltip</a> in the Windows API.
//
class _OWLCLASS TTooltip
  : public TControl
{
public:
 
  //
  /// Represents a tool identified by handle.
  /// Identifies a tool associated with a control in the tool container window.
  ///
  /// \sa GetTool to retrieve the tool, given the identifier.
  //
  struct TCtrlToolId
  {
    HWND ToolContainer;
    HWND Handle;
  };
 
  //
  /// Represents a tool identified by numerical ID.
  /// Identifies a tool associated with a rectangular area in the tool container window.
  ///
  /// \sa GetTool to retrieve the tool, given the identifier.
  //
  struct TRectToolId
  {
    HWND ToolContainer;
    uint Id;
  };
 
  //
  /// Represents a tool identified by index.
  ///
  /// The tool identified by index may be associated with a rectangular area or a control within
  /// the tool container window.
  ///
  /// \sa GetTool to retrieve the tool, given the identifier.
  /// \sa GetToolCount for the upper limit on the index.
  //
  using TToolIndex = int;
 
  //
  /// Represents a tool identified by numerical ID, handle or index.
  //
  using TToolId = std::variant<TCtrlToolId, TRectToolId, TToolIndex>;
 
  //
  /// Contains information about a particular tool registered with a TTooltip control.
  /// Return type for TTooltip::GetTool.
  ///
  /// A tool is either a child control or an application-defined rectangular area within a tool
  /// container window (note that this may or may not be the parent window of the TTooltip). This
  /// class holds the information registered for a tool, such as the tip text to be displayed by
  /// the tooltip when the mouse is hovering over the tool.
  //
  class TTool
    : private TTTOOLINFO
  {
  public:
    using TId = TToolId; ///< Will only hold TTooltip::TCtrlToolId or TTooltip::TRectToolId; not TTooltip::TToolIndex, though.
 
    TTool() = default;
    explicit TTool(const TTTOOLINFO& i);
    auto GetId() const -> TId;
    auto GetToolContainer() const -> HWND;
    auto GetBoundingBox() const -> TRect;
    auto GetStaticTipText() const -> tstring;
    auto HasDynamicTipText() const -> bool;
    auto GetApplicationData() const -> LPARAM;
    auto GetFlags() const -> uint;
 
  private:
    tstring TipText;
  };
 
  //
  /// Iterator type for TToolRange.
  ///
  /// \sa TToolRange and GetTools
  //
  class TToolIterator
  {
  public:
    using iterator_category = std::bidirectional_iterator_tag;
    using value_type = TTool;
    using pointer = std::optional<TTool>;
    using reference = TTool&;
 
    TToolIterator(const TTooltip& tooltip, int current = 0)
      : Tooltip{&tooltip},
      Current{current}
    {}
 
    auto operator *() const -> value_type { return *Tooltip->GetTool(Current); }
    auto operator ->() const -> pointer { return Tooltip->GetTool(Current); }
    auto operator ++() -> TToolIterator& { ++Current; return *this; }
    auto operator --() -> TToolIterator& { --Current; return *this; }
 
    friend auto operator ==(const TToolIterator& a, const TToolIterator& b) { return a.Tooltip == b.Tooltip && a.Current == b.Current; }
    friend auto operator !=(const TToolIterator& a, const TToolIterator& b) { return !(a == b); }
 
  private:
    const TTooltip* Tooltip;
    int Current;
  };
 
  //
  /// Encapsulates the tools registered for a tooltip.
  /// Return type for TTooltip::GetTools.
  ///
  /// \sa GetTools
  //
  class TToolRange
  {
  public:
    using value_type = TTool;
    using size_type = int;
    using pointer = TTool*;
    using const_pointer = const TTool*;
    using reference = TTool&;
    using const_reference = const TTool&;
 
    using iterator = TToolIterator;
    using const_iterator = TToolIterator;
 
    TToolRange() = default;
    TToolRange(const TTooltip& tooltip) : Tooltip{&tooltip} {}
    auto begin() { return iterator{*Tooltip}; }
    auto end() { return iterator{*Tooltip, Tooltip->GetToolCount()}; }
    auto begin() const { return const_iterator{*Tooltip}; }
    auto end() const { return const_iterator{*Tooltip, Tooltip->GetToolCount()}; }
    auto cbegin() const { return begin(); }
    auto cend() const { return end(); }
    auto size() const { return Tooltip->GetToolCount(); }
 
  private:
    const TTooltip* Tooltip;
  };
 
  //
  /// Parameter type for TTooltip::GetDelayTime.
  //
  enum TDelayTime
  {
    dtAutoPop = TTDT_AUTOPOP, ///< The amount of time a tip remains visible with the mouse pointer at rest.
    dtInitial = TTDT_INITIAL, ///< The amount of time the mouse pointer must be at rest before a tip appears.
    dtReshow = TTDT_RESHOW ///< The delay for tips to reappear as the mouse pointer moves between tools.
  };
 
  //
  /// Return value type for TTooltip::GetMargin.
  //
  struct TMargin
  {
    int Left;
    int Top;
    int Right;
    int Bottom;
  };
 
  //
  /// Argument type for TTooltip::SetTitle.
  //
  enum TPredefinedIcon
  {
    piNone = TTI_NONE, ///< No icon.
    piInfo = TTI_INFO, ///< Info icon.
    piWarning = TTI_WARNING, ///< Warning icon.
    piError = TTI_ERROR, ///< Error icon.
    piInfoLarge = TTI_INFO_LARGE, ///< Large info icon.
    piWarningLarge = TTI_WARNING_LARGE, ///< Large warning icon.
    piErrorLarge = TTI_ERROR_LARGE ///< Large error icon.
  };
 
  //
  /// Parameter type for TTooltip::SetTitle.
  //
  using TTitleIcon = std::variant<HICON, TPredefinedIcon>;
 
  //
  /// Return value type for TTooltip::GetTitle.
  ///
  /// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settitle">
  /// TTM_SETTITLE</a> for a definition of the possible predefined icon IDs.
  //
  struct TTitle
  {
    tstring Text;
    TTitleIcon Icon;
  };
 
  //
  /// Parameter type for TTooltip::AdjustRect.
  //
  enum TAdjustRectOp
  {
    aroTextToWindow, ///< Text rectangle to window rectangle tranform. 
    aroWindowToText ///< Window rectangle to text rectangle transform.
  };
 
  /// \name Construction and destruction
  /// \{
 
  TTooltip(TWindow* parent, bool alwaysTip = true, TModule* = nullptr);
  TTooltip(TWindow* parent, TWindowFlag, TModule* = nullptr);
  TTooltip(HWND hWnd, TModule* = nullptr);
 
  TTooltip(const TTooltip&) = delete;
  TTooltip& operator =(const TTooltip&) = delete;
 
  /// \}
  /// \name Tool registration and removal
  /// \{
 
  auto AddTool(TCtrlToolId, const tstring& tip = {}, LPARAM appData = 0, uint flags = TTF_SUBCLASS)->TTooltip&;
  auto AddTool(TRectToolId, const TRect& tipArea, const tstring& tip = {}, LPARAM appData = 0, uint flags = TTF_SUBCLASS) -> TTooltip&;
  auto AddTool(HWND ctrl, const tstring& tip = {}, LPARAM appData = 0, uint flags = TTF_SUBCLASS) -> TTooltip&;
  auto AddTool(HWND toolContainer, int ctrlId, const tstring& tip = {}, LPARAM appData = 0, uint flags = TTF_SUBCLASS) -> TTooltip&;
  auto DelTool(TToolId) -> TTooltip&;
 
  /// \}
  /// \name Tool enumeration
  /// \{
 
  auto GetTools() const -> TToolRange;
  auto GetToolCount() const -> int;
 
  /// \}
  /// \name Tool attribute access and modification
  /// \{
 
  auto GetCurrentTool() const -> std::optional<TTool>;
  auto GetText(TToolId) const -> tstring;
  auto GetTool(TToolId) const -> std::optional<TTool>;
  auto HitTest(HWND toolContainer, const TPoint&) const -> std::optional<TTool>;
  auto ModifyFlags(TToolId, uint onFlags, uint offFlags, uint swapFlags = 0) -> TTooltip&;
  auto NewToolRect(TToolId, const TRect&) -> TTooltip&;
  auto SetApplicationData(TToolId, LPARAM data) -> TTooltip&;
  auto SetFlags(TToolId, uint flags) -> TTooltip&;
  auto UpdateTipText(TToolId, const tstring& tip) -> TTooltip&;
 
  /// \}
  /// \name Tooltip attribute access and modification
  /// \{
 
  auto GetDelayTime(TDelayTime = dtInitial) const -> int;
  auto GetMargin() const -> TMargin;
  auto GetMaxTipWidth() const -> int;
  auto GetTipBkColor() const -> TColor;
  auto GetTipTextColor() const -> TColor;
  auto GetTitle() const -> TTitle;
  auto SetDelayTime(TDelayTime, int delay) -> TTooltip&;
  auto SetMargin(const TMargin&) -> TTooltip&;
  auto SetMaxTipWidth(int width = -1) -> TTooltip&;
  auto SetTipBkColor(const TColor&) -> TTooltip&;
  auto SetTipTextColor(const TColor&) -> TTooltip&;
  auto SetTitle(const tstring& title, TTitleIcon = piNone) -> TTooltip&;
 
  /// \}
  /// \name Activation, display control and tracking
  /// \{
 
  auto Activate(bool activate = true) -> TTooltip&;
  auto AdjustRect(const TRect&, TAdjustRectOp) -> TRect;
  auto Pop() -> TTooltip&;
  auto TrackActivate(TToolId, bool activate = true) -> TTooltip&;
  auto TrackPosition(const TPoint& pos) -> TTooltip&;
  auto Update() -> TTooltip&;
 
  /// \}
  /// \name Message handling
  /// \{
 
  auto RelayEvent(MSG&) -> TTooltip&;
 
  /// \}
  /// \name Low-level API
  /// See <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
  /// TTTOOLINFO</a> in the Windows API.
  /// \{
 
  auto AddTool(const TTTOOLINFO&) -> TTooltip&;
  auto DelTool(const TTTOOLINFO&) -> TTooltip&;
  void EnumTools(int index, TTTOOLINFO&) const;
  auto GetCurrentTool(TTTOOLINFO&) const -> bool;
  void GetText(int bufSize, TTTOOLINFO&) const;
  void GetToolInfo(TTTOOLINFO&) const;
  auto HitTest(TTHITTESTINFO&) const -> bool;
  auto InitToolInfo(TToolId tool, bool shouldResolveToolIndex = true) const -> TTTOOLINFO;
  auto NewToolRect(const TTTOOLINFO&) -> TTooltip&;
  auto SetToolInfo(const TTTOOLINFO&) -> TTooltip&;
  auto UpdateTipText(const TTTOOLINFO&) -> TTooltip&;
 
  /// \}
  /// \name Deprecated functions
  /// \{
 
  //#if defined(OWL5_COMPAT)
 
  [[deprecated]] auto GetBkColor() const -> TColor;
  [[deprecated]] auto GetDelayTime(uint flag = TTDT_INITIAL) const -> int;
  [[deprecated]] void GetMargins(int& left, int& top, int& right, int& bottom) const;
  [[deprecated]] auto GetTextColor() const -> TColor;
  [[deprecated]] void HideTip();
  [[deprecated]] void SetBkColor(const TColor&);
  [[deprecated]] void SetDelayTime(uint flag, int delay);
  [[deprecated]] void SetMargins(int left, int top, int right, int bottom);
  [[deprecated]] void SetTextColor(const TColor&);
 
  [[deprecated]] auto AddTool(const TToolInfo& i) -> bool;
  [[deprecated]] void DeleteTool(const TToolInfo&);
  [[deprecated]] auto EnumTools(int index, TToolInfo&) const -> bool;
  [[deprecated]] auto GetCurrentTool(TToolInfo&) const -> bool;
  [[deprecated]] auto GetToolInfo(TToolInfo&) const -> bool;
  [[deprecated]] void GetToolText(TToolInfo& ti) const;
  [[deprecated]] void NewToolRect(const TToolInfo&);
  [[deprecated]] void SetToolInfo(const TToolInfo&);
  [[deprecated]] void UpdateTipText(const TToolInfo&);
 
  //#endif
 
  /// \}
 
protected:
 
  // TWindow overrides
  //
  auto GetWindowClassName() -> TWindowClassName override;
};
 
//
/// Represents a failure in a TTooltip operation.
//
class TXTooltip
  : public TXOwl
{
public:
  TXTooltip(const tstring& msg)
    : TXOwl(msg)
  {}
};
 
/// \}
 
/// \cond NoSuppressDoxygenWarning
#include <owl/posclass.h>
/// \endcond
 
//-------------------------------------------------------------------------------------------------
// Inline implementation for TTooltip::TTool
 
//
/// Constructs an instance based on the information in the given parameter package.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
/// TTTOOLINFO</a> in the Windows API.
//
inline TTooltip::TTool::TTool(const TTTOOLINFO& i)
  : TTTOOLINFO{i},
  TipText{}
{
  if (lpszText && lpszText != LPSTR_TEXTCALLBACK)
  {
    const auto resId = TResId{lpszText};
    TipText = resId.IsInt() ?
      TModule{_T(""), hinst, false}.LoadString(resId.GetInt()) :
      lpszText;
  }
}
 
//
/// Retrieves the data identifying this tool.
///
/// The return vale will either hold a TTooltip::TCtrlToolId or a TTooltip::TRectToolId. Use
/// `std::variant::holds_alternative` to determine which.
//
inline auto TTooltip::TTool::GetId() const -> TId
{
  return (uFlags & TTF_IDISHWND) ?
    TId{TCtrlToolId{hwnd, reinterpret_cast<HWND>(uId)}} :
    TId{TRectToolId{hwnd, static_cast<uint>(uId)}};
}
 
//
/// Retrieves the handle of the tool container window.
//
inline auto TTooltip::TTool::GetToolContainer() const -> HWND
{
  return hwnd;
}
 
//
/// Retrieves the bounding box around this tool.
///
/// When the mouse hovers in this area, the tool tip text is shown.
/// The coordinates of the returned rectangle are relative to the tool container window.
///
/// \sa GetToolContainer can be used to retrieve the tool container window handle.
//
inline auto TTooltip::TTool::GetBoundingBox() const -> TRect
{
  return (uFlags & TTF_IDISHWND) ?
    TWindow{hwnd}.MapScreenToClient(TWindow{std::get<TCtrlToolId>(GetId()).Handle}.GetWindowRect()) :
    rect;
}
 
//
/// Retrieves the tip text for this tool, if set statically.
///
/// \returns Unless this tool determines its tip text dynamically, the statically set tip text
/// is returned. Otherwise, an empty string is returned.
///
/// \sa HasDynamicTipText can be used to determine whether the tip text is static or dynamic.
//
inline auto TTooltip::TTool::GetStaticTipText() const -> tstring
{
  return TipText;
}
 
//
/// Retrieves whether the tip text is determined by a callback.
///
/// \returns If this tool is configured to send TTN_GETDISPINFO notification messages to the
/// tooltip owner to retrieve tip text, then `true` is returned; otherwise, `false`.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttn-getdispinfo">
/// TTN_GETDISPINFO</a> in the Windows API.
//
inline auto TTooltip::TTool::HasDynamicTipText() const -> bool
{
  return lpszText == LPSTR_TEXTCALLBACK;
}
 
//
/// Retrieves the application-defined value associated with this tool.
///
/// \returns If the tool has associated application data, then the data is returned. Otherwise,
/// 0 is returned.
///
/// \note A return value of 0 does not mean that no application data has been attached. A value
/// of 0 is perfectly acceptable application data (e.g. an index). It is up to the client to
/// determine whether the application has attached data or not. 0 is simply the default value.
//
inline auto TTooltip::TTool::GetApplicationData() const -> LPARAM
{
  return lParam;
}
 
//
/// Retrieves the flags used to configure this tool.
///
/// \returns A bit container with the set of bit flags set for this tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
/// TTTOOLINFO</a> in the Windows API for the documentation of the tool flags.
//
inline auto TTooltip::TTool::GetFlags() const -> uint
{
  return uFlags;
}
 
//-------------------------------------------------------------------------------------------------
// Inline implementation for TTooltip
 
//
/// Registers a control tool with the tooltip control.
///
/// \param tool identifies the control that defines the tool.
///
/// \param tip is the static tip text to use, if any. If set to empty, the tooltip control will
/// send the TTN_GETDISPINFO notification message to the tool container window (specified in
/// parameter \p tool) to retrieve the tip text when required.
///
/// \param appData is an optional application-defined value to associate with the tool.
///
/// \param flags control the tip text display for the tool. The default is TTF_SUBCLASS, which
/// causes the tooltip control to handle window messages for tool container window, so that tip
/// text will automatically show. Without this flag, you must use the TTM_RELAYEVENT message to
/// forward message to the tooltip control. See the Windows API documentation for more information
/// about this and other available flags.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa AddTool(const TTTOOLINFO&)
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-addtool">
/// TTM_ADDTOOL</a> in the Windows API.
//
inline auto TTooltip::AddTool(TCtrlToolId tool, const tstring& tip, LPARAM appData, uint flags) -> TTooltip&
{
  AddTool(TTTOOLINFO
    {
      static_cast<UINT>(sizeof(TTTOOLINFO)), // cbSize
      flags | TTF_IDISHWND, // uFlags
      tool.ToolContainer, // hwnd
      reinterpret_cast<UINT_PTR>(tool.Handle), // uId
      RECT{}, // rect
      nullptr, // hinst
      tip.empty() ? LPSTR_TEXTCALLBACK : const_cast<LPTSTR>(tip.c_str()), // lpszText
      appData, // lParam
      nullptr // lpReserved
    });
  return *this;
}
 
//
/// Registers a rectangle tool with the tooltip control.
///
/// \param tool identifies the rectangular area that defines the tool.
/// \param tipArea defines the rectangular area for the tool.
///
/// \param tip is the static tip text to use, if any. If set to empty, the tooltip control will
/// send the TTN_GETDISPINFO notification message to the tool container window (specified in
/// parameter \p tool) to retrieve the tip text when required.
///
/// \param appData is an optional application-defined value to associate with the tool.
///
/// \param flags control the tip text display for the tool. The default is TTF_SUBCLASS, which
/// causes the tooltip control to handle window messages for the tool container window, so that tip
/// text will automatically show. Without this flag, you must use the TTM_RELAYEVENT message to
/// forward message to the tooltip control. See the Windows API documentation for more information
/// about this and other available flags.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa AddTool(const TTTOOLINFO&)
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-addtool">
/// TTM_ADDTOOL</a> in the Windows API.
//
inline auto TTooltip::AddTool(TRectToolId tool, const TRect& tipArea, const tstring& tip, LPARAM appData, uint flags) -> TTooltip&
{
  AddTool(TTTOOLINFO
    {
      static_cast<UINT>(sizeof(TTTOOLINFO)), // cbSize
      flags & ~TTF_IDISHWND, // uFlags
      tool.ToolContainer, // hwnd
      tool.Id, // uId
      tipArea, // rect
      nullptr, // hinst
      tip.empty() ? LPSTR_TEXTCALLBACK : const_cast<LPTSTR>(tip.c_str()), // lpszText
      appData, // lParam
      nullptr // lpReserved
    });
  return *this;
}
 
//
/// Registers a tool for the given control.
/// Overload of AddTool(TCtrlToolId, const tstring& tip, LPARAM appData, uint flags).
//
inline auto TTooltip::AddTool(HWND ctrl, const tstring& tip, LPARAM appData, uint flags) -> TTooltip&
{
  AddTool({::GetParent(ctrl), ctrl}, tip, appData, flags);
  return *this;
}
 
//
/// Registers a tool for the control identified by the given identifier in the given tool container.
/// Overload of AddTool(TCtrlToolId, const tstring& tip, LPARAM appData, uint flags).
//
inline auto TTooltip::AddTool(HWND toolContainer, int ctrlId, const tstring& tip, LPARAM appData, uint flags) -> TTooltip&
{
  AddTool(::GetDlgItem(toolContainer, ctrlId), tip, appData, flags);
  return *this;
}
 
//
/// Deregisters a tool from the tooltip control.
///
/// Example usage:
///
/// \code
/// tooltip.DelTool(TTooltip::TCtrlToolId{dlg, ctrl});
/// tooltip.DelTool(TTooltip::TRectToolId{gadgetBar, gadgetId});
/// tooltip.DelTool(toolIndex);
/// \endcode
///
/// \sa TToolId for more information on how to identify a tool.
/// \sa DelTool(const TTTOOLINFO&) is called to perform the removal.
//
inline auto TTooltip::DelTool(TToolId tool) -> TTooltip&
{
  DelTool(InitToolInfo(tool));
  return *this;
}
 
//
/// Retrieves a range encapsulating the tools registered for this tooltip.
///
/// The range can be iterated over in standard ways. For example:
///
/// \code
/// for (auto tool : tooltip.GetTools())
/// { /*...*/ }
/// \endcode
///
/// \sa GetToolCount, GetTool and EnumTools(int index, TTTOOLINFO&) const.
//
inline auto TTooltip::GetTools() const -> TToolRange
{
  return TToolRange{*this};
}
 
//
/// Returns the number of tools currently registered with the tooltip control.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa EnumTools can be used to retrieve tool information by index.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolcount">
/// TTM_GETTOOLCOUNT</a> in the Windows API.
//
inline auto TTooltip::GetToolCount() const -> int
{
  const auto n = static_cast<int>(SendMessage(TTM_GETTOOLCOUNT));
  if (n < 0) throw TXTooltip{_T("TTM_GETTOOLCOUNT failed")};
  return n;
}
 
//
/// Retrieves information about the current tool, if any.
/// \returns If a current tool does not exist, `std::nullopt` is returned.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getcurrenttool">
/// TTM_GETCURRENTTOOL</a> in the Windows API.
//
inline auto TTooltip::GetCurrentTool() const -> std::optional<TTool>
{
  auto ti = InitToolInfo(0, false);
  const auto ok = GetCurrentTool(ti);
  return ok ? std::make_optional(TTool{ti}) : std::nullopt;
}
 
//
/// Retrieves the tool at the given point, if any.
///
/// \returns On success, the hit tool is returned. Otherwise, `std::nullopt` is returned.
///
/// \note For it to be hit, a tool must have the TTF_TRACK flag set. Otherwise, `std::nullopt` will
/// be returned, even if the given point lies within the bounding box of the tool. For more
/// information, see HitTest(TTHITTESTINFO&) const.
///
/// \sa HitTest(TTHITTESTINFO&) const is called to perform the test.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-hittest">
/// TTM_HITTEST</a> in the Windows API.
//
inline auto TTooltip::HitTest(HWND toolContainer, const TPoint& p) const -> std::optional<TTool>
{
  auto h = TTHITTESTINFO{toolContainer, p};
  const auto isHit = HitTest(h);
  return isHit ? std::make_optional(TTool{h.ti}) : std::nullopt;
}
 
//
/// Modifies the specified flags of the given tool.
///
/// \note Flag TTF_IDISHWND can not be modified, and is ignored if specified.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa GetToolInfo(TTTOOLINFO&) is called to get the current tool attributes.
/// \sa SetToolInfo(const TTTOOLINFO&) is called to update the tool attributes.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolinfo">
/// TTM_GETTOOLINFO</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settoolinfo">
/// TTM_SETTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::ModifyFlags(TToolId tool, uint offFlags, uint onFlags, uint swapFlags) -> TTooltip&
{
  auto ti = InitToolInfo(tool);
  GetToolInfo(ti);
  ti.uFlags = (((ti.uFlags & ~(offFlags & ~TTF_IDISHWND)) | (onFlags & ~TTF_IDISHWND))) ^ (swapFlags & ~TTF_IDISHWND);
  SetToolInfo(ti);
  return *this;
}
 
//
/// Updates the bounding rectangle of a tool.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-newtoolrect">
/// TTM_NEWTOOLRECT</a> in the Windows API.
//
inline auto TTooltip::NewToolRect(TToolId tool, const TRect& r) -> TTooltip&
{
  auto ti = InitToolInfo(tool);
  ti.rect = r;
  NewToolRect(ti);
  return *this;
}
 
//
/// Modifies the application data associated with the given tool.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa GetToolInfo(TTTOOLINFO&) is called to get the current tool attributes.
/// \sa SetToolInfo(const TTTOOLINFO&) is called to update the tool attributes.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolinfo">
/// TTM_GETTOOLINFO</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settoolinfo">
/// TTM_SETTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::SetApplicationData(TToolId tool, LPARAM data) -> TTooltip&
{
  auto ti = InitToolInfo(tool);
  GetToolInfo(ti);
  ti.lParam = data;
  SetToolInfo(ti);
  return *this;
}
 
//
/// Sets all the flags of the given tool to the given values.
///
/// \note Flag TTF_IDISHWND can not be modified, and is ignored.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa GetToolInfo(TTTOOLINFO&) is called to get the current tool attributes.
/// \sa SetToolInfo(const TTTOOLINFO&) is called to update the tool attributes.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolinfo">
/// TTM_GETTOOLINFO</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settoolinfo">
/// TTM_SETTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::SetFlags(TToolId tool, uint flags) -> TTooltip&
{
  auto ti = InitToolInfo(tool);
  GetToolInfo(ti);
  ti.uFlags = (ti.uFlags & TTF_IDISHWND) | (flags & ~TTF_IDISHWND);
  SetToolInfo(ti);
  return *this;
}
 
//
/// Updates the tip text for the given tool.
///
/// \param tool is a variant that can be TCtrlToolId, TRectToolId or TToolIndex.
///
/// \param tip specifies the new tip text. If empty, the tooltip control sends the TTN_GETDISPINFO
/// notification message to the owner window (the tool container) to retrieve the tip text when
/// required.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-updatetiptext">
/// TTM_UPDATETIPTEXT</a> in the Windows API.
//
inline auto TTooltip::UpdateTipText(TToolId tool, const tstring& tip) -> TTooltip&
{
  auto ti = InitToolInfo(tool);
  ti.lpszText = tip.empty() ? LPSTR_TEXTCALLBACK : const_cast<LPTSTR>(tip.c_str());
  UpdateTipText(ti);
  return *this;
}
 
//
/// Retrieves the initial, pop-up or reshow duration currently set for a tooltip control.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getdelaytime">
/// TTM_GETDELAYTIME</a> in the Windows API.
//
inline int TTooltip::GetDelayTime(TDelayTime dt) const
{
  return static_cast<int>(SendMessage(TTM_GETDELAYTIME, dt));
}
 
//
/// Retrieves the top, left, bottom, and right margins set for the tooltip window.
///
/// A margin is the distance, in pixels, between the tooltip window border and the text contained
/// within the tooltip window.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getmargin">
/// TTM_GETMARGIN</a> in the Windows API.
//
inline auto TTooltip::GetMargin() const -> TMargin
{
  auto r = TRect{};
  SendMessage(TTM_GETMARGIN, 0, reinterpret_cast<TParam2>(&r));
  return {r.left, r.top, r.right, r.bottom};
}
 
//
/// Retrieves the maximum width for the tooltip window.
///
/// \returns The maximum width in pixel is returned, if previously set. Otherwise, -1 is returned.
///
/// \note If a tip text exceeds the maximum width, it is broken into multiple lines. If the text
/// cannot be segmented into multiple lines, it will be displayed on a single line. The length of
/// this line may exceed the maximum tooltip width.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getmaxtipwidth">
/// TTM_GETMAXTIPWIDTH</a> in the Windows API.
//
inline auto TTooltip::GetMaxTipWidth() const -> int
{
  return static_cast<int>(SendMessage(TTM_GETMAXTIPWIDTH));
}
 
//
/// Retrieves the background color for the tooltip window.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettipbkcolor">
/// TTM_GETTIPBKCOLOR</a> in the Windows API.
//
inline auto TTooltip::GetTipBkColor() const -> TColor
{
  return TColor{static_cast<COLORREF>(SendMessage(TTM_GETTIPBKCOLOR))};
}
 
//
///
/// Retrieves the text color for the tooltip window.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettiptextcolor">
/// TTM_GETTIPTEXTCOLOR</a> in the Windows API.
//
inline auto TTooltip::GetTipTextColor() const -> TColor
{
  return TColor{static_cast<COLORREF>(SendMessage(TTM_GETTIPTEXTCOLOR))};
}
 
//
/// Retrieves the currently set title attributes of the tooltip.
///
/// \exception TXOwl is thrown on failure.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettitle">
/// TTM_GETTITLE</a> in the Windows API.
//
inline auto TTooltip::GetTitle() const -> TTitle
{
  auto t = TTGETTITLE{sizeof(TTGETTITLE)};
  const auto r = SendMessage(TTM_GETTITLE, 0, reinterpret_cast<TParam2>(&t));
  if (!r) throw TXOwl{_T("TTooltip::SetTitle: TTM_GETTITLE failed")};
  return IS_INTRESOURCE(t.uTitleBitmap) ?
    TTitle{to_tstring(const_cast<LPCWSTR>(t.pszTitle)), TTitleIcon{std::in_place_type<TPredefinedIcon>, static_cast<TPredefinedIcon>(t.uTitleBitmap)}} :
    TTitle{to_tstring(const_cast<LPCWSTR>(t.pszTitle)), TTitleIcon{std::in_place_type<HICON>, reinterpret_cast<HICON>(static_cast<UINT_PTR>(t.uTitleBitmap))}};
}
 
//
/// Sets the initial, popup or reshow durations for the tooltip control.
///
/// \param dt is the particular TDelayTime to set.
/// \param delay is the duration in milliseconds.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-setdelaytime">
/// TTM_SETDELAYTIME</a> in the Windows API.
//
inline auto TTooltip::SetDelayTime(TDelayTime dt, int delay) -> TTooltip&
{
  SendMessage(TTM_SETDELAYTIME, dt, delay);
  return *this;
}
 
//
/// Sets the top, left, bottom, and right margins for the tooltip window.
///
/// A margin is the distance, in pixels, between the tooltip window border and the text contained
/// within the tooltip window.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-setmargin">
/// TTM_SETMARGIN</a> in the Windows API.
//
inline auto TTooltip::SetMargin(const TMargin& m) -> TTooltip&
{
  const auto rect = TRect{m.Left, m.Top, m.Right, m.Bottom};
  SendMessage(TTM_SETMARGIN, 0, reinterpret_cast<TParam2>(&rect));
  return *this;
}
 
//
/// Sets the maximum width for the tooltip window.
///
/// \param width is the desired window width in pixels, or -1 to allow any width (default).
///
/// \note If a tip text exceeds the maximum width, it is broken into multiple lines. If the text
/// cannot be segmented into multiple lines, it will be displayed on a single line. The length of
/// this line may exceed the maximum tooltip width.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-setmaxtipwidth">
/// TTM_SETMAXTIPWIDTH</a> in the Windows API.
//
inline auto TTooltip::SetMaxTipWidth(int width) -> TTooltip&
{
  SendMessage(TTM_SETMAXTIPWIDTH, 0, static_cast<TParam2>(width));
  return *this;
}
 
//
/// Sets the background color for the tooltip window.
///
/// \note When visual styles are enabled, this function has no effect.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settipbkcolor">
/// TTM_SETTIPBKCOLOR</a> in the Windows API.
//
inline auto TTooltip::SetTipBkColor(const TColor& c) -> TTooltip&
{
  SendMessage(TTM_SETTIPBKCOLOR, static_cast<TParam2>(c.GetValue()));
  return *this;
}
 
//
/// Sets the text color for the tooltip window.
///
/// \note When visual styles are enabled, this function has no effect.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settiptextcolor">
/// TTM_SETTIPTEXTCOLOR</a> in the Windows API.
//
inline auto TTooltip::SetTipTextColor(const TColor& c) -> TTooltip&
{
  SendMessage(TTM_SETTIPTEXTCOLOR, static_cast<TParam2>(c.GetValue()));
  return *this;
}
 
//
/// Assigns a caption, with a title text and optional icon, to the tips shown.
///
/// \param title is the desired title text.
///
/// \param icon is either a TTooltip::TPredefinedIcon or a HICON handle. If the latter, the icon is
/// copied, i.e. no ownership transfer or sharing of the given handle is taking place.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settitle">
/// TTM_SETTITLE</a> in the Windows API.
//
inline auto TTooltip::SetTitle(const tstring& title, TTitleIcon icon) -> TTooltip&
{
  const auto iconParam = std::holds_alternative<HICON>(icon) ?
    static_cast<TParam1>(reinterpret_cast<UINT_PTR>(std::get<HICON>(icon))) :
    static_cast<TParam1>(std::get<TPredefinedIcon>(icon));
  const auto r = SendMessage(TTM_SETTITLE, iconParam, reinterpret_cast<TParam2>(title.c_str()));
  if (!r) throw TXTooltip{_T("TTooltip::SetTitle: TTM_SETTITLE failed")};
  return *this;
}
 
//
/// Activates or deactivates the tooltip control.
///
/// If parameter \p activate is true, the tooltip is activated; otherwise it is deactivated.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-activate">
/// TTM_ACTIVATE</a> in the Windows API.
//
inline auto TTooltip::Activate(bool activate) -> TTooltip&
{
  SendMessage(TTM_ACTIVATE, TParam1(activate));
  return *this;
}
 
//
/// Calculates a tip text display rectangle from its window rectangle, or vice versa.
///
/// This message is particularly useful when you want to use a tooltip control to display the full
/// text of a string that is usually truncated. It is commonly used with List View and Tree View
/// controls. You typically send this message in response to a TTN_SHOW notification code so that
/// you can position the tooltip control properly.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-adjustrect">
/// TTM_ADJUSTRECT</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttn-show">
/// TTN_SHOW</a> in the Windows API.
//
inline auto TTooltip::AdjustRect(const TRect& rect, TAdjustRectOp op) -> TRect
{
  auto r = TRect{rect};
  const auto ok = SendMessage(TTM_ADJUSTRECT, op == aroTextToWindow ? TRUE : FALSE, reinterpret_cast<TParam2>(&r));
  if (!ok) throw TXTooltip{_T("TTM_ADJUSTRECT failed")};
  return r;
}
 
//
/// Removes a displayed tooltip window from view.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-pop">
/// TTM_POP</a> in the Windows API.
//
inline auto TTooltip::Pop() -> TTooltip&
{
  SendMessage(TTM_POP);
  return *this;
}
 
//
/// Activates or deactivates a tracking tooltip.
/// The given \p tool identifies the tool to be tracked.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-trackactivate">
/// TTM_TRACKACTIVATE</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/implement-tracking-tooltips">
/// How to Implement Tracking Tooltips</a> in the Windows API.
//
inline auto TTooltip::TrackActivate(TToolId tool, bool activate) -> TTooltip&
{
  const auto info = InitToolInfo(tool);
  SendMessage(TTM_TRACKACTIVATE, activate ? TRUE : FALSE, reinterpret_cast<TParam2>(&info));
  return *this;
}
 
//
/// Sets the position of a tracking tooltip.
///
/// \sa TrackActivate to activate tracking for a given tool.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-trackposition">
/// TTM_TRACKPOSITION</a> in the Windows API.
//
inline auto TTooltip::TrackPosition(const TPoint& p) -> TTooltip&
{
  SendMessage(TTM_TRACKPOSITION, 0, MkParam2(p.x, p.y));
  return *this;
}
 
//
/// Forces the current tooltip to be redrawn.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-update">
/// TTM_UPDATE</a> in the Windows API.
//
inline auto TTooltip::Update() -> TTooltip&
{
  SendMessage(TTM_UPDATE);
  return *this;
}
 
//
/// Passes a mouse message to the tooltip control for processing.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-relayevent">
/// TTM_RELAYEVENT</a> in the Windows API.
//
inline auto TTooltip::RelayEvent(MSG& msg) -> TTooltip&
{
  SendMessage(TTM_RELAYEVENT, 0, TParam2(&msg));
  return *this;
}
 
//
/// Registers a tool with the tooltip control.
///
/// The given TTTOOLINFO must have been initialized with the information needed for the tooltip
/// control to display tip text for the tool.
///
/// Example usage:
///
/// \code
/// const auto id = EncodeId(toolId);
/// AddTool(TTTOOLINFO
///   {
///     static_cast<UINT>(sizeof(TTTOOLINFO)), // cbSize
///     id.uFlags, // uFlags
///     id.hwnd, // hwnd
///     id.uId, // uId
///     RECT{}, // rect
///     GetModule()->GetHandle(), // hinst
///     IDS_TOOLTIP, // lpszText
///     0, // lParam
///     nullptr // lpReserved
///   });
/// \endcode
///
/// \note This is a low-level encapsulation of the Windows API. Prefer to use the higher-level
/// overloads of AddTool instead.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-addtool">
/// TTM_ADDTOOL</a> in the Windows API.
//
inline auto TTooltip::AddTool(const TTTOOLINFO& ti) -> TTooltip&
{
  const auto ok = SendMessage(TTM_ADDTOOL, 0, reinterpret_cast<TParam2>(&ti));
  if (!ok) throw TXTooltip{_T("TTM_ADDTOOL failed")};
  return *this;
}
 
//
/// Deregisters a tool from the tooltip control.
///
/// The given TTTOOLINFO must have been initialized with the information needed to identify the
/// tool to delete.
///
/// Example usage:
///
/// \code
/// const auto id = tooltip.EncodeId(toolId);
/// tooltip.DelTool(TTTOOLINFO
///   {
///     static_cast<UINT>(sizeof(TTTOOLINFO)), // cbSize
///     id.uFlags, // uFlags
///     id.hwnd, // hwnd
///     id.uId, // uId
///     RECT{}, // rect
///     nullptr, // hinst
///     nullptr, // lpszText
///     0, // lParam
///     nullptr // lpReserved
///   });
/// \endcode
///
/// \note This is a low-level encapsulation of the Windows API. Prefer to use the higher-level
/// overload of DelTool instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-deltool">
/// TTM_DELTOOL</a> in the Windows API.
//
inline auto TTooltip::DelTool(const TTTOOLINFO& ti) -> TTooltip&
{
  SendMessage(TTM_DELTOOL, 0, TParam2(&ti));
  return *this;
}
 
//
/// Retrieves the information that the tooltip control maintains about the specified tool.
///
/// \exception TXTooltip is thrown on failure.
///
/// \sa GetToolCount returns the number of tools registered for this tooltip.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-enumtools">
/// TTM_ENUMTOOLS</a> in the Windows API.
//
inline void TTooltip::EnumTools(int index, TTTOOLINFO& ti) const
{
  const auto r = SendMessage(TTM_ENUMTOOLS, index, reinterpret_cast<TParam2>(&ti));
  if (!r) throw TXTooltip{_T("TTM_ENUMTOOLS failed")};
}
 
//
/// Retrieves information about the current tool.
///
/// \returns Returns `true` if successful; otherwise `false`. If a current tool does not exist,
/// `false` is returned.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getcurrenttool">
/// TTM_GETCURRENTTOOL</a> in the Windows API.
//
inline auto TTooltip::GetCurrentTool(TTTOOLINFO& ti) const -> bool
{
  return SendMessage(TTM_GETCURRENTTOOL, 0, reinterpret_cast<TParam2>(&ti)) != 0;
}
 
//
/// Retrieves the text associated with the specified tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettext">
/// TTM_GETTEXT</a> in the Windows API.
//
inline void TTooltip::GetText(int bufSize, TTTOOLINFO& ti) const
{
  PRECONDITION(bufSize >= 0);
  SendMessage(TTM_GETTEXT, bufSize, reinterpret_cast<TParam2>(&ti));
}
 
//
/// Retrieves the information that the tooltip control maintains about a tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolinfo">
/// TTM_GETTOOLINFO</a> in the Windows API.
//
inline void TTooltip::GetToolInfo(TTTOOLINFO& ti) const
{
  const auto r = SendMessage(TTM_GETTOOLINFO, 0, reinterpret_cast<TParam2>(&ti));
  if (!r) throw TXTooltip{_T("TTM_GETTOOLINFO failed")};
}
 
//
/// Determines whether a given point is within the bounding rectangle of the specified tool.
/// The method also retrieves information about the tool, if one is identified at that location.
///
/// \returns Returns `true` if a tool if found at the location; otherwise `false`.
///
/// \note For this function to work, the tool must have the TTF_TRACK flag set. For more
/// information on this flag, see the documentation of TTTOOLINFO in the Windows API. This function
/// will return `false` if TTF_TRACK is not set, even if the given hit point is within the tool's
/// bounding rectangle.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-hittest">
/// TTM_HITTEST</a> in the Windows API.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
/// TTTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::HitTest(TTHITTESTINFO& ttHTInfo) const -> bool
{
  return SendMessage(TTM_HITTEST, 0, reinterpret_cast<TParam2>(&ttHTInfo)) != 0;
}
 
//
/// Properly initializes a TTTOOLINFO instance based on the tool ID given.
///
/// \param tool is a variant that can be TCtrlToolId, TRectToolId or TToolIndex.
///
/// \param shouldResolveToolIndex indicates whether the function should call TTooltip::GetTool to
/// retrieve the actual identifying fields for the tool (`TTTOOLINFO::hwnd` and `TTTOOLINFO::uId`),
/// when a TToolIndex is passed for parameter \p tool. If set to `false` (the default) these
/// fields will be initialized to `nullptr` and 0, respectively; i.e. the returned structure will
/// have no identifying information. In this case, the caller must provide this information.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfoa">
/// TTTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::InitToolInfo(TToolId tool, bool shouldResolveToolIndex) const -> TTTOOLINFO
{
  const auto initCtrlTool = [&](TCtrlToolId tool)
  {
    return TTTOOLINFO
    {
      static_cast<UINT>(sizeof(TTTOOLINFO)),
      TTF_IDISHWND, // uFlags
      tool.ToolContainer, // hwnd
      reinterpret_cast<UINT_PTR>(tool.Handle), // uId
      RECT{}, // rect
      nullptr, // hinst
      nullptr, // lpszText
      0, // lParam
      nullptr // lpReserved
    };
  };
  const auto initRectTool = [&](TRectToolId tool)
  {
    return TTTOOLINFO
    {
      static_cast<UINT>(sizeof(TTTOOLINFO)),
      0, // uFlags
      tool.ToolContainer, // hwnd
      tool.Id, // uId
      RECT{}, // rect
      nullptr, // hinst
      nullptr, // lpszText
      0, // lParam
      nullptr // lpReserved
    };
  };
  const auto initToolIndex = [&](TToolIndex tool)
  {
    return shouldResolveToolIndex ? InitToolInfo(GetTool(tool)->GetId()) :
      TTTOOLINFO{static_cast<UINT>(sizeof(TTTOOLINFO))};
  };
  return std::visit(TOverloaded{initCtrlTool, initRectTool, initToolIndex}, tool);
}
 
//
/// Updates the bounding rectangle of a tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-newtoolrect">
/// TTM_NEWTOOLRECT</a> in the Windows API.
//
inline auto TTooltip::NewToolRect(const TTTOOLINFO& ti) -> TTooltip&
{
  SendMessage(TTM_NEWTOOLRECT, 0, reinterpret_cast<TParam2>(&ti));
  return *this;
}
 
//
/// Sets the information that the tooltip control maintains for a particular tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settoolinfo">
/// TTM_SETTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::SetToolInfo(const TTTOOLINFO& ti) -> TTooltip&
{
  SendMessage(TTM_SETTOOLINFO, 0, reinterpret_cast<TParam2>(&ti));
  return *this;
}
 
//
/// Updates the tip for the given tool.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-updatetiptext">
/// TTM_UPDATETIPTEXT</a> in the Windows API.
//
inline auto TTooltip::UpdateTipText(const TTTOOLINFO& ti) -> TTooltip&
{
  SendMessage(TTM_UPDATETIPTEXT, 0, reinterpret_cast<TParam2>(&ti));
  return *this;
}
 
//#if defined(OWL5_COMPAT)
 
//
/// Retrieves the background color for the tooltip window.
///
/// \deprecated Use GetTipBkColor() const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettipbkcolor">
/// TTM_GETTIPBKCOLOR</a> in the Windows API.
//
inline auto TTooltip::GetBkColor() const -> TColor
{
  return TColor{static_cast<COLORREF>(SendMessage(TTM_GETTIPBKCOLOR))};
}
 
//
/// Retrieves the initial, pop-up or reshow duration currently set for a tooltip control.
///
/// \deprecated Use GetDelayTime(TDelayTime) const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getdelaytime">
/// TTM_GETDELAYTIME</a> in the Windows API.
//
inline auto TTooltip::GetDelayTime(uint flag) const -> int
{
  return static_cast<int>(SendMessage(TTM_GETDELAYTIME, flag));
}
 
//
/// Retrieves the top, left, bottom, and right margins set for a tooltip window.
///
/// \deprecated Use GetMargin() const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getmargin">
/// TTM_GETMARGIN</a> in the Windows API.
//
inline void TTooltip::GetMargins(int& left, int& top, int& right, int& bottom) const
{
  auto rect = TRect{};
  SendMessage(TTM_GETMARGIN, 0, reinterpret_cast<TParam2>(&rect));
  left = rect.left;
  top = rect.top;
  right = rect.right;
  bottom = rect.bottom;
}
 
//
///
/// Retrieves the text color used to display tip text.
///
/// \deprecated Use GetTipTextColor() const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettiptextcolor">
/// TTM_GETTIPTEXTCOLOR</a> in the Windows API.
//
inline auto TTooltip::GetTextColor() const -> TColor
{
  return TColor{static_cast<COLORREF>(SendMessage(TTM_GETTIPTEXTCOLOR))};
}
 
//
/// Removes a displayed tooltip window from view.
///
/// \deprecated Use TTooltip::Pop instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-pop">
/// TTM_POP</a> in the Windows API.
//
inline void TTooltip::HideTip()
{
  SendMessage(TTM_POP);
}
 
//
/// Sets the background color for the tooltip window.
///
/// \note When visual styles are enabled, this function has no effect.
///
/// \deprecated Use SetTipBkColor(const TColor&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settipbkcolor">
/// TTM_SETTIPBKCOLOR</a> in the Windows API.
//
inline void TTooltip::SetBkColor(const TColor& c)
{
  SendMessage(TTM_SETTIPBKCOLOR, static_cast<TParam2>(c.GetValue()));
}
 
//
/// Sets the initial, popup or reshow duration for a tooltip control.
///
/// \param flag can be one of the following:
///
/// - \c TTDT_AUTOMATIC --- automatically calculates the initial, reshow, and autopopup duration
/// based on parameter \p delay.
///
/// - \c TTDT_AUTOPOP --- sets the length of time before the tooltip window is hidden if the cursor
/// remains stationary in the tool's bounding rectangle after the tooltip window has disappeared.
///
/// - \c TTDT_INITIAL --- sets the length of time that the cursor must remain stationary within the
/// bounding rectangle before the tooltip window is displayed.
///
/// - \c TTDT_RESHOW: Sets the length of time before subsequent tooltip windows are displayed when
/// the cursor is moved from one tool to another.
///
/// \param delay is the duration in milliseconds.
///
/// \deprecated Use SetDelayTime(TDelayTime, int delay) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-setdelaytime">
/// TTM_SETDELAYTIME</a> in the Windows API.
//
inline void TTooltip::SetDelayTime(uint flag, int delay)
{
  SendMessage(TTM_SETDELAYTIME, flag, delay);
}
 
//
/// Sets the top, left, bottom, and right margins for a tooltip window.
///
/// \deprecated Use SetMargin(const TMargin&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-setmargin">
/// TTM_SETMARGIN</a> in the Windows API.
//
inline void TTooltip::SetMargins(int left, int top, int right, int bottom)
{
  const auto rect = TRect{left, top, right, bottom};
  SendMessage(TTM_SETMARGIN, 0, reinterpret_cast<TParam2>(&rect));
}
 
//
/// Sets the text color for the tooltip window.
///
/// \note When visual styles are enabled, this function has no effect.
///
/// \deprecated Use SetTipTextColor(const TColor&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settiptextcolor">
/// TTM_SETTIPTEXTCOLOR</a> in the Windows API.
//
inline void TTooltip::SetTextColor(const TColor& c)
{
  SendMessage(TTM_SETTIPTEXTCOLOR, static_cast<TParam2>(c.GetValue()));
}
 
//
/// Registers a tool with the tooltip control.
///
/// \deprecated Use the other AddTool overloads or AddTool(const TTTOOLINFO&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-addtool">
/// TTM_ADDTOOL</a> in the Windows API.
//
inline auto TTooltip::AddTool(const TToolInfo& ti) -> bool
{
  return SendMessage(TTM_ADDTOOL, 0, reinterpret_cast<TParam2>(&ti)) != 0;
}
 
//
/// Removes a tool from the tooltip control.
///
/// \deprecated Use DelTool(TToolId) or DelTool(const TTTOOLINFO&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-deltool">
/// TTM_DELTOOL</a> in the Windows API.
//
inline void TTooltip::DeleteTool(const TToolInfo& ti)
{
  DelTool(static_cast<const TTTOOLINFO&>(ti));
}
 
//
/// Retrieves the information that the tooltip control maintains about the specified tool.
///
/// \code
/// auto tooltip = GetTooltip(); CHECK(tooltip);
/// const auto count = tooltip->GetToolCount();
/// for (auto i = 0; i != count; ++i)
/// {
///   auto ti = TToolInfo{};
///   const auto r = tooltip->EnumTools(i, ti);
///   if (!r) throw std::runtime_error{"EnumTools failed"};
///   // ...
/// }
/// \endcode
///
/// \returns `true` if successful, `false` otherwise.
///
/// \deprecated Use GetTool(TToolId) const or EnumTools(int index, TTTOOLINFO&) const instead.
/// For iteration of the tools, use GetTools() const and iterate over the tools in standard ways.
///
/// \sa GetToolCount returns the number of tools registered for this tooltip.
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-enumtools">
/// TTM_ENUMTOOLS</a> in the Windows API.
//
inline auto TTooltip::EnumTools(int index, TToolInfo& ti) const -> bool
{
  return SendMessage(TTM_ENUMTOOLS, index, reinterpret_cast<TParam2>(&ti)) != 0;
}
 
//
/// Retrieves information about the current tool. 
///
/// \returns Returns `true` if successful; otherwise `false`.
///
/// \deprecated Use GetCurrentTool() const or GetCurrentTool(TTTOOLINFO&) const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-getcurrenttool">
/// TTM_GETCURRENTTOOL</a> in the Windows API.
//
inline auto TTooltip::GetCurrentTool(TToolInfo& ti) const -> bool
{
  return SendMessage(TTM_GETCURRENTTOOL, 0, reinterpret_cast<TParam2>(&ti)) != 0;
}
 
//
/// Retrieves the information that the tooltip control maintains about a tool.
///
/// You must invoke TToolInfo::SetToolHandle or TToolInfo::SetToolId to identify the tool.
/// I.e. the `hwnd` and `uId` members of the TToolInfo structure must identify the tool.
///
/// \returns Returns `true` if successful; otherwise `false`.
///
/// \deprecated Use GetTool(TToolId) const or GetToolInfo(TTTOOLINFO&) const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettoolinfo">
/// TTM_GETTOOLINFO</a> in the Windows API.
//
inline auto TTooltip::GetToolInfo(TToolInfo& ti) const -> bool
{
  return SendMessage(TTM_GETTOOLINFO, 0, reinterpret_cast<TParam2>(&ti)) != 0;
}
 
//
/// Retrieves the text associated with the specified tool. 
///
/// You must invoke TToolInfo::SetToolHandle or TToolInfo::SetToolId to identify the tool.
/// I.e. the `hwnd` and `uId` members of the TToolInfo structure must identify the tool.
///
/// \deprecated Use GetText(TToolId) const or GetText(TTTOOLINFO&) const instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-gettext">
/// TTM_GETTEXT</a> in the Windows API.
//
inline void TTooltip::GetToolText(TToolInfo& ti) const
{
  SendMessage(TTM_GETTEXT, 0, reinterpret_cast<TParam2>(&ti));
}
 
//
/// Updates the bounding rectangle of a tool.
///
/// You must invoke TToolInfo::SetToolHandle or TToolInfo::SetToolId to identify the tool.
/// I.e. the `hwnd` and `uId` members of the TToolInfo structure must identify the tool.
///
/// \deprecated Use NewToolRect(TToolId, const TRect&) or NewToolRect(TTTOOLINFO&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-newtoolrect">
/// TTM_NEWTOOLRECT</a> in the Windows API.
//
inline void TTooltip::NewToolRect(const TToolInfo& ti)
{
  SendMessage(TTM_NEWTOOLRECT, 0, reinterpret_cast<TParam2>(&ti));
}
 
//
/// Sets the information that the tooltip control maintains for a particular tool.
///
/// You must invoke TToolInfo::SetToolHandle or TToolInfo::SetToolId to identify the tool.
/// I.e. the `hwnd` and `uId` members of the TToolInfo structure must identify the tool.
///
/// \deprecated Prefer to use the high-level attribute setters, e.g. SetFlags(TToolId, uint flags),
/// or use the low-level SetToolInfo(TTTOOLINFO&) instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-settoolinfo">
/// TTM_SETTOOLINFO</a> in the Windows API.
//
inline void TTooltip::SetToolInfo(const TToolInfo& ti)
{
  SendMessage(TTM_SETTOOLINFO, 0, reinterpret_cast<TParam2>(&ti));
}
 
//
/// Updates the tip for the given tool.
///
/// You must invoke TToolInfo::SetToolHandle or TToolInfo::SetToolId to identify the tool.
/// I.e. the `hwnd` and `uId` members of the TToolInfo structure must identify the tool.
///
/// \deprecated Use UpdateTipText(TToolId, const tstring&) or UpdateTipText(const TTTOOLINFO&)
/// instead.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/controls/ttm-updatetiptext">
/// TTM_UPDATETIPTEXT</a> in the Windows API.
//
inline void TTooltip::UpdateTipText(const TToolInfo& ti)
{
  SendMessage(TTM_UPDATETIPTEXT, 0, reinterpret_cast<TParam2>(&ti));
}
 
//#endif // OWL5_COMPAT
 
} // OWL namespace
 
#endif  // OWL_TOOLTIP_H

V524 It is odd that the body of 'GetBkColor' function is fully equivalent to the body of 'GetTipBkColor' function.

V524 It is odd that the body of 'GetTextColor' function is fully equivalent to the body of 'GetTipTextColor' function.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.

V813 Decreased performance. The 'tool' argument should probably be rendered as a constant reference.