 /*
        Document.m

	Document class & WindowController : This file is part of Preview 

	Copyright (C) 2003;2004 Fabien VALLON 
	2003,2004 Alcove ( http://www.alcove.com ) 
	Additional copyrights here

	Authors : Fabien VALLON <fabien@sonappart.net>
	Date:	10 Oct 2003

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License as
	published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program 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.

	You should have received a copy of the GNU General Public
	License along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA
*/

// See Doumentation/DEVELOPERS

#include "Document.h"

#include <Foundation/NSNotification.h>

#include <AppKit/AppKit.h>
#include <AppKit/NSApplication.h>
#include <AppKit/NSAffineTransform.h>
#include <AppKit/NSClipView.h>
#include <AppKit/NSCursor.h>
#include <AppKit/NSImage.h>
#include <AppKit/NSImageView.h>
#include <AppKit/NSMatrix.h>
#include <AppKit/NSPasteboard.h>
#include <AppKit/NSPopUpButton.h>
#include <AppKit/NSScreen.h>
#include <AppKit/NSScrollView.h>
#include <AppKit/NSWindow.h>
#include <AppKit/NSWindowController.h>
#include <AppKit/NSScroller.h>

/*********************************************************************/
/**************** NSDocument Private methods *************************/
/*********************************************************************/

#define HEIGHT_HUNDRED_PER_CENT 0
#define FOUR_HUNDRED_PER_CENT   1
#define DOUBLE_SIZE             2
#define FULL_SIZE               3
#define HALF_SIZE               4
#define FIT_WINDOW              5
#define FIT_WIDTH               6

#define ZOOM_IN                 0
#define ZOOM_OUT                1

#define SCALEFACTOR            0.1



@interface CheckeredView: NSView
@end


@implementation CheckeredView 

- (void)drawRect:(NSRect)rect
{
  NSColor *backColor = [NSColor darkGrayColor];
  NSColor *color = [NSColor grayColor];

  [backColor set];
  NSRectFill(rect);
  [color set];
  int i, j;
  BOOL drawForeground = NO;
  for(i = 0; i < rect.size.width; i+=10)
    {
      drawForeground = i % 20 == 0;
      for(j = 0; j < rect.size.height; j+=10)
        {
	  if(drawForeground)
            {
	      NSRectFill(NSMakeRect(rect.origin.x+i, rect.origin.y+j, 10, 10));
            }
	  drawForeground = !drawForeground;
        }
    }

}

@end




@interface Document  (Private)
-(void) _setScaleFactor: (double) factor;
-(double) _scaleFactor;
-(void) _updateImage;
-(void) _setOriginalSize: (NSSize) originalSize;
-(NSSize) _originalSize;
-(void) _setCurrentItem:(unsigned) tag;
-(unsigned) _currentItem;
-(void) _notifyDragScroll: (id)notification;
-(void) _setIsAlpha:(BOOL) flag;
-(BOOL) _isAlpha;
@end

@implementation Document (Private)

-(void) _updateImage 
{
  if ([self _scaleFactor] != 1.0 )
    {
      NSSize imageSize;
      NSAffineTransform *affineTransform;

      affineTransform = [NSAffineTransform transform];
      [affineTransform scaleBy:[self _scaleFactor]];
      imageSize = [imageView frame].size;
      
      [imageView  setFrameSize:[affineTransform transformSize:imageSize]];
      if ( _isAlpha ) 
	{
	  [checkeredView  setFrameSize:
			    [affineTransform transformSize:imageSize]];
	}
      
      [imageView setNeedsDisplay:YES];

    }
}

/*
 * Set the scale factor (zoom).
 */
- (void) _setScaleFactor: (double)factor
{
  _scaleFactor = factor;
}

- (double) _scaleFactor
{
   return _scaleFactor;
}

-(void) _setOriginalSize: (NSSize) orginalSize
{
  _originalSize = orginalSize;
}

-(NSSize) _originalSize
{
  return _originalSize;
}

