1 /*
2        Document.m
3
4	Document class & WindowController : This file is part of Preview
5
6	Copyright (C) 2003;2004 Fabien VALLON
7	2003,2004 Alcove ( http://www.alcove.com )
8	Additional copyrights here
9
10	Authors : Fabien VALLON <fabien@sonappart.net>
11	Date:	10 Oct 2003
12
13	This program is free software; you can redistribute it and/or
14	modify it under the terms of the GNU General Public License as
15	published by the Free Software Foundation; either version 2 of
16	the License, or (at your option) any later version.
17
18	This program is distributed in the hope that it will be useful,
19	but WITHOUT ANY WARRANTY; without even the implied warranty of
20	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21
22	See the GNU General Public License for more details.
23
24	You should have received a copy of the GNU General Public
25	License along with this program; if not, write to:
26
27		Free Software Foundation, Inc.
28		59 Temple Place - Suite 330
29		Boston, MA  02111-1307, USA
30*/
31
32// See Doumentation/DEVELOPERS
33
34#include "Document.h"
35
36#include <Foundation/NSNotification.h>
37
38#include <AppKit/AppKit.h>
39#include <AppKit/NSApplication.h>
40#include <AppKit/NSAffineTransform.h>
41#include <AppKit/NSClipView.h>
42#include <AppKit/NSCursor.h>
43#include <AppKit/NSImage.h>
44#include <AppKit/NSImageView.h>
45#include <AppKit/NSMatrix.h>
46#include <AppKit/NSPasteboard.h>
47#include <AppKit/NSPopUpButton.h>
48#include <AppKit/NSScreen.h>
49#include <AppKit/NSScrollView.h>
50#include <AppKit/NSWindow.h>
51#include <AppKit/NSWindowController.h>
52#include <AppKit/NSScroller.h>
53
54/*********************************************************************/
55/**************** NSDocument Private methods *************************/
56/*********************************************************************/
57
58#define HEIGHT_HUNDRED_PER_CENT 0
59#define FOUR_HUNDRED_PER_CENT   1
60#define DOUBLE_SIZE             2
61#define FULL_SIZE               3
62#define HALF_SIZE               4
63#define FIT_WINDOW              5
64#define FIT_WIDTH               6
65
66#define ZOOM_IN                 0
67#define ZOOM_OUT                1
68
69#define SCALEFACTOR            0.1
70
71
72
73@interface CheckeredView: NSView
74@end
75
76
77@implementation CheckeredView
78
79- (void)drawRect:(NSRect)rect
80{
81  NSColor *backColor = [NSColor darkGrayColor];
82  NSColor *color = [NSColor grayColor];
83
84  [backColor set];
85  NSRectFill(rect);
86  [color set];
87  int i, j;
88  BOOL drawForeground = NO;
89  for(i = 0; i < rect.size.width; i+=10)
90    {
91      drawForeground = i % 20 == 0;
92      for(j = 0; j < rect.size.height; j+=10)
93        {
94	  if(drawForeground)
95            {
96	      NSRectFill(NSMakeRect(rect.origin.x+i, rect.origin.y+j, 10, 10));
97            }
98	  drawForeground = !drawForeground;
99        }
100    }
101
102}
103
104@end
105
106
107
108
109@interface Document  (Private)
110-(void) _setScaleFactor: (double) factor;
111-(double) _scaleFactor;
112-(void) _updateImage;
113-(void) _setOriginalSize: (NSSize) originalSize;
114-(NSSize) _originalSize;
115-(void) _setCurrentItem:(unsigned) tag;
116-(unsigned) _currentItem;
117-(void) _notifyDragScroll: (id)notification;
118-(void) _setIsAlpha:(BOOL) flag;
119-(BOOL) _isAlpha;
120@end
121
122@implementation Document (Private)
123
124-(void) _updateImage
125{
126  if ([self _scaleFactor] != 1.0 )
127    {
128      NSSize imageSize;
129      NSAffineTransform *affineTransform;
130
131      affineTransform = [NSAffineTransform transform];
132      [affineTransform scaleBy:[self _scaleFactor]];
133      imageSize = [imageView frame].size;
134
135      [imageView  setFrameSize:[affineTransform transformSize:imageSize]];
136      if ( _isAlpha )
137	{
138	  [checkeredView  setFrameSize:
139			    [affineTransform transformSize:imageSize]];
140	}
141
142      [imageView setNeedsDisplay:YES];
143
144    }
145}
146
147/*
148 * Set the scale factor (zoom).
149 */
150- (void) _setScaleFactor: (double)factor
151{
152  _scaleFactor = factor;
153}
154
155- (double) _scaleFactor
156{
157   return _scaleFactor;
158}
159
160-(void) _setOriginalSize: (NSSize) orginalSize
161{
162  _originalSize = orginalSize;
163}
164
165-(NSSize) _originalSize
166{
167  return _originalSize;
168}
169
170-(void) _setCurrentItem:(unsigned) tag
171{
172  if (tag <= FIT_WIDTH)
173    _tag = tag;
174}
175
176-(unsigned) _currentItem
177{
178  return _tag;
179}
180
181- (void) _notifyDragScroll: (id)notification
182{
183   NSPoint  newOrigin;
184   NSSize   scrollAmount;
185   NSSize   contentSize;
186   NSRect   vRect;
187
188   scrollAmount =
189     [[[notification userInfo] objectForKey: @"UserInfoKeyScrollAmount"] sizeValue];
190
191   vRect       = [scrollView documentVisibleRect];
192   contentSize = [scrollView contentSize];
193
194   newOrigin = NSMakePoint(vRect.origin.x + scrollAmount.width,
195                           vRect.origin.y + scrollAmount.height);
196
197
198   [[scrollView contentView] scrollToPoint:
199			        [[scrollView contentView] constrainScrollPoint:newOrigin]];
200}
201
202
203-(void) _setIsAlpha:(BOOL) flag
204{
205  _isAlpha = flag;
206}
207
208-(BOOL) _isAlpha
209{
210  return _isAlpha;
211}
212
213
214
215@end
216
217
218/************************************************************/
219/*********** NSDocument subclass methods ********************/
220/************************************************************/
221
222@implementation Document
223
224/**
225 * NSDocument subclass method
226 * return the nib (Preview)
227 */
228- (NSString *) windowNibName
229{
230  return @"Preview";
231}
232
233/**
234 * NSDocument subclass method
235 * 1- set the window frame:
236 * window origin is set to (120,100) == (WINDOW_ORIGIN_X,WINDOW_ORIGIN_Y)
237 * the windowSize have a Minsize (set in Preview.gorm)
238 * the window size is not bigger than the NSScreen:visibleFrame (-origin)
239 *
240 * 2- set _image into imageView
241 */
242
243- (void) windowControllerDidLoadNib:(NSWindowController *)windowController
244{
245  // NSSize windowSize;
246
247  window = [windowController window];
248
249  if ( ( ! window ) || (!_image )  )
250    return;
251
252  {
253    BOOL bigger = NO;
254    NSSize imageSize = [_image size];
255    NSSize contentSize;
256    NSSize screenSize = [[NSScreen mainScreen] frame].size;
257    screenSize.width -= 100 + 64;
258    screenSize.height -= 120;
259
260    if ( screenSize.width > imageSize.width  + [[scrollView verticalScroller] frame].size.width )
261      contentSize.width = imageSize.width  + [[scrollView verticalScroller] frame].size.width;
262    else
263      {
264	contentSize.width = screenSize.width;
265	bigger = YES;
266      }
267
268    if ( screenSize.height > imageSize.height + [[scrollView horizontalScroller] frame].size.height )
269      contentSize.height = imageSize.height +  [[scrollView horizontalScroller] frame].size.height;
270    else
271      {
272	contentSize.height = screenSize.height;
273	bigger = YES;
274      }
275
276    if ( [[_image  bestRepresentationForDevice:nil]  hasAlpha] )
277      {
278	[self _setIsAlpha: YES];
279	checkeredView = [[CheckeredView alloc] initWithFrame: NSMakeRect(0,0,imageSize.width,imageSize.height)];
280	[imageView retain];
281	[scrollView setDocumentView: checkeredView];
282 	[checkeredView addSubview:imageView ];
283      }
284    else
285      {
286	[self _setIsAlpha: NO];
287      }
288
289
290    [imageView setFrame:NSMakeRect(0,0,imageSize.width,imageSize.height)];
291    [imageView setImage:_image];
292
293    int test = [imageView addTrackingRect:[imageView bounds]
294			  owner:imageView
295			  userData:nil
296			  assumeInside:YES];
297
298    [window setContentSize:contentSize];
299    [window setFrameOrigin: NSMakePoint(100,120)];
300  }
301
302  {
303    [[NSNotificationCenter defaultCenter] addObserver: self
304					  selector: @selector(_notifyDragScroll:)
305					  name: @"TEST"
306					  object: imageView];
307
308    [[NSNotificationCenter defaultCenter] addObserver: self
309					  selector: @selector(_notifyMouseDown:)
310					  name: @"MOUSEDOWN"
311					  object: nil];
312
313    [[NSNotificationCenter defaultCenter] addObserver: self
314					  selector: @selector(_notifyMouseUp:)
315					  name: @"MOUSEUP"
316					  object: nil];
317
318  }
319
320
321  //Registering Objects for Services
322  {
323    [NSApp registerServicesMenuSendTypes:[[NSArray alloc] initWithObjects:NSFilenamesPboardType,nil ]
324	   returnTypes:nil];
325  }
326
327}
328
329
330/**
331 * NSDocument subclass method
332 * This will create a new document.
333 * if it the _image is succesfully init it set the size.
334 */
335- (BOOL) loadDataRepresentation: (NSData*)data ofType: (NSString*)docType
336{
337  _image = [[[NSImage alloc] initWithData: data] autorelease];
338
339  if (! _image )
340    return NO;
341
342  [self _setOriginalSize: [_image size]];
343  return YES;
344}
345
346/**
347 * NSDocument subclass method
348 */
349- (NSData *)dataRepresentationOfType:(NSString *)aType
350{
351  Class imageRepClass = [NSImageRep imageRepClassForFileType:aType];
352
353  if ( ! imageRepClass )
354    {
355      return nil;
356    }
357
358  return [_image TIFFRepresentation];
359}
360
361/**
362 * NSDocument subclass method
363 */
364- (NSString *)fileType
365{
366  return @"tiff";
367}
368
369
370- (id)validRequestorForSendType:(NSString *)sendType
371		     returnType:(NSString *)returnType
372{
373  return self;
374}
375
376
377- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteBoard
378			     types:(NSArray *)types
379{
380  NSArray *pbTypeArray = [[NSArray alloc] initWithObjects:NSStringPboardType,NSFilenamesPboardType,NSTIFFPboardType,nil];
381  BOOL ok = NO;
382  NSData *tiffRep = [_image TIFFRepresentation];
383
384  [pasteBoard declareTypes:pbTypeArray owner:nil];
385
386  if ( [types containsObject : NSStringPboardType] )
387    {
388       if ( [pasteBoard setString: [super fileName]
389			forType: NSStringPboardType] )
390 	ok = YES;
391    }
392  if ( [types containsObject : NSFilenamesPboardType] )
393    {
394      if ( [pasteBoard setPropertyList: [NSArray arrayWithObject:[super fileName]]
395		       forType: NSFilenamesPboardType] )
396	ok = YES;
397    }
398
399  if ( [types containsObject : NSTIFFPboardType] )
400    {
401      if ([pasteBoard setData: tiffRep forType: NSTIFFPboardType] )
402	ok = YES;
403    }
404
405  return ok;
406
407
408}
409
410/**
411 *
412 * This method copy NSTIFFPboardType, NSFilenamesPboardType or NSStringPboardType
413 * into the general Pasteboard
414 */
415- (void) copy: (id) sender
416{
417  NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
418  NSData *tiffRep = [_image TIFFRepresentation];
419
420  NSArray *pbTypeArray = [[NSArray alloc] initWithObjects:NSStringPboardType,NSFilenamesPboardType,NSTIFFPboardType,nil];
421
422  [pasteBoard declareTypes: pbTypeArray owner:self];
423
424
425  if ( ! [pasteBoard setPropertyList: [[NSArray alloc] initWithObjects:[super fileName],nil]
426		   forType: NSStringPboardType] )
427    NSLog(@"Problem : cannot copy NSStringPboardType");
428
429
430  if (! [pasteBoard setPropertyList: [NSArray arrayWithObject:[super fileName]]
431		   forType: NSFilenamesPboardType] )
432    NSLog(@"Problem : cannot copy NSFilenamesPboardType");
433
434  if (! [pasteBoard setData: tiffRep forType: NSTIFFPboardType] )
435    NSLog(@"Problem : cannot copy NSTIFFPboardType");
436}
437
438
439/**
440 * Action method
441 *
442 *
443 *
444 */
445-(void) resize: (id) sender
446{
447  unsigned tag;
448  NSSize newSize;
449
450  //Get Tag (sender comes from popUp or matrix or menu
451  {
452    if ( sender == popUp )
453      tag = [sender indexOfSelectedItem]; //popUp
454    else if ( sender == matrix )
455      {
456	tag = [[sender selectedCell] tag]; //matrix
457      }
458    else
459      tag = [sender tag]; //menu
460  }
461
462
463  //Deselect matrix cells if popPup is selected
464  {
465    if (tag <= HALF_SIZE)
466      {
467	if (  [matrix selectedCell] )
468	  [matrix deselectAllCells];
469
470	[imageView setAutoresizingMask: NSViewNotSizable];
471      }
472  }
473
474  switch (tag)
475    {
476    case HEIGHT_HUNDRED_PER_CENT:
477      newSize.width = [self _originalSize].width * 8;
478      newSize.height = [self _originalSize].height * 8;
479      break;
480    case FOUR_HUNDRED_PER_CENT:
481      newSize.width = [self _originalSize].width * 4;
482      newSize.height = [self _originalSize].height * 4;
483      break;
484    case DOUBLE_SIZE:
485      newSize.width = [self _originalSize].width * 2;
486      newSize.height = [self _originalSize].height * 2;
487      break;
488    case FULL_SIZE:
489      newSize.width = [self _originalSize].width ;
490      newSize.height = [self _originalSize].height;
491      break;
492   case HALF_SIZE:
493      newSize.width = [self _originalSize].width / 2;
494      newSize.height = [self _originalSize].height / 2;
495      break;
496   case FIT_WINDOW:
497     newSize.width = [scrollView contentSize].width;
498     newSize.height = [scrollView contentSize].height;
499     [imageView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
500     if ( [self _isAlpha] )
501	{
502	  NSLog(@"isAlpha");
503	  [checkeredView setAutoresizingMask:
504			   (NSViewWidthSizable|NSViewHeightSizable)];
505	}
506     break;
507   case FIT_WIDTH:
508      newSize.width = [scrollView contentSize].width;
509      newSize.height = [imageView frame].size.height;
510      [imageView setAutoresizingMask: (NSViewWidthSizable)];
511      if ( [self _isAlpha] )
512	[checkeredView setAutoresizingMask: NSViewWidthSizable];
513      break;
514    default:
515      printf("problem resize default \n");
516      return;
517    }
518
519  //set autoresizing Mask
520  if ( tag < FIT_WINDOW )
521    {
522      if ( [self  _isAlpha] )
523	[checkeredView setAutoresizingMask: NSViewNotSizable];
524
525      [imageView setAutoresizingMask: NSViewNotSizable];
526    }
527
528
529  //Resize
530  if ( [self _isAlpha] )
531    {
532      [checkeredView setFrame:NSMakeRect(0,0,newSize.width,newSize.height) ];
533    }
534
535  NSLog(@"apres newSize %@",NSStringFromSize(newSize));
536
537  [imageView setFrame: NSMakeRect(0,0,newSize.width,newSize.height)];
538
539  // why TODO FIXME !!! only need with FIT_WINDOW
540  [imageView setNeedsDisplay:YES];
541  [checkeredView setNeedsDisplay:YES];
542  [self _setCurrentItem: tag];
543}
544
545-(void) zoomImage : (id) sender
546{
547  unsigned tag;
548  tag = [sender tag];
549
550  if ( tag == ZOOM_IN )
551    {
552      [self _setScaleFactor: (1 + SCALEFACTOR)];
553      [self _updateImage];
554    }
555  else if ( tag == ZOOM_OUT )
556    {
557      [self _setScaleFactor: (1 -SCALEFACTOR)];
558      [self _updateImage];
559    }
560  else
561    {
562      NSLog(@"zoomImage tag: %i",tag);
563    }
564}
565
566/**
567 * window delegate method.
568 * The mini icon is generate from _image
569 */
570- (void)windowDidMiniaturize:(NSNotification *)aNotification
571{
572  NSImage *miniImage = _image;
573  [miniImage setSize: NSMakeSize(48,48)];
574  [window setMiniwindowImage:miniImage];
575}
576
577/**
578 * window delegate method.
579 * This method is used to refresh the horizontalScroller width
580 */
581- (void)windowDidResize:(NSNotification *)aNotification
582{
583  NSRect scrollerRect =   [[scrollView horizontalScroller] frame];
584
585  scrollerRect.size.width = [window frame].size.width - 135;
586  [[scrollView horizontalScroller] setFrame:scrollerRect];
587}
588
589
590//Validate Menu :
591- (BOOL) validateMenuItem: (id)menuItem
592{
593  SEL action = [menuItem action];
594
595  if ( sel_isEqual(action,@selector(resize:)) )
596    {
597      if ( [menuItem tag] == [self _currentItem] )
598	return NO;
599    }
600
601  return YES;
602}
603
604
605@end
606
607