//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TClipboard which provides clipboard encapsulation
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/clipboar.h>
#include <memory> // unique_ptr
 
namespace owl {
 
OWL_DIAGINFO;
 
/// Points to a string that specifies the name of the protocol the client needs. The
/// default protocol is "StdFileEditing," which is the name of the object linking
/// and embedding protocol.
LPCTSTR TClipboard::DefaultProtocol = _T("StdFileEditing");
 
/// \class TClipboard
/// TClipboard encapsulates and manipulates Clipboard data. You can open, close,
/// empty, and paste data in a variety of data formats between the Clipboard and the
/// open window.  An object on the Clipboard can exist in a variety of Clipboard
/// formats, which range from bitmaps to text.
///
/// Usually, the window is in charge of manipulating Clipboard interactions between
/// the window and the Clipboard. It does this by responding to messages sent
/// between the Clipboard owner and the application. The following ObjectWindows
/// event-handling functions encapsulate these Clipboard messages:
/// EvRenderFormat -   Responds to a WM_RENDERFORMAT message sent to the Clipboard
/// owner if a specific Clipboard format that an application has requested hasn't
/// been rendered. After the Clipboard owner renders the data in the requested
/// format, it callsSetClipboardData to place the data on the Clipboard.
/// EvRenderAllFormats -   Responds to a message sent to the Clipboard owner if the
/// Clipboard owner has delayed rendering a Clipboard format.  After the Clipboard
/// owner renders data in all of possible formats, it calls SetClipboardData.
///
/// The following example tests to see If there is a palette on the Clipboard. If
/// one exists, TClipboard retrieves the palette, realizes it, and then closes the
/// Clipboard.
/// \code
///   if (clipboard.IsClipboardFormatAvailable(CF_PALETTE)) {
///     newPal = new TPalette(TPalette(clipboard));  // make a copy
///     UpdatePalette(true);
///   }
///   // Try DIB format first
///   if (clipboard.IsClipboardFormatAvailable(CF_DIB)) {
///     newDib = new TDib(TDib(clipboard));        // make a copy
///     newBitmap = new TBitmap(*newDib, newPal);  // newPal==0 is OK
///   // try metafile Second
///   //
///   } else if (clipboard.IsClipboardFormatAvailable(CF_METAFILEPICT)) {
///     if (!newPal)
///       newPal = new TPalette((HPALETTE)GetStockObject(DEFAULT_PALETTE));
///     newBitmap = new TBitmap(TMetaFilePict(clipboard), *newPal,
///                             GetClientRect().Size());
/// ...
///   // Gets a bitmap , keeps it, and sets up data on the clipboard.
///   //
///   delete Bitmap;
///   Bitmap = newBitmap;
///
///   if (!newDib)
///     newDib = new TDib(*newBitmap, newPal);
/// #endif    // ????
///   delete Dib;
///   Dib = newDib;
///
///   delete Palette;
///   Palette = newPal ? newPal : new TPalette(*newDib);
///   Palette->GetObject(Colors);
///
///   PixelWidth  = Dib->Width();
///   PixelHeight = Dib->Height();
///   AdjustScroller();
///   SetCaption("(Clipboard)");
///
///   clipboard.CloseClipboard();
/// \endcode
 
 
 
//
/// Constructs a clipboard object to grab the clipboard given a window handle.
/// This is the preferred method of getting the clipboard;
//
/// Throws an exception on open failure if mustOpen is true (default)
/// mustOpen can be passed as false for compatability
//
TClipboard::TClipboard(HWND hWnd, bool mustOpen)
{
  OpenClipboard(hWnd);
  if (mustOpen && !IsOpen)
    throw TXClipboard(IDS_CLIPBOARDBUSY);
}
 
//
/// Destruct a clipboard object & close the clipboard if open
//
TClipboard::~TClipboard()
{
  if (IsOpen)
    ::CloseClipboard();
}
 
//
/// If the Clipboard is opened (IsOpen is true), closes the Clipboard. Closing the
/// Clipboard allows other applications to access the Clipboard.
//
void
TClipboard::CloseClipboard()
{
  if (IsOpen) {
    ::CloseClipboard();
    IsOpen = false;
  }
}
 
//
/// Opens the Clipboard and associates it with the window specified in Wnd. Other
/// applications cannot change the Clipboard data until the Clipboard is closed.
/// Returns true if successful; otherwise, returns false.
//
bool
TClipboard::OpenClipboard(HWND hWnd)
{
  return IsOpen = ::OpenClipboard(hWnd) != 0;
}
 
//
/// Copies the given string to the clipboard.
/// \exception TXClipboard is thrown on failure.
/// \sa SetClipboardData is called to perform the clipboard update.
//
void TClipboard::Copy(const tstring_view& s)
{
  const auto bufSize = (s.size() + 1) * sizeof(tchar); // Include space for null-termination.
  using THandle = std::unique_ptr<void, decltype(&GlobalFree)>;
  auto h = THandle{GlobalAlloc(GMEM_MOVEABLE, bufSize), &GlobalFree};
  if (!h) throw TXClipboard{_T("TClipboard::Copy: GlobalAlloc failed, GetLastError: " + to_tstring(GetLastError()))};
 
  const auto buf = GlobalLock(h.get()); CHECK(buf);
  using TLock = std::unique_ptr<void, decltype(&GlobalUnlock)>;
  auto lock = TLock{h.get(), &GlobalUnlock};
  memcpy_s(buf, bufSize, s.data(), s.size() * sizeof(tchar));
  static_cast<tchar*>(buf)[s.size()] = _T('\0'); // Null-terminate.
  lock.reset(); // Calls GlobalUnlock.
 
  const auto f = sizeof(tchar) > 1 ? CF_UNICODETEXT : CF_TEXT;
  EmptyClipboard();
  const auto r = SetClipboardData(f, h.get());
  if (r == nullptr) throw TXClipboard{_T("TClipboard::Copy: SetClipboardData failed, GetLastError: " + to_tstring(GetLastError()))};
  h.release(); // SetClipboardData took ownership of the handle.
}
 
//
/// Retrieves a copy of the string currently on the clipboard, if any.
/// \returns An empty string is returned, if the clipboard is empty or does not contain a string.
/// \sa GetClipboardData is called to perform the string retrieval.
//
auto TClipboard::PasteString() const -> tstring
{
  const auto f = sizeof(tchar) == 1 ? CF_TEXT : CF_UNICODETEXT;
  if (!IsClipboardFormatAvailable(f)) return {};
  const auto h = GetClipboardData(f);
  if (!h) throw TXClipboard{_T("TClipboard::PasteString: GetClipboardData failed, GetLastError: " + to_tstring(GetLastError()))};
  const auto buf = GlobalLock(h); CHECK(buf);
  using TLock = std::unique_ptr<void, decltype(&GlobalUnlock)>;
  auto lock = TLock{h, &GlobalUnlock}; // Calls GlobalUnlock at end of scope.
  return {static_cast<LPCTSTR>(buf)};
}
 
//----------------------------------------------------------------------------
 
//
/// Construct an available format iterator for a clipboard.
//
#if __DEBUG >= 1
TClipboardFormatIterator::TClipboardFormatIterator(const TClipboard& clip)
#else
TClipboardFormatIterator::TClipboardFormatIterator(const TClipboard& /*clip*/)
#endif
{
  PRECONDITION(bool(clip));
  Restart();
}
 
//
/// Restart the format iterator.
//
void
TClipboardFormatIterator::Restart()
{
  _Current = ::EnumClipboardFormats(0);
}
 
//
/// Get the next available format.
//
uint
TClipboardFormatIterator::operator ++()
{
  return _Current = ::EnumClipboardFormats(_Current);
}
 
//
/// Get the previous format.
//
uint
TClipboardFormatIterator::operator ++(int)
{
  uint current = _Current;
  _Current = ::EnumClipboardFormats(_Current);
  return current;
}
 
//----------------------------------------------------------------------------
 
//
/// Create the TXClipboard exception with a string resource.
//
TXClipboard::TXClipboard(uint resId)
:
  TXOwl(resId)
{
}
 
//
// Clone the exception for safe throwing in Windows.
//
TXClipboard*
TXClipboard::Clone()
{
  return new TXClipboard(*this);
}
 
 
//
/// Throw the exception.
//
void
TXClipboard::Throw()
{
  throw *this;
}
 
//
/// Throw the exception.
//
void
TXClipboard::Raise(uint resourceId)
{
  TXClipboard(resourceId).Throw();
}
 
} // OWL namespace
/////////////////////////////////

V1004 The 'buf' pointer was used unsafely after it was verified against nullptr. Check lines: 143, 146.