
#import <appkit/Menu.h>
#import <appkit/Text.h>
#import <appkit/Window.h>
#import <appkit/View.h>
#import <appkit/ScrollView.h>
#import <appkit/Pasteboard.h>
#import <appkit/Application.h>
#import <appkit/Listener.h>
#import <appkit/Matrix.h>
#import <appkit/Panel.h>
#import <appkit/PopUpList.h>
#import <appkit/NXImage.h>
#import <appkit/NXBrowser.h>
#import <appkit/NXBrowserCell.h>
#import <appkit/Button.h>
#import <objc/List.h>
#import <appkit/NXEPSImageRep.h>

#import <soundkit/SoundView.h>
#import <soundkit/Sound.h>

#import <strings.h>

#import "PasteboardController.h"
#import "GCell.h"
#import "PSView.h"

#define ascii 0
#define rtf 1
#define ps 2
#define tiff 3
#define snd 4
#define	fname 5
#define tabtext 6
#define font 7
#define ruler 8
#define nothing 9
#define unknown 10

@implementation PasteboardController

const char *titles[] = {"NXAsciiPboard", "NXRTFPboard", "NXPostScriptPboard", "NXTIFFPboard", "NXSoundPboard", "NXFilenamePboard", "NXTabularTextPboard", "NXFontPboard", "NXRulerPboard", "Nothing", "Unknown"};

- appDidInit: sender
{
		
	[Text registerDirective:"attach" forClass: [GCell class]];
	list = [menu target];
	text = [textScroll docView];
	rtfText = [rtfScroll docView];
	tabText = [tabScroll docView];
	[list setAutoupdate: YES];
	unknownText = [unknownScroll docView];
		
	[self setupText: fontText];
	[fontText setText:"NXFontPboardType"];
	
	[self setupText: fnameText];
	
	[self setupText: rulerText];
	[rulerText toggleRuler: self];
	[rulerText setText: "NXRulerPBoardType"];
	
	[[NXApp appListener] setServicesDelegate: self];
	[browser setMinColumnWidth:0];
	[browser setMaxVisibleColumns:1];
	pasteboardList = [[List alloc] init];
	[pasteboardList addObject:[Pasteboard newName:NXSelectionPboard]];
	[pasteboardList addObject:[Pasteboard newName:NXFontPboard]];
	[pasteboardList addObject:[Pasteboard newName:NXRulerPboard]];
	[pasteboardList addObject:[Pasteboard newName:NXFindPboard]];
	[browser setAction: @selector(setPasteboard:)];
	[browser setDoubleAction: @selector(setPasteboard:)];
	viewList = [[List alloc] init];
	[viewList addObject: textView];
	[viewList addObject: rtfView];
	[viewList addObject: epsView];
	[viewList addObject: graphicView];
	[viewList addObject: soundView];
	[viewList addObject: fileView];
	[viewList addObject: tabView];
	[viewList addObject: fontView];
	[viewList addObject: rulerView];
	[viewList addObject: nothingView];
	[viewList addObject: unknownView];
	[browser loadColumnZero];
	[[browser matrixInColumn: 0] selectCellAt: 0 : 0];
	[self getPasteboard:[pasteboardList objectAt:0]];
	
	return self;
}

- setPasteboard: sender
{
		[self getPasteboard:[pasteboardList objectAt:[[browser matrixInColumn: 0] selectedRow]]];
		
		return self;
}

- (int)browser: sender fillMatrix: matrix inColumn: (int)column
{
	int i, row, col;
	
	for(i = 0; i < [pasteboardList count]; i++)
	{
		[matrix addRow];
		[[matrix cellAt: i : 0] setStringValue:[[pasteboardList objectAt:i] name]];
		[[matrix cellAt: i : 0] setLeaf: YES];
		[[matrix cellAt: i : 0] setLoaded: YES];
	}
	
	[matrix getNumRows:&row numCols:&col];
	return row;
}

- windowWillResize: sender toSize: (NXSize *) frameSize
{
	if(frameSize->width <= 268)
		frameSize->width = 268;
	if(frameSize->height <= 400)
		frameSize->height = 400;
		
	return self;
}
		
- getPasteboard: (id) pasteboard userData: (const char *)userData error: (char **) msg
{
	NXRect r;
	
	[browser getFrame:&r];
	[[pasteboardWindow contentView] replaceSubview: browser with: examineView];
	[examineView setFrame:&r];
	[examineView display];
	[pasteboardWindow flushWindow];
	[self getPasteboard:pasteboard];
	
	return self;
}

- setupText: txt
{
	NXSize aSize = {1.0E38,1.0E38};
	NXRect r;

	[[txt superview] getFrame: &r];
	[txt setMonoFont: NO];
	[txt setVertResizable:YES];
	[txt setHorizResizable:YES];
	[txt setOpaque:YES];
	[txt setMinSize:&r.size];
	[txt setMaxSize:&aSize];
	
	return self;
}

- switchBoxes:sender
{
	NXRect r;
	
	[curView getFrame: &r];
	[[pasteboardWindow contentView] replaceSubview: curView
									with:[viewList objectAt:[[list itemList] selectedTag]]];
	curView = [viewList objectAt:[[list itemList] selectedTag]];
	[pasteboardWindow setTitle: titles[[[list itemList] selectedTag]]];
	[curView setFrame: &r];	

	if([[list itemList] selectedTag] == unknown)
	{
		NXStream *stream;
		char *data;
		int len;
		
		if(![self 	check:[[pasteboardList objectAt:[[browser matrixInColumn:0] selectedRow]] readType:[[[list itemList] selectedCell] title]
					data:&data
					length: &len]])
				return self;
		stream = NXOpenMemory(data, len, NX_READONLY);
		NXSeek(stream, 0, NX_FROMSTART);
		[unknownText readText: stream];
		NXClose(stream);
	}
	[[pasteboardWindow contentView] display];
	[pasteboardWindow flushWindow];
    return self;
}


- getPasteboard:pb
{
	char *const *s;
    char *const *types;
	int i;
	id cell;
	int ct = 0;
	
	for(i = 0; i < [list count]; i++)
	{
		[list removeItemAt:0];
	}
	
	[menu setTitle:titles[nothing]];
	
	types = [pb types];
	
	for(s = types; *s; s++)
	{
		if (!strcmp(*s, NXAsciiPboardType))
		{
			[self addMenuItem: ascii : pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXRTFPboardType))
		{
			[self addMenuItem: rtf: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXPostScriptPboardType))
		{
			[self addMenuItem: ps: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXTIFFPboardType))
		{
			[self addMenuItem: tiff: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXSoundPboardType))
		{
			[self addMenuItem: snd: pb];
			ct++;
		}
		else
		if (!strcmp(*s,  NXFilenamePboardType))
		{
			[self addMenuItem: fname: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXTabularTextPboardType))
		{
			[self addMenuItem: tabtext: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXFontPboardType))
		{
			[self addMenuItem: font: pb];
			ct++;
		}
		else
		if (!strcmp(*s, NXRulerPboardType))
		{
			[self addMenuItem: ruler: pb];
			ct++;
		}
		else
		if(NX_NOT_IN_LIST != [pasteboardList indexOf:pb])
		{
			cell = [list addItem: *s action: @selector(switchBoxes:) keyEquivalent: 0];
			[cell setTag: unknown];
			[menu setTitle:*s];
			[[list itemList] selectCellWithTag:unknown];
			ct++;
		}
	}
				
	if(ct == 0)
	{
		cell = [list addItem: titles[nothing] action: @selector(switchBoxes:) keyEquivalent: 0];
		[cell setTag: nothing];
		[menu setTitle:titles[nothing]];
		[[list itemList] selectCellWithTag:nothing];
	}
	
	[self switchBoxes: self];
		
    return self;
}

- addMenuItem: (int) type : pb
{
	id cell;
    char *data;
    int len;
	NXStream *stream;
	NXRect r;
	[curView getFrame:&r];
	
	if([list indexOfItem:titles[type]] == -1)
	{
		cell = [list addItem: titles[type] action: @selector(switchBoxes:) keyEquivalent: 0];
		[cell setTag: type];
		[menu setTitle:titles[type]];
		[[list itemList] selectCellWithTag:type];
	}

	switch([[list itemList] selectedTag])
	{
		case ascii :
			if(![self check:[pb 	readType:NXAsciiPboardType
						data:&data
						length: &len]])
				return self;
			data[len] = '\0';
			[textView setFrame:&r];
			[text setText: data];
			break;
		case rtf :
			if(![self check:[pb 	readType:NXRTFPboardType
						data:&data
						length: &len]])
				return self;
			data[len] = '\0';
			stream = NXOpenMemory(data, len, NX_READONLY);
			[rtfView setFrame:&r];
			[rtfText readRichText: stream];
			break;
		case ps : 
 			if(![self check:[pb 	readType:NXPostScriptPboardType
						data:&data
						length: &len]])
				return self;
			stream = NXOpenMemory(data, len, NX_READONLY);
			[epsView setFrame:&r];
			[eps setImage: [[NXImage alloc] initFromStream: stream]];
			break;
		case tiff : 
 			if(![self check:[pb 	readType:NXTIFFPboardType
						data:&data
						length: &len]])
				return self;
			stream = NXOpenMemory(data, len, NX_READONLY);
			[graphicView setFrame:&r];
			[graphic setImage: [[NXImage alloc] initFromStream: stream]];
			NXClose(stream);
			break;
		case snd : 
			[soundView setFrame:&r];
			[sound	setSound: [Sound newFromPasteboard]];
			break;
		case fname :
		{
			char *tab;
 			if(![self check:[pb 	readType:NXFilenamePboardType
						data:&data
						length: &len]])
				return self;
			[fileView setFrame:&r];
			while(data)
			{
				tab = strchr(data, '\t');
				if (tab) *tab = '\0';
				[fnameText 	setSel: [fnameText textLength]
							: [fnameText textLength]];
				[fnameText replaceSelWithCell: [[GCell alloc] initFromFile: data]];

	   			data = tab ? ++tab : NULL;
			}
			break;
		}
		case tabtext :
			if(![self check:[pb 	readType:NXTabularTextPboardType
						data:&data
						length: &len]])
				return self;
			data[len] = '\0';
			[tabView setFrame:&r];
			[tabText setText: data];
			break;
		case font :
			[fontView setFrame:&r];
			[fontText setSel: 0 : [fontText textLength]];
			[fontText pasteFont: self];
			break;
		case ruler :
			[rulerView setFrame:&r];
			[rulerText setSel: 0 : [rulerText textLength]];
			[rulerText pasteRuler: self];
			break;
		case nothing :
		default :
			break;
	}
		
	return self;
}