-(void) _setCurrentItem:(unsigned) tag
{
  if (tag <= FIT_WIDTH)
    _tag = tag;
}

-(unsigned) _currentItem
{
  return _tag;
}

- (void) _notifyDragScroll: (id)notification
{
   NSPoint  newOrigin;
   NSSize   scrollAmount;
   NSSize   contentSize;
   NSRect   vRect;
   
   scrollAmount =
     [[[notification userInfo] objectForKey: @"UserInfoKeyScrollAmount"] sizeValue];

   vRect       = [scrollView documentVisibleRect];
   contentSize = [scrollView contentSize];

   newOrigin = NSMakePoint(vRect.origin.x + scrollAmount.width,
                           vRect.origin.y + scrollAmount.height);


   [[scrollView contentView] scrollToPoint:
			        [[scrollView contentView] constrainScrollPoint:newOrigin]];
}


-(void) _setIsAlpha:(BOOL) flag
{
  _isAlpha = flag;
}

-(BOOL) _isAlpha
{
  return _isAlpha;
}



@end


/************************************************************/
/*********** NSDocument subclass methods ********************/
/************************************************************/

@implementation Document

/**
 * NSDocument subclass method
 * return the nib (Preview) 
 */
- (NSString *) windowNibName
{
  return @"Preview";
}

/**
 * NSDocument subclass method
 * 1- set the window frame: 
 * window origin is set to (120,100) == (WINDOW_ORIGIN_X,WINDOW_ORIGIN_Y)
 * the windowSize have a Minsize (set in Preview.gorm)
 * the window size is not bigger than the NSScreen:visibleFrame (-origin)
 *
 * 2- set _image into imageView
 */

- (void) windowControllerDidLoadNib:(NSWindowController *)windowController
{
  // NSSize windowSize;

  window = [windowController window];
  
  if ( ( ! window ) || (!_image )  )
    return;

  {
    BOOL bigger = NO;
    NSSize imageSize = [_image size];
    NSSize contentSize;
    NSSize screenSize = [[NSScreen mainScreen] frame].size;
    screenSize.width -= 100 + 64;
    screenSize.height -= 120;

    if ( screenSize.width > imageSize.width  + [[scrollView verticalScroller] frame].size.width )
      contentSize.width = imageSize.width  + [[scrollView verticalScroller] frame].size.width;
    else
      {
	contentSize.width = screenSize.width;
	bigger = YES;
      }
    
    if ( screenSize.height > imageSize.height + [[scrollView horizontalScroller] frame].size.height ) 
      contentSize.height = imageSize.height +  [[scrollView horizontalScroller] frame].size.height; 
    else
      {
	contentSize.height = screenSize.height;
	bigger = YES;
      }
    
    if ( [[_image  bestRepresentationForDevice:nil]  hasAlpha] ) 
      {
	[self _setIsAlpha: YES];
	checkeredView = [[CheckeredView alloc] initWithFrame: NSMakeRect(0,0,imageSize.width,imageSize.height)];
	[imageView retain];
	[scrollView setDocumentView: checkeredView];
 	[checkeredView addSubview:imageView ];
      }
    else 
      {
	[self _setIsAlpha: NO];
      }

    
    [imageView setFrame:NSMakeRect(0,0,imageSize.width,imageSize.height)];
    [imageView setImage:_image];
    
    int test = [imageView addTrackingRect:[imageView bounds]
			  owner:imageView 
			  userData:nil
			  assumeInside:YES];

    [window setContentSize:contentSize];
    [window setFrameOrigin: NSMakePoint(100,120)];
  }

  {
    [[NSNotificationCenter defaultCenter] addObserver: self
					  selector: @selector(_notifyDragScroll:)
					  name: @"TEST"
					  object: imageView];

    [[NSNotificationCenter defaultCenter] addObserver: self
					  selector: @selector(_notifyMouseDown:)
					  name: @"MOUSEDOWN"
					  object: nil];

    [[NSNotificationCenter defaultCenter] addObserver: self
					  selector: @selector(_notifyMouseUp:)
					  name: @"MOUSEUP"
					  object: nil];

  }


  //Registering Objects for Services
  {
    [NSApp registerServicesMenuSendTypes:[[NSArray alloc] initWithObjects:NSFilenamesPboardType,nil ]
	   returnTypes:nil];
  }
  
}


