# testClipboard.py
import unittest
import pythoncom
import win32con
import winerror
import win32clipboard

from pywin32_testutil import str2bytes

from win32com.server.util import NewEnum, wrap
from win32com.server.exception import COMException

IDataObject_Methods = """GetData GetDataHere QueryGetData
                         GetCanonicalFormatEtc SetData EnumFormatEtc
                         DAdvise DUnadvise EnumDAdvise""".split()

# A COM object implementing IDataObject used for basic testing.
num_do_objects = 0

def WrapCOMObject(ob, iid=None):
    return wrap(ob, iid=iid, useDispatcher = 0)

class TestDataObject:
    _com_interfaces_ = [pythoncom.IID_IDataObject]
    _public_methods_ = IDataObject_Methods
    def __init__(self, strval):
        global num_do_objects
        num_do_objects += 1
        self.strval = strval
        self.supported_fe = []
        for cf in (win32con.CF_TEXT, win32con.CF_UNICODETEXT):
            fe = cf, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
            self.supported_fe.append(fe)

    def __del__(self):
        global num_do_objects
        num_do_objects -= 1

    def _query_interface_(self, iid):
        if iid==pythoncom.IID_IEnumFORMATETC:
            return NewEnum(self.supported_fe, iid=iid)

    def GetData(self, fe):
        ret_stg = None
        cf, target, aspect, index, tymed  = fe
        if aspect & pythoncom.DVASPECT_CONTENT and \
           tymed==pythoncom.TYMED_HGLOBAL:
            if cf == win32con.CF_TEXT:
                ret_stg = pythoncom.STGMEDIUM()
                # ensure always 'bytes' by encoding string.
                ret_stg.set(pythoncom.TYMED_HGLOBAL, str2bytes(self.strval))
            elif cf == win32con.CF_UNICODETEXT:
                ret_stg = pythoncom.STGMEDIUM()
                ret_stg.set(pythoncom.TYMED_HGLOBAL, unicode(self.strval))

        if ret_stg is None:
            raise COMException(hresult=winerror.E_NOTIMPL)
        return ret_stg

    def GetDataHere(self, fe):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def QueryGetData(self, fe):
        cf, target, aspect, index, tymed  = fe
        if aspect & pythoncom.DVASPECT_CONTENT == 0:
            raise COMException(hresult=winerror.DV_E_DVASPECT)
        if tymed!=pythoncom.TYMED_HGLOBAL:
            raise COMException(hresult=winerror.DV_E_TYMED)
        return None # should check better

    def GetCanonicalFormatEtc(self, fe):
        RaiseCOMException(winerror.DATA_S_SAMEFORMATETC)
        # return fe

    def SetData(self, fe, medium):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def EnumFormatEtc(self, direction):
        if direction != pythoncom.DATADIR_GET:
            raise COMException(hresult=winerror.E_NOTIMPL)
        return NewEnum(self.supported_fe, iid=pythoncom.IID_IEnumFORMATETC)

    def DAdvise(self, fe, flags, sink):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def DUnadvise(self, connection):
        raise COMException(hresult=winerror.E_NOTIMPL)

    def EnumDAdvise(self):
        raise COMException(hresult=winerror.E_NOTIMPL)

class ClipboardTester(unittest.TestCase):
    def setUp(self):
        pythoncom.OleInitialize()
    def tearDown(self):
        try:
            pythoncom.OleFlushClipboard()
        except pythoncom.com_error:
            # We never set anything!
            pass
    def testIsCurrentClipboard(self):
        do = TestDataObject("Hello from Python")
        do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
        pythoncom.OleSetClipboard(do)
        self.failUnless(pythoncom.OleIsCurrentClipboard(do))

    def testComToWin32(self):
        # Set the data via our DataObject
        do = TestDataObject("Hello from Python")
        do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
        pythoncom.OleSetClipboard(do)
        # Then get it back via the standard win32 clipboard functions.
        win32clipboard.OpenClipboard()
        got = win32clipboard.GetClipboardData(win32con.CF_TEXT)
        # CF_TEXT gives bytes on py3k - use str2bytes() to ensure that's true.
        expected = str2bytes("Hello from Python")
        self.assertEqual(got, expected)
        # Now check unicode
        got = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
        self.assertEqual(got, u"Hello from Python")
        win32clipboard.CloseClipboard()

    def testWin32ToCom(self):
        # Set the data via the std win32 clipboard functions.
        val = str2bytes("Hello again!") # ensure always bytes, even in py3k
        win32clipboard.OpenClipboard()
        win32clipboard.SetClipboardData(win32con.CF_TEXT, val)
        win32clipboard.CloseClipboard()
        # and get it via an IDataObject provided by COM
        do = pythoncom.OleGetClipboard()
        cf = win32con.CF_TEXT, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
        stg = do.GetData(cf)
        got = stg.data
        # The data we get back has the \0, as our STGMEDIUM has no way of 
        # knowing if it meant to be a string, or a binary buffer, so
        # it must return it too.
        self.failUnlessEqual(got, str2bytes("Hello again!\0"))
        
    def testDataObjectFlush(self):
        do = TestDataObject("Hello from Python")
        do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
        pythoncom.OleSetClipboard(do)
        self.assertEqual(num_do_objects, 1)

        do = None # clear my ref!
        pythoncom.OleFlushClipboard()
        self.assertEqual(num_do_objects, 0)

    def testDataObjectReset(self):
        do = TestDataObject("Hello from Python")
        do = WrapCOMObject(do)
        pythoncom.OleSetClipboard(do)
        do = None # clear my ref!
        self.assertEqual(num_do_objects, 1)
        pythoncom.OleSetClipboard(None)
        self.assertEqual(num_do_objects, 0)

if __name__=='__main__':
    from win32com.test import util
    util.testmain()