- (int)check: pb
{
	if(!pb)
	{
		if(NXRunAlertPanel(NULL, "The type you requested is not currently on the pasteboard. Would you like to update the pasteboard menu?", "Yes", "No", NULL) == NX_ALERTDEFAULT)
		{
			[self getPasteboard:[pasteboardList objectAt:[[browser matrixInColumn: 0] selectedRow]]];
		}
		return NO;
	}
	else
		return YES;
}

- done: sender
{
	[self perform:@selector(done) with:self afterDelay:5 cancelPrevious:YES];
	return self;
}

- done
{
	NXRect r;
	[examineView getFrame:&r];
	[[pasteboardWindow contentView] replaceSubview: examineView with: browser];
	[browser setFrame:&r];
	[browser display];
	[pasteboardWindow flushWindow];
	[self getPasteboard:[pasteboardList objectAt:[[browser matrixInColumn: 0] selectedRow]]];
	
	return self;
}

- putBack: sender
{
    char *data;
    NXStream *stream;   
    const char *types[1];
    int length, maxlen;
	id pboard = [Pasteboard new];
	
	switch([[list itemList] selectedTag])
	{
		case ascii :
			[text selectAll: self];
			[text copy: self];
			break;
		case rtf :
			[rtfText selectAll: self];
			[rtfText copy: self];
			break;
		case ps :
		{
			id image = [eps getImage];
			id rep = [image bestRepresentation];
			[rep getEPS: &data length:&length];
			types[0] = NXPostScriptPboardType;
			[pboard declareTypes:types num:1 owner:[self class]];
			[pboard writeType:types[0] data:data length:length];
			break;
		}
		case tiff :
		{
			id image = [graphic getImage];
			id rep = [image bestRepresentation];
			stream = NXOpenMemory(NULL, 0, NX_READWRITE);
			[rep writeTIFF:stream];
			NXGetMemoryBuffer(stream, &data, &length, &maxlen);
			types[0] = NXTIFFPboardType;
			[pboard declareTypes:types num:1 owner:[self class]];
			[pboard writeType:types[0] data:data length:length];
			NXClose(stream);			
			break;
		}
		case snd :
			[sound selectAll:self];
			[sound copy:self];
			break;
		case fname :
			types[0] = NXFilenamePboardType;
			[pboard declareTypes:types num:1 owner:[self class]];
			break;
		case tabtext :
			types[0] = NXTabularTextPboardType;
			stream = NXOpenMemory(NULL, 0, NX_READWRITE);
			[tabText writeText: stream];
			NXSeek(stream, 0, NX_FROMSTART);
			NXGetMemoryBuffer(stream, &data, &length, &maxlen);
			[pboard writeType:types[0] data:data length:length];
			NXClose(stream);			
			break;
		case font :
			[fontText selectAll: self];
			[fontText copyFont:self];
			break;
		case ruler :
			[rulerText selectAll: self];
			[rulerText copyRuler:self];
			break;
		case nothing :
			break;
		case unknown :
			types[0] = [[[list itemList] selectedCell] title];
			[pboard declareTypes:types num:1 owner:[self class]];
			stream = NXOpenMemory(NULL, 0, NX_READWRITE);
			[unknownText writeText: stream];
			NXSeek(stream, 0, NX_FROMSTART);
			NXGetMemoryBuffer(stream, &data, &length, &maxlen);
			[pboard writeType:types[0] data:data length:length];
			NXClose(stream);			
			break;
		default:
			break;
	}
	
	return self;
}

@end