/**
 * NSDocument subclass method
 * This will create a new document. 
 * if it the _image is succesfully init it set the size.
 */
- (BOOL) loadDataRepresentation: (NSData*)data ofType: (NSString*)docType
{
  _image = [[[NSImage alloc] initWithData: data] autorelease];
  
  if (! _image )
    return NO;

  [self _setOriginalSize: [_image size]];
  return YES;
}

/**
 * NSDocument subclass method
 */
- (NSData *)dataRepresentationOfType:(NSString *)aType 
{
  Class imageRepClass = [NSImageRep imageRepClassForFileType:aType];

  if ( ! imageRepClass )
    {
      return nil;
    }
    
  return [_image TIFFRepresentation];
}

/**
 * NSDocument subclass method
 */
- (NSString *)fileType
{
  return @"tiff";
}


- (id)validRequestorForSendType:(NSString *)sendType
		     returnType:(NSString *)returnType
{
  return self;
}


- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteBoard
			     types:(NSArray *)types
{
  NSArray *pbTypeArray = [[NSArray alloc] initWithObjects:NSStringPboardType,NSFilenamesPboardType,NSTIFFPboardType,nil];
  BOOL ok = NO;
  NSData *tiffRep = [_image TIFFRepresentation];

  [pasteBoard declareTypes:pbTypeArray owner:nil];

  if ( [types containsObject : NSStringPboardType] ) 
    {
       if ( [pasteBoard setString: [super fileName]
			forType: NSStringPboardType] ) 
 	ok = YES;
    }
  if ( [types containsObject : NSFilenamesPboardType] ) 
    {
      if ( [pasteBoard setPropertyList: [NSArray arrayWithObject:[super fileName]]
		       forType: NSFilenamesPboardType] ) 
	ok = YES;
    }

  if ( [types containsObject : NSTIFFPboardType] ) 
    {
      if ([pasteBoard setData: tiffRep forType: NSTIFFPboardType] )
	ok = YES;
    }

  return ok;


}

/**
 *
 * This method copy NSTIFFPboardType, NSFilenamesPboardType or NSStringPboardType
 * into the general Pasteboard
 */
- (void) copy: (id) sender 
{  
  NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
  NSData *tiffRep = [_image TIFFRepresentation];

  NSArray *pbTypeArray = [[NSArray alloc] initWithObjects:NSStringPboardType,NSFilenamesPboardType,NSTIFFPboardType,nil];
  
  [pasteBoard declareTypes: pbTypeArray owner:self];


  if ( ! [pasteBoard setPropertyList: [[NSArray alloc] initWithObjects:[super fileName],nil]
		   forType: NSStringPboardType] ) 
    NSLog(@"Problem : cannot copy NSStringPboardType");


  if (! [pasteBoard setPropertyList: [NSArray arrayWithObject:[super fileName]]
		   forType: NSFilenamesPboardType] ) 
    NSLog(@"Problem : cannot copy NSFilenamesPboardType");
  
  if (! [pasteBoard setData: tiffRep forType: NSTIFFPboardType] )
    NSLog(@"Problem : cannot copy NSTIFFPboardType");
}


/**
 * Action method
 *
 *
 *
 */
-(void) resize: (id) sender
{
  unsigned tag;
  NSSize newSize;

  //Get Tag (sender comes from popUp or matrix or menu
  {
    if ( sender == popUp ) 
      tag = [sender indexOfSelectedItem]; //popUp
    else if ( sender == matrix ) 
      {
	tag = [[sender selectedCell] tag]; //matrix
      }
    else 
      tag = [sender tag]; //menu
  }
  

  //Deselect matrix cells if popPup is selected
  {
    if (tag <= HALF_SIZE) 
      {
	if (  [matrix selectedCell] )
	  [matrix deselectAllCells];
	
	[imageView setAutoresizingMask: NSViewNotSizable];
      }
  }

  switch (tag)
    {
    case HEIGHT_HUNDRED_PER_CENT: 
      newSize.width = [self _originalSize].width * 8;
      newSize.height = [self _originalSize].height * 8;
      break;
    case FOUR_HUNDRED_PER_CENT: 
      newSize.width = [self _originalSize].width * 4;
      newSize.height = [self _originalSize].height * 4;
      break;
    case DOUBLE_SIZE:
      newSize.width = [self _originalSize].width * 2;
      newSize.height = [self _originalSize].height * 2;
      break;
    case FULL_SIZE: 
      newSize.width = [self _originalSize].width ;
      newSize.height = [self _originalSize].height;
      break;
   case HALF_SIZE:
      newSize.width = [self _originalSize].width / 2;
      newSize.height = [self _originalSize].height / 2;
      break;
   case FIT_WINDOW:
     newSize.width = [scrollView contentSize].width;
     newSize.height = [scrollView contentSize].height;
     [imageView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
     if ( [self _isAlpha] ) 
	{
	  NSLog(@"isAlpha");
	  [checkeredView setAutoresizingMask: 
			   (NSViewWidthSizable|NSViewHeightSizable)];
	}
     break;
   case FIT_WIDTH:
      newSize.width = [scrollView contentSize].width;
      newSize.height = [imageView frame].size.height;
      [imageView setAutoresizingMask: (NSViewWidthSizable)];
      if ( [self _isAlpha] ) 
	[checkeredView setAutoresizingMask: NSViewWidthSizable];
      break; 
    default: 
      printf("problem resize default \n");
      return;
    }

  //set autoresizing Mask
  if ( tag < FIT_WINDOW ) 
    {
      if ( [self  _isAlpha] ) 
	[checkeredView setAutoresizingMask: NSViewNotSizable];

      [imageView setAutoresizingMask: NSViewNotSizable];
    }

  
  //Resize
  if ( [self _isAlpha] ) 
    {
      [checkeredView setFrame:NSMakeRect(0,0,newSize.width,newSize.height) ];
    }
  
  NSLog(@"apres newSize %@",NSStringFromSize(newSize));

  [imageView setFrame: NSMakeRect(0,0,newSize.width,newSize.height)];
    
  // why TODO FIXME !!! only need with FIT_WINDOW 
  [imageView setNeedsDisplay:YES];
  [checkeredView setNeedsDisplay:YES];
  [self _setCurrentItem: tag];
}

-(void) zoomImage : (id) sender
{
  unsigned tag;
  tag = [sender tag];

  if ( tag == ZOOM_IN ) 
    {
      [self _setScaleFactor: (1 + SCALEFACTOR)];
      [self _updateImage];
    }
  else if ( tag == ZOOM_OUT ) 
    {
      [self _setScaleFactor: (1 -SCALEFACTOR)];
      [self _updateImage];
    }
  else 
    {
      NSLog(@"zoomImage tag: %i",tag);
    }
}

/**
 * window delegate method.
 * The mini icon is generate from _image
 */
- (void)windowDidMiniaturize:(NSNotification *)aNotification
{
  NSImage *miniImage = _image;
  [miniImage setSize: NSMakeSize(48,48)];
  [window setMiniwindowImage:miniImage];
}

/**
 * window delegate method.
 * This method is used to refresh the horizontalScroller width
 */
- (void)windowDidResize:(NSNotification *)aNotification
{
  NSRect scrollerRect =   [[scrollView horizontalScroller] frame];

  scrollerRect.size.width = [window frame].size.width - 135;
  [[scrollView horizontalScroller] setFrame:scrollerRect];
}


//Validate Menu : 
- (BOOL) validateMenuItem: (id)menuItem
{
  SEL action = [menuItem action];

  if ( sel_isEqual(action,@selector(resize:)) )
    {
      if ( [menuItem tag] == [self _currentItem] ) 
	return NO;	
    }

  return YES;
}


@end

