/*-
* Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#include "apricot.h"
#include "Application.h"
#include "Image.h"
#include "Clipboard.h"
#include <Clipboard.inc>
#ifdef __cplusplus
extern "C" {
#endif
#undef my
#define inherited CComponent->
#define my ((( PClipboard) self)-> self)
#define var (( PClipboard) self)
#define cefInit 0
#define cefDone 1
#define cefStore 2
#define cefFetch 3
struct _ClipboardFormatReg;
typedef SV* ClipboardExchangeFunc( Handle self, struct _ClipboardFormatReg * instance, int function, SV * data);
typedef ClipboardExchangeFunc *PClipboardExchangeFunc;
typedef struct _ClipboardFormatReg
{
char *id;
Handle sysId;
ClipboardExchangeFunc *server;
void *data;
Bool written;
} ClipboardFormatReg, *PClipboardFormatReg;
static SV * text_server ( Handle self, PClipboardFormatReg, int, SV *);
static SV * utf8_server ( Handle self, PClipboardFormatReg, int, SV *);
static SV * image_server ( Handle self, PClipboardFormatReg, int, SV *);
static SV * binary_server( Handle self, PClipboardFormatReg, int, SV *);
static int clipboards = 0;
static int formatCount = 0;
static Bool protect_formats = false;
static PClipboardFormatReg formats = nil;
void *
Clipboard_register_format_proc( Handle self, char * format, void * serverProc);
void
Clipboard_init( Handle self, HV * profile)
{
inherited init( self, profile);
if ( !apc_clipboard_create(self))
croak( "Cannot create clipboard");
if (clipboards == 0) {
Clipboard_register_format_proc( self, "Text", (void*)text_server);
Clipboard_register_format_proc( self, "Image", (void*)image_server);
Clipboard_register_format_proc( self, "UTF8", (void*)utf8_server);
protect_formats = 1;
}
clipboards++;
CORE_INIT_TRANSIENT(Clipboard);
}
void
Clipboard_done( Handle self)
{
clipboards--;
if ( clipboards == 0) {
protect_formats = 0;
while( formatCount)
my-> deregister_format( self, formats-> id);
}
apc_clipboard_destroy(self);
inherited done( self);
}
Bool
Clipboard_validate_owner( Handle self, Handle * owner, HV * profile)
{
dPROFILE;
if ( pget_H( owner) != application || application == nilHandle) return false;
*owner = application;
return true;
}
typedef Bool ActionProc ( Handle self, PClipboardFormatReg item, void * params);
typedef ActionProc *PActionProc;
static PClipboardFormatReg
first_that( Handle self, void * actionProc, void * params)
{
int i;
PClipboardFormatReg list = formats;
if ( actionProc == nil) return nil;
for ( i = 0; i < formatCount; i++) {
if ((( PActionProc) actionProc)( self, list+i, params))
return list+i;
}
return nil;
}
static Bool
find_format( Handle self, PClipboardFormatReg item, char *format)
{
return strcmp( item-> id, format) == 0;
}
static Bool
reset_written( Handle self, PClipboardFormatReg item, char *format)
{
item-> written = false;
return false;
}
void *
Clipboard_register_format_proc( Handle self, char * format, void * serverProc)
{
PClipboardFormatReg list = first_that( self, (void*)find_format, format);
if ( list) {
my-> deregister_format( self, format);
}
if (!( list = allocn( ClipboardFormatReg, formatCount + 1)))
return nil;
if ( formats != nil) {
memcpy( list, formats, sizeof( ClipboardFormatReg) * formatCount);
free( formats);
}
formats = list;
list += formatCount++;
list-> id = duplicate_string( format);
list-> server = ( ClipboardExchangeFunc *) serverProc;
list-> sysId = ( Handle) list-> server( self, list, cefInit, nilSV);
return list;
}
void
Clipboard_deregister_format( Handle self, char * format)
{
PClipboardFormatReg fr, list;
if ( protect_formats && (
( strlen( format) == 0) ||
( strcmp( format, "Text") == 0) ||
( strcmp( format, "UTF8") == 0) ||
( strcmp( format, "Image") == 0)))
return;
fr = first_that( self, (void*)find_format, format);
if ( fr == nil) return;
list = formats;
fr-> server( self, fr, cefDone, nilSV);
free( fr-> id);
formatCount--;
memmove( fr, fr + 1, sizeof( ClipboardFormatReg) * ( formatCount - ( fr - list)));
if ( formatCount > 0) {
if (( fr = allocn( ClipboardFormatReg, formatCount)))
memcpy( fr, list, sizeof( ClipboardFormatReg) * formatCount);
} else
fr = nil;
free( formats);
formats = fr;
}
Bool
Clipboard_open( Handle self)
{
var-> openCount++;
if ( var-> openCount > 1) return true;
first_that( self, (void*) reset_written, nil);
return apc_clipboard_open( self);
}
void
Clipboard_close( Handle self)
{
if ( var-> openCount > 0) {
PClipboardFormatReg text, utf8;
var-> openCount--;
if ( var-> openCount > 0) return;
text = formats + cfText;
utf8 = formats + cfUTF8;
/* automatically downgrade UTF8 to TEXT */
if ( utf8-> written && !text-> written) {
SV *utf8_sv, *text_sv;
if (( utf8_sv = utf8-> server( self, utf8, cefFetch, nilSV))) {
STRLEN bytelen, charlen, bytecount;
U8 * src;
src = ( U8 *) SvPV( utf8_sv, bytelen);
bytecount = bytelen;
text_sv = newSVpvn("", 0);
while ( bytecount > 0) {
register UV u =
#if PERL_PATCHLEVEL >= 16
utf8_to_uvchr_buf( src, src + bytelen, &charlen)
#else
utf8_to_uvchr( src, &charlen)
#endif
;
char c = ( u < 0x7f) ? u : '?';
src += charlen;
bytecount -= charlen;
sv_catpvn( text_sv, &c, 1);
if ( charlen == 0 ) break;
}
text-> server( self, text, cefFetch, text_sv);
sv_free( text_sv);
}
}
apc_clipboard_close( self);
} else
var-> openCount = 0;
}
Bool
Clipboard_format_exists( Handle self, char * format)
{
Bool ret;
PClipboardFormatReg fr = first_that( self, (void*)find_format, format);
if ( !fr) return false;
my-> open( self);
ret = apc_clipboard_has_format( self, fr-> sysId);
my-> close( self);
return ret;
}
SV *
Clipboard_fetch( Handle self, char * format)
{
SV * ret;
PClipboardFormatReg fr = first_that( self, (void*)find_format, format);
my-> open( self);
if ( !fr || !my-> format_exists( self, format))
ret = newSVsv( nilSV);
else
ret = fr-> server( self, fr, cefFetch, nilSV);
my-> close( self);
return ret;
}
void
Clipboard_store( Handle self, char * format, SV * data)
{
PClipboardFormatReg fr = first_that( self, (void*)find_format, format);
if ( !fr) return;
my-> open( self);
if ( var-> openCount == 1) {
first_that( self, (void*) reset_written, nil);
apc_clipboard_clear( self);
}
fr-> server( self, fr, cefStore, data);
my-> close( self);
}
void
Clipboard_clear( Handle self)
{
my-> open( self);
first_that( self, (void*) reset_written, nil);
apc_clipboard_clear( self);
my-> close( self);
}
SV *
Clipboard_get_handle( Handle self)
{
char buf[ 256];
snprintf( buf, 256, "0x%08lx", apc_clipboard_get_handle( self));
return newSVpv( buf, 0);
}
Bool
Clipboard_register_format( Handle self, char * format)
{
void * proc;
if (( strlen( format) == 0) ||
( strcmp( format, "Text") == 0) ||
( strcmp( format, "UTF8") == 0) ||
( strcmp( format, "Image") == 0))
return false;
proc = Clipboard_register_format_proc( self, format, (void*)binary_server);
return proc != nil;
}
XS( Clipboard_get_formats_FROMPERL)
{
dXSARGS;
Handle self;
int i;
PClipboardFormatReg list;
if ( items != 1)
croak ("Invalid usage of Clipboard.get_formats");
SP -= items;
self = gimme_the_mate( ST( 0));
if ( self == nilHandle)
croak( "Illegal object reference passed to Clipboard.get_formats");
my-> open( self);
list = formats;
for ( i = 0; i < formatCount; i++) {
if ( !apc_clipboard_has_format( self, list[ i]. sysId)) continue;
XPUSHs( sv_2mortal( newSVpv( list[ i]. id, 0)));
}
my-> close( self);
PUTBACK;
}
XS( Clipboard_get_registered_formats_FROMPERL)
{
dXSARGS;
Handle self;
int i;
PClipboardFormatReg list;
if ( items < 1)
croak ("Invalid usage of Clipboard.get_registered_formats");
SP -= items;
self = gimme_the_mate( ST( 0));
if ( self == nilHandle)
croak( "Illegal object reference passed to Clipboard.get_registered_formats");
list = formats;
EXTEND( sp, formatCount);
for ( i = 0; i < formatCount; i++)
PUSHs( sv_2mortal( newSVpv( list[ i]. id, 0)));
PUTBACK;
}
XS( Clipboard_get_standard_clipboards_FROMPERL)
{
dXSARGS;
int i;
PList l;
(void)ax; SP -= items;
l = apc_get_standard_clipboards();
if ( l && l-> count > 0) {
EXTEND( sp, l-> count);
for ( i = 0; i < l-> count; i++) {
char *cc = (char *)list_at( l, i);
PUSHs( sv_2mortal( newSVpv(cc, 0)));
}
}
if (l) {
list_delete_all( l, true);
plist_destroy( l);
}
PUTBACK;
}
void Clipboard_get_formats ( Handle self) { warn("Invalid call of Clipboard::get_formats"); }
void Clipboard_get_formats_REDEFINED ( Handle self) { warn("Invalid call of Clipboard::get_formats"); }
void Clipboard_get_registered_formats ( Handle self) { warn("Invalid call of Clipboard::get_registered_formats"); }
void Clipboard_get_registered_formats_REDEFINED ( Handle self) { warn("Invalid call of Clipboard::get_registered_formats"); }
void Clipboard_get_standard_clipboards ( Handle self) { warn("Invalid call of Clipboard::get_standard_clipboards"); }
void Clipboard_get_standard_clipboards_REDEFINED ( Handle self) { warn("Invalid call of Clipboard::get_standard_clipboards"); }
static SV *
text_server( Handle self, PClipboardFormatReg instance, int function, SV * data)
{
ClipboardDataRec c;
switch( function) {
case cefInit:
return ( SV *) cfText;
case cefFetch:
if ( apc_clipboard_get_data( self, cfText, &c)) {
data = newSVpv(( char*) c. data, c. length);
free( c. data);
return data;
}
break;
case cefStore:
if ( prima_is_utf8_sv( data)) {
/* jump to UTF8. close() will later downgrade data to ascii, if any */
instance = formats + cfUTF8;
return instance-> server( self, instance, cefStore, data);
} else {
c. data = ( Byte*) SvPV( data, c. length);
instance-> written = apc_clipboard_set_data( self, cfText, &c);
}
break;
}
return nilSV;
}
static SV *
utf8_server( Handle self, PClipboardFormatReg instance, int function, SV * data)
{
ClipboardDataRec c;
switch( function) {
case cefInit:
return ( SV *) cfUTF8;
case cefFetch:
if ( apc_clipboard_get_data( self, cfUTF8, &c)) {
data = newSVpv(( char*) c. data, c. length);
SvUTF8_on( data);
free( c. data);
return data;
}
break;
case cefStore:
c. data = ( Byte*) SvPV( data, c. length);
instance-> written = apc_clipboard_set_data( self, cfUTF8, &c);
break;
}
return nilSV;
}
static SV *
image_server( Handle self, PClipboardFormatReg instance, int function, SV * data)
{
ClipboardDataRec c;
switch( function) {
case cefInit:
return ( SV *) cfBitmap;
case cefFetch:
{
HV * profile = newHV();
c. image = Object_create( "Prima::Image", profile);
sv_free(( SV *) profile);
if ( apc_clipboard_get_data( self, cfBitmap, &c)) {
--SvREFCNT( SvRV( PImage(c. image)-> mate));
return newSVsv( PImage(c. image)-> mate);
}
Object_destroy( c. image);
}
break;
case cefStore:
c. image = gimme_the_mate( data);
if ( !kind_of( c. image, CImage)) {
warn("Not an image passed to clipboard");
return nilSV;
}
instance-> written = apc_clipboard_set_data( self, cfBitmap, &c);
break;
}
return nilSV;
}
static SV *
binary_server( Handle self, PClipboardFormatReg instance, int function, SV * data)
{
ClipboardDataRec c;
switch( function) {
case cefInit:
return ( SV*) apc_clipboard_register_format( self, instance-> id);
case cefDone:
apc_clipboard_deregister_format( self, instance-> sysId);
break;
case cefFetch:
if ( apc_clipboard_get_data( self, instance-> sysId, &c)) {
SV * ret = newSVpv((char*) c. data, c. length);
free( c. data);
return ret;
}
break;
case cefStore:
c. data = (Byte*) SvPV( data, c. length);
instance-> written = apc_clipboard_set_data( self, instance-> sysId, &c);
break;
}
return nilSV;
}
#ifdef __cplusplus
}
#endif