/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2004 Chris Schoeneman
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file COPYING that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "COSXClipboard.h"
#include "COSXClipboardUTF16Converter.h"
#include "COSXClipboardTextConverter.h"
#include "CLog.h"
#include "XArch.h"

//
// COSXClipboard
//

COSXClipboard::COSXClipboard() :
	m_time(0),
	m_scrap(NULL)
{
	m_converters.push_back(new COSXClipboardUTF16Converter);
	m_converters.push_back(new COSXClipboardTextConverter);
}

COSXClipboard::~COSXClipboard()
{
	clearConverters();
}

bool
COSXClipboard::empty()
{
	LOG((CLOG_DEBUG "empty clipboard"));
	assert(m_scrap != NULL);

	OSStatus err = ClearScrap(&m_scrap);
	if (err != noErr) {
		LOG((CLOG_DEBUG "failed to grab clipboard"));
		return false;
	}

	// we own the clipboard
	err = PutScrapFlavor(
				m_scrap,
				getOwnershipFlavor(),
				kScrapFlavorMaskNone,
				0,
				0);
	if (err != noErr) {
		LOG((CLOG_DEBUG "failed to grab clipboard"));
		return false;
	}

	return true;
}

void
COSXClipboard::add(EFormat format, const CString & data)
{
	LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format));

	for (ConverterList::const_iterator index = m_converters.begin();
								index != m_converters.end(); ++index) {

		IOSXClipboardConverter* converter = *index;

		// skip converters for other formats
		if (converter->getFormat() == format) {
			CString osXData = converter->fromIClipboard(data);
			ScrapFlavorType flavorType = converter->getOSXFormat();

			PutScrapFlavor(
				m_scrap,
				flavorType,
				kScrapFlavorMaskNone,
				osXData.size(),
				osXData.data()); 
		}
	}
}

bool
COSXClipboard::open(Time time) const 
{
	LOG((CLOG_DEBUG "open clipboard"));
	m_time = time;
	OSStatus err = GetCurrentScrap(&m_scrap);
	return (err == noErr);
}

void
COSXClipboard::close() const
{
	LOG((CLOG_DEBUG "close clipboard"));
	m_scrap = NULL;
}

IClipboard::Time
COSXClipboard::getTime() const
{
	return m_time;
}

bool
COSXClipboard::has(EFormat format) const
{
	assert(m_scrap != NULL);

	for (ConverterList::const_iterator index = m_converters.begin();
							index != m_converters.end(); ++index) {
		IOSXClipboardConverter* converter = *index;
		if (converter->getFormat() == format) {
			ScrapFlavorFlags flags;
			ScrapFlavorType type = converter->getOSXFormat();

			if (GetScrapFlavorFlags(m_scrap, type, &flags) == noErr) {
				return true;
			}
		}
	}

	return false;
}

CString
COSXClipboard::get(EFormat format) const
{
	CString result;

	// find the converter for the first clipboard format we can handle
	IOSXClipboardConverter* converter = NULL;
	for (ConverterList::const_iterator index = m_converters.begin();
							index != m_converters.end(); ++index) {
		converter = *index;

		ScrapFlavorFlags flags;
		ScrapFlavorType type = converter->getOSXFormat();

		if (converter->getFormat() == format &&
			GetScrapFlavorFlags(m_scrap, type, &flags) == noErr) {
			break;
		}
		converter = NULL;
	}

	// if no converter then we don't recognize any formats
	if (converter == NULL) {
		return result;
	}

	// get the clipboard data.
	char* buffer = NULL;
	try {
		Size flavorSize;
		OSStatus err = GetScrapFlavorSize(m_scrap,
							converter->getOSXFormat(), &flavorSize);
		if (err != noErr) {
			throw err;
		}

		buffer = new char[flavorSize];
		if (buffer == NULL) {
			throw memFullErr;
		}

		err = GetScrapFlavorData(m_scrap,
							converter->getOSXFormat(), &flavorSize, buffer);
		if (err != noErr) {
			throw err;
		}

		result = CString(buffer, flavorSize);
	}
	catch (OSStatus err) {
		LOG((CLOG_DEBUG "exception thrown in COSXClipboard::get MacError (%d)", err));
	}
	catch (...) {
		LOG((CLOG_DEBUG "unknown exception in COSXClipboard::get"));
		RETHROW_XTHREAD
	}
	delete[] buffer;

	return converter->toIClipboard(result);
}

void
COSXClipboard::clearConverters()
{
	for (ConverterList::iterator index = m_converters.begin();
							index != m_converters.end(); ++index) {
		delete *index;
	}
	m_converters.clear();
}

bool 
COSXClipboard::isOwnedBySynergy()
{
	ScrapFlavorFlags flags;
	ScrapRef scrap;
	OSStatus err = GetCurrentScrap(&scrap);
	if (err == noErr) {
		err = GetScrapFlavorFlags(scrap, getOwnershipFlavor() , &flags);
	}
	return (err == noErr);
}

ScrapFlavorType 
COSXClipboard::getOwnershipFlavor()
{
	return 'Syne';
}


syntax highlighted by Code2HTML, v. 0.9.1