1/** <title>NSView</title>
2
3   <abstract>The view class which encapsulates all drawing functionality</abstract>
4
5   Copyright (C) 1996 Free Software Foundation, Inc.
6
7   Author: Scott Christley <scottc@net-community.com>
8   Date: 1996
9   Author: Ovidiu Predescu <ovidiu@net-community.com>
10   Date: 1997   Author: Felipe A. Rodriguez <far@ix.netcom.com>
11   Date: August 1998
12   Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
13   Date: January 1999
14
15   This file is part of the GNUstep GUI Library.
16
17   This library is free software; you can redistribute it and/or
18   modify it under the terms of the GNU Lesser General Public
19   License as published by the Free Software Foundation; either
20   version 2 of the License, or (at your option) any later version.
21
22   This library is distributed in the hope that it will be useful,
23   but WITHOUT ANY WARRANTY; without even the implied warranty of
24   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
25   Lesser General Public License for more details.
26
27   You should have received a copy of the GNU Lesser General Public
28   License along with this library; see the file COPYING.LIB.
29   If not, see <http://www.gnu.org/licenses/> or write to the
30   Free Software Foundation, 51 Franklin Street, Fifth Floor,
31   Boston, MA 02110-1301, USA.
32*/
33
34#import "config.h"
35#include <math.h>
36#include <float.h>
37
38#import <Foundation/NSString.h>
39#import <Foundation/NSBundle.h>
40#import <Foundation/NSCalendarDate.h>
41#import <Foundation/NSCoder.h>
42#import <Foundation/NSKeyedArchiver.h>
43#import <Foundation/NSDictionary.h>
44#import <Foundation/NSThread.h>
45#import <Foundation/NSLock.h>
46#import <Foundation/NSArray.h>
47#import <Foundation/NSNotification.h>
48#import <Foundation/NSValue.h>
49#import <Foundation/NSData.h>
50#import <Foundation/NSDebug.h>
51#import <Foundation/NSPathUtilities.h>
52#import <Foundation/NSSet.h>
53
54#import "AppKit/NSAffineTransform.h"
55#import "AppKit/NSApplication.h"
56#import "AppKit/NSBezierPath.h"
57#import "AppKit/NSBitmapImageRep.h"
58#import "AppKit/NSCursor.h"
59#import "AppKit/NSDocumentController.h"
60#import "AppKit/NSDocument.h"
61#import "AppKit/NSClipView.h"
62#import "AppKit/NSFont.h"
63#import "AppKit/NSGraphics.h"
64#import "AppKit/NSKeyValueBinding.h"
65#import "AppKit/NSMenu.h"
66#import "AppKit/NSPasteboard.h"
67#import "AppKit/NSPrintInfo.h"
68#import "AppKit/NSPrintOperation.h"
69#import "AppKit/NSScrollView.h"
70#import "AppKit/NSView.h"
71#import "AppKit/NSWindow.h"
72#import "AppKit/NSWorkspace.h"
73#import "AppKit/PSOperators.h"
74#import "GNUstepGUI/GSDisplayServer.h"
75#import "GNUstepGUI/GSTrackingRect.h"
76#import "GNUstepGUI/GSNibLoading.h"
77#import "GSToolTips.h"
78#import "GSBindingHelpers.h"
79#import "GSGuiPrivate.h"
80#import "NSViewPrivate.h"
81
82/*
83 * We need a fast array that can store objects without retain/release ...
84 */
85#define GSI_ARRAY_TYPES		GSUNION_OBJ
86#define GSI_ARRAY_NO_RELEASE	1
87#define GSI_ARRAY_NO_RETAIN	1
88
89#ifdef GSIArray
90#undef GSIArray
91#endif
92#include <GNUstepBase/GSIArray.h>
93
94#define	nKV(O)	((GSIArray)(O->_nextKeyView))
95#define	pKV(O)	((GSIArray)(O->_previousKeyView))
96
97/* Variable tells this view and subviews that we're printing. Not really
98   a class variable because we want it visible to subviews also
99*/
100NSView *viewIsPrinting = nil;
101
102/**
103  <unit>
104  <heading>NSView</heading>
105
106  <p>NSView is an abstract class which provides facilities for drawing
107  in a window and receiving events.  It is the superclass of many of
108  the visual elements of the GUI.</p>
109
110  <p>In order to display itself, a view must be placed in a window
111  (represented by an NSWindow object).  Within the window is a
112  hierarchy of NSViews, headed by the window's content view.  Every
113  other view in a window is a descendant of this view.</p>
114
115  <p>Subclasses can override -drawRect: in order to
116  implement their appearance.  Other methods of NSView and NSResponder
117  can also be overridden to handle user generated events.</p>
118
119  </unit>
120*/
121
122@implementation NSView
123
124/*
125 * Class variables */
126static Class	rectClass;
127static Class	viewClass;
128
129static NSAffineTransform	*flip = nil;
130
131static NSNotificationCenter *nc = nil;
132
133static SEL	preSel;
134static SEL	invalidateSel;
135
136static void	(*preImp)(NSAffineTransform*, SEL, NSAffineTransform*);
137static void	(*invalidateImp)(NSView*, SEL);
138
139/*
140 *	Stuff to maintain a map table so we know what views are
141 *	registered for drag and drop - we don't store the info in
142 *	the view directly 'cot it would take up a pointer in each
143 *	view and the vast majority of views wouldn't use it.
144 *	Types are not registered/unregistered often enough for the
145 *	performance of this mechanism to be an issue.
146 */
147static NSMapTable	*typesMap = 0;
148static NSLock		*typesLock = nil;
149
150/*
151 * This is the only external interface to the drag types info.
152 */
153NSArray*
154GSGetDragTypes(NSView *obj)
155{
156  NSArray	*t;
157
158  [typesLock lock];
159  t = (NSArray*)NSMapGet(typesMap, (void*)(gsaddr)obj);
160  [typesLock unlock];
161  return t;
162}
163
164static void
165GSRemoveDragTypes(NSView* obj)
166{
167  [typesLock lock];
168  NSMapRemove(typesMap, (void*)(gsaddr)obj);
169  [typesLock unlock];
170}
171
172static NSArray*
173GSSetDragTypes(NSView* obj, NSArray *types)
174{
175  NSUInteger	count = [types count];
176  NSString	*strings[count];
177  NSArray	*t;
178  NSUInteger	i;
179
180  /*
181   * Make a new array with copies of the type strings so we don't get
182   * them mutated by someone else.
183   */
184  [types getObjects: strings];
185  for (i = 0; i < count; i++)
186    {
187      strings[i] = [strings[i] copy];
188    }
189  t = [NSArray arrayWithObjects: strings count: count];
190  for (i = 0; i < count; i++)
191    {
192      RELEASE(strings[i]);
193    }
194  /*
195   * Store it.
196   */
197  [typesLock lock];
198  NSMapInsert(typesMap, (void*)(gsaddr)obj, (void*)(gsaddr)t);
199  [typesLock unlock];
200  return t;
201}
202
203
204/*
205 *	Private methods.
206 */
207
208
209/*
210 *	The [-_invalidateCoordinates] method marks the coordinate mapping
211 *	matrices (matrixFromWindow and _matrixToWindow) and the cached visible
212 *	rectangle as invalid.  It recursively invalidates the coordinates for
213 *	all subviews as well.
214 *	This method must be called whenever the size, shape or position of
215 *	the view is changed in any way.
216 */
217- (void) _invalidateCoordinates
218{
219  if (_coordinates_valid == YES)
220    {
221      NSUInteger count;
222
223      _coordinates_valid = NO;
224      if (_rFlags.valid_rects != 0)
225        {
226          [_window invalidateCursorRectsForView: self];
227        }
228      if (_rFlags.has_subviews)
229        {
230          count = [_sub_views count];
231          if (count > 0)
232            {
233              NSView*	array[count];
234              NSUInteger i;
235
236              [_sub_views getObjects: array];
237              for (i = 0; i < count; i++)
238                {
239                  NSView	*sub = array[i];
240
241                  if (sub->_coordinates_valid == YES)
242                    {
243                      (*invalidateImp)(sub, invalidateSel);
244                    }
245                }
246            }
247        }
248      [self renewGState];
249    }
250}
251
252/*
253 *	The [-_matrixFromWindow] method returns a matrix that can be used to
254 *	map coordinates in the windows coordinate system to coordinates in the
255 *	views coordinate system.  It rebuilds the mapping matrices and
256 *	visible rectangle cache if necessary.
257 *	All coordinate transformations use this matrix.
258 */
259- (NSAffineTransform*) _matrixFromWindow
260{
261  [self _rebuildCoordinates];
262  return _matrixFromWindow;
263}
264
265/*
266 *	The [-_matrixToWindow] method returns a matrix that can be used to
267 *	map coordinates in the views coordinate system to coordinates in the
268 *	windows coordinate system.  It rebuilds the mapping matrices and
269 *	visible rectangle cache if necessary.
270 *	All coordinate transformations use this matrix.
271 */
272- (NSAffineTransform*) _matrixToWindow
273{
274  [self _rebuildCoordinates];
275  return _matrixToWindow;
276}
277
278/*
279 *	The [-_rebuildCoordinates] method rebuilds the coordinate mapping
280 *	matrices (matrixFromWindow and _matrixToWindow) and the cached visible
281 *	rectangle if they have been invalidated.
282 */
283- (void) _rebuildCoordinates
284{
285  BOOL isFlipped = [self isFlipped];
286  BOOL lastFlipped = _rFlags.flipped_view;
287
288  if ((_coordinates_valid == NO) || (isFlipped != lastFlipped))
289    {
290      _coordinates_valid = YES;
291      _rFlags.flipped_view = isFlipped;
292
293      if (!_window && !_super_view)
294        {
295          _visibleRect = _bounds;
296          [_matrixToWindow makeIdentityMatrix];
297          [_matrixFromWindow makeIdentityMatrix];
298        }
299      else
300        {
301          NSRect		superviewsVisibleRect;
302          BOOL			superFlipped;
303          NSAffineTransform	*pMatrix;
304          NSAffineTransformStruct     ts;
305
306	  if (_super_view != nil)
307	    {
308	      superFlipped = [_super_view isFlipped];
309	      pMatrix = [_super_view _matrixToWindow];
310	    }
311	  else
312	    {
313	      superFlipped = NO;
314	      pMatrix = [NSAffineTransform transform];
315           }
316
317	  ts = [pMatrix transformStruct];
318
319          /* prepend translation */
320          ts.tX = NSMinX(_frame) * ts.m11 + NSMinY(_frame) * ts.m21 + ts.tX;
321          ts.tY = NSMinX(_frame) * ts.m12 + NSMinY(_frame) * ts.m22 + ts.tY;
322          [_matrixToWindow setTransformStruct: ts];
323
324          /* prepend rotation */
325          if (_frameMatrix != nil)
326            {
327              (*preImp)(_matrixToWindow, preSel, _frameMatrix);
328            }
329
330          if (isFlipped != superFlipped)
331            {
332              /*
333               * The flipping process must result in a coordinate system that
334               * exactly overlays the original.	 To do that, we must translate
335               * the origin by the height of the view.
336               */
337              ts = [flip transformStruct];
338              ts.tY = _frame.size.height;
339              [flip setTransformStruct: ts];
340              (*preImp)(_matrixToWindow, preSel, flip);
341            }
342          if (_boundsMatrix != nil)
343            {
344              (*preImp)(_matrixToWindow, preSel, _boundsMatrix);
345            }
346          ts = [_matrixToWindow transformStruct];
347          [_matrixFromWindow setTransformStruct: ts];
348          [_matrixFromWindow invert];
349
350	  if (_super_view != nil)
351	    {
352	      superviewsVisibleRect = [self convertRect: [_super_view visibleRect]
353					       fromView: _super_view];
354
355	      _visibleRect = NSIntersectionRect(superviewsVisibleRect, _bounds);
356	    }
357	  else
358	    {
359	      _visibleRect = _bounds;
360	    }
361        }
362    }
363}
364
365- (void) _viewDidMoveToWindow
366{
367  [self viewDidMoveToWindow];
368  if (_rFlags.has_subviews)
369    {
370      NSUInteger count = [_sub_views count];
371
372      if (count > 0)
373        {
374          NSUInteger i;
375          NSView *array[count];
376
377          [_sub_views getObjects: array];
378          for (i = 0; i < count; ++i)
379            {
380              [array[i] _viewDidMoveToWindow];
381            }
382        }
383    }
384}
385
386- (void) _viewWillMoveToWindow: (NSWindow*)newWindow
387{
388  BOOL old_allocate_gstate;
389
390  [self viewWillMoveToWindow: newWindow];
391  if (_coordinates_valid)
392    {
393      (*invalidateImp)(self, invalidateSel);
394    }
395  if (_rFlags.has_currects != 0)
396    {
397      [self discardCursorRects];
398    }
399
400  if (newWindow == _window)
401    {
402      return;
403    }
404
405  // This call also reset _allocate_gstate, so we have
406  // to store this value and set it again.
407  // This way we keep the logic in one place.
408  old_allocate_gstate = _allocate_gstate;
409  [self releaseGState];
410  _allocate_gstate = old_allocate_gstate;
411
412  if (_rFlags.has_draginfo)
413    {
414      NSArray *t = GSGetDragTypes(self);
415
416      if (_window != nil)
417        {
418          [GSDisplayServer removeDragTypes: t fromWindow: _window];
419	  if ([_window autorecalculatesKeyViewLoop])
420	    {
421	      [_window recalculateKeyViewLoop];
422	    }
423        }
424      if (newWindow != nil)
425        {
426          [GSDisplayServer addDragTypes: t toWindow: newWindow];
427	  if ([newWindow autorecalculatesKeyViewLoop])
428	    {
429	      [newWindow recalculateKeyViewLoop];
430	    }
431        }
432    }
433
434  _window = newWindow;
435
436  if (_rFlags.has_subviews)
437    {
438      NSUInteger count = [_sub_views count];
439
440      if (count > 0)
441        {
442          NSUInteger i;
443          NSView *array[count];
444
445          [_sub_views getObjects: array];
446          for (i = 0; i < count; ++i)
447            {
448              [array[i] _viewWillMoveToWindow: newWindow];
449            }
450        }
451    }
452}
453
454- (void) _viewWillMoveToSuperview: (NSView*)newSuper
455{
456  [self viewWillMoveToSuperview: newSuper];
457  _super_view = newSuper;
458}
459
460/*
461 * Extend in super view covered by the frame of a view.
462 * When the frame is rotated, this is different from the frame.
463 */
464- (NSRect) _frameExtend
465{
466  NSRect frame = _frame;
467
468  if (_frameMatrix != nil)
469    {
470      NSRect r;
471
472      r.origin = NSZeroPoint;
473      r.size = frame.size;
474      [_frameMatrix boundingRectFor: r result: &r];
475      frame = NSOffsetRect(r, NSMinX(frame),
476                           NSMinY(frame));
477    }
478
479  return frame;
480}
481
482
483- (NSString*) _subtreeDescriptionWithPrefix: (NSString*)prefix
484{
485  NSMutableString *desc = [[NSMutableString alloc] init];
486  NSEnumerator *e;
487  NSView *v;
488
489  [desc appendFormat: @"%@%@\n", prefix, [self description], nil];
490
491  prefix = [prefix stringByAppendingString: @"  "];
492  e = [_sub_views objectEnumerator];
493  while ((v = (NSView*)[e nextObject]) != nil)
494    {
495      [desc appendString: [v _subtreeDescriptionWithPrefix: prefix]];
496    }
497
498  return AUTORELEASE(desc);
499}
500
501/*
502 * Unofficial Cocoa method for debugging a view hierarchy.
503 */
504- (NSString*) _subtreeDescription
505{
506  return [self _subtreeDescriptionWithPrefix: @""];
507}
508
509- (NSString*) _flagDescription
510{
511  return @"";
512}
513
514- (NSString*) _resizeDescription
515{
516  return [NSString stringWithFormat: @"h=%c%c%c v=%c%c%c",
517                   (_autoresizingMask & NSViewMinXMargin) ? '&' : '-',
518                   (_autoresizingMask & NSViewWidthSizable) ? '&' : '-',
519                   (_autoresizingMask & NSViewMaxXMargin) ? '&' : '-',
520                   (_autoresizingMask & NSViewMinYMargin) ? '&' : '-',
521                   (_autoresizingMask & NSViewHeightSizable) ? '&' : '-',
522                   (_autoresizingMask & NSViewMaxYMargin) ? '&' : '-',
523                   nil];
524}
525
526- (NSString*) description
527{
528  return [NSString stringWithFormat: @"%@ %@ %@ f=%@ b=%@",
529                   [self _flagDescription],
530                   [self _resizeDescription], [super description],
531                   NSStringFromRect(_frame), NSStringFromRect(_bounds), nil];
532}
533
534/*
535 * Class methods
536 */
537+ (void) initialize
538{
539  if (self == [NSView class])
540    {
541      Class	matrixClass = [NSAffineTransform class];
542      NSAffineTransformStruct	ats = { 1, 0, 0, -1, 0, 1 };
543
544      typesMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
545                NSObjectMapValueCallBacks, 0);
546      typesLock = [NSLock new];
547
548      preSel = @selector(prependTransform:);
549      invalidateSel = @selector(_invalidateCoordinates);
550
551      preImp = (void (*)(NSAffineTransform*, SEL, NSAffineTransform*))
552          [matrixClass instanceMethodForSelector: preSel];
553
554      invalidateImp = (void (*)(NSView*, SEL))
555          [self instanceMethodForSelector: invalidateSel];
556
557      flip = [matrixClass new];
558      [flip setTransformStruct: ats];
559
560      nc = [NSNotificationCenter defaultCenter];
561
562      viewClass = [NSView class];
563      rectClass = [GSTrackingRect class];
564      NSDebugLLog(@"NSView", @"Initialize NSView class\n");
565      [self setVersion: 1];
566
567      // expose bindings
568      [self exposeBinding: NSToolTipBinding];
569      [self exposeBinding: NSHiddenBinding];
570    }
571}
572
573/**
574 Return the view at the top of graphics contexts stack
575 or nil if none is focused.
576 */
577+ (NSView*) focusView
578{
579  return [GSCurrentContext() focusView];
580}
581
582/*
583 * Instance methods
584 */
585- (id) init
586{
587  return [self initWithFrame: NSZeroRect];
588}
589
590- (id) initWithFrame: (NSRect)frameRect
591{
592  self = [super init];
593  if (!self)
594    return self;
595
596  if (frameRect.size.width < 0)
597    {
598      NSWarnMLog(@"given negative width");
599      frameRect.size.width = 0;
600    }
601  if (frameRect.size.height < 0)
602    {
603      NSWarnMLog(@"given negative height");
604      frameRect.size.height = 0;
605    }
606  _frame = frameRect;			// Set frame rectangle
607  _bounds.origin = NSZeroPoint;		// Set bounds rectangle
608  _bounds.size = _frame.size;
609
610  // _frameMatrix = [NSAffineTransform new];    // Map fromsuperview to frame
611  // _boundsMatrix = [NSAffineTransform new];   // Map from superview to bounds
612  _matrixToWindow = [NSAffineTransform new];   // Map to window coordinates
613  _matrixFromWindow = [NSAffineTransform new]; // Map from window coordinates
614
615  _sub_views = [NSMutableArray new];
616  _tracking_rects = [NSMutableArray new];
617  _cursor_rects = [NSMutableArray new];
618
619  // Some values are already set by initialisation
620  //_super_view = nil;
621  //_window = nil;
622  //_is_rotated_from_base = NO;
623  //_is_rotated_or_scaled_from_base = NO;
624  _rFlags.needs_display = YES;
625  _post_bounds_changes = YES;
626  _post_frame_changes = YES;
627  _autoresizes_subviews = YES;
628  _autoresizingMask = NSViewNotSizable;
629  //_coordinates_valid = NO;
630  //_nextKeyView = 0;
631  //_previousKeyView = 0;
632
633  _alphaValue = 1.0;
634
635  return self;
636}
637
638- (void) dealloc
639{
640  NSView *tmp;
641  NSUInteger count;
642
643  // Remove all key value bindings for this view.
644  [GSKeyValueBinding unbindAllForObject: self];
645
646  /*
647   * Remove self from view chain.  Try to mimic MacOS-X behavior ...
648   * We send setNextKeyView: messages to all view for which we are the
649   * next key view, setting their next key view to nil.
650   *
651   * First we do the obvious stuff using the standard methods.
652   */
653  [self setNextKeyView: nil];
654  tmp = [self previousKeyView];
655  if ([tmp nextKeyView] == self)
656    [tmp setNextKeyView: nil];
657
658  /*
659   * Now, we locate any remaining cases where a view has us as its next
660   * view, and ask the view to change that.
661   */
662  if (pKV(self) != 0)
663    {
664      count = GSIArrayCount(pKV(self));
665      while (count-- > 0)
666	{
667	  tmp = GSIArrayItemAtIndex(pKV(self), count).obj;
668	  if ([tmp nextKeyView] == self)
669	    {
670	      [tmp setNextKeyView: nil];
671	    }
672	}
673    }
674
675  /*
676   * Now we clean up the previous view array, in case subclasses have
677   * overridden the default -setNextKeyView: method and broken things.
678   * We also relase the memory we used.
679   */
680  if (pKV(self) != 0)
681    {
682      count = GSIArrayCount(pKV(self));
683      while (count-- > 0)
684	{
685	  tmp = GSIArrayItemAtIndex(pKV(self), count).obj;
686	  if (tmp != nil && nKV(tmp) != 0)
687	    {
688	      NSUInteger otherCount = GSIArrayCount(nKV(tmp));
689
690	      while (otherCount-- > 1)
691		{
692		  if (GSIArrayItemAtIndex(nKV(tmp), otherCount).obj == self)
693		    {
694		      GSIArrayRemoveItemAtIndex(nKV(tmp), otherCount);
695		    }
696		}
697	      if (GSIArrayItemAtIndex(nKV(tmp), 0).obj == self)
698		{
699		  GSIArraySetItemAtIndex(nKV(tmp), (GSIArrayItem)nil, 0);
700		}
701	    }
702	}
703      GSIArrayClear(pKV(self));
704      NSZoneFree(NSDefaultMallocZone(), pKV(self));
705      _previousKeyView = 0;
706    }
707
708  /*
709   * Now we clean up all views which have us as their previous view.
710   * We also release the memory we used.
711   */
712  if (nKV(self) != 0)
713    {
714      count = GSIArrayCount(nKV(self));
715      while (count-- > 0)
716	{
717	  tmp = GSIArrayItemAtIndex(nKV(self), count).obj;
718	  if (tmp != nil && pKV(tmp) != 0)
719	    {
720	      NSUInteger otherCount = GSIArrayCount(pKV(tmp));
721
722	      while (otherCount-- > 1)
723		{
724		  if (GSIArrayItemAtIndex(pKV(tmp), otherCount).obj == self)
725		    {
726		      GSIArrayRemoveItemAtIndex(pKV(tmp), otherCount);
727		    }
728		}
729	      if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self)
730		{
731		  GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0);
732		}
733	    }
734	}
735      GSIArrayClear(nKV(self));
736      NSZoneFree(NSDefaultMallocZone(), nKV(self));
737      _nextKeyView = 0;
738    }
739
740  /*
741   * Now remove our subviews, AFTER cleaning up the view chain, in case
742   * any of our subviews were in the chain.
743   */
744  while ([_sub_views count] > 0)
745    {
746      [[_sub_views lastObject] removeFromSuperviewWithoutNeedingDisplay];
747    }
748
749  RELEASE(_matrixToWindow);
750  RELEASE(_matrixFromWindow);
751  TEST_RELEASE(_frameMatrix);
752  TEST_RELEASE(_boundsMatrix);
753  TEST_RELEASE(_sub_views);
754  if (_rFlags.has_tooltips != 0)
755    {
756      [GSToolTips removeTipsForView: self];
757    }
758  if (_rFlags.has_currects != 0)
759    {
760      [self discardCursorRects];	// Handle release of cursors
761    }
762  TEST_RELEASE(_cursor_rects);
763  TEST_RELEASE(_tracking_rects);
764  [self unregisterDraggedTypes];
765  [self releaseGState];
766
767  [super dealloc];
768}
769
770/**
771 * Adds aView as a subview of the receiver.
772 */
773- (void) addSubview: (NSView*)aView
774{
775  [self addSubview: aView
776        positioned: NSWindowAbove
777        relativeTo: nil];
778}
779
780- (void) addSubview: (NSView*)aView
781	 positioned: (NSWindowOrderingMode)place
782	 relativeTo: (NSView*)otherView
783{
784  NSUInteger index;
785
786  if (aView == nil)
787    {
788      return;
789    }
790  if ([self isDescendantOf: aView])
791    {
792      [NSException raise: NSInvalidArgumentException
793		  format: @"addSubview:positioned:relativeTo: creates a "
794	@"loop in the views tree!"];
795    }
796
797  if (aView == otherView)
798    return;
799
800  RETAIN(aView);
801  [aView removeFromSuperview];
802
803  // Do this after the removeFromSuperview, as aView may already
804  // be a subview and the index could change.
805  if (otherView == nil)
806    {
807      index = NSNotFound;
808    }
809  else
810    {
811      index = [_sub_views indexOfObjectIdenticalTo: otherView];
812    }
813  if (index == NSNotFound)
814    {
815      if (place == NSWindowBelow)
816        index = 0;
817      else
818        index = [_sub_views count];
819    }
820  else if (place != NSWindowBelow)
821    {
822      index += 1;
823    }
824
825  [aView _viewWillMoveToWindow: _window];
826  [aView _viewWillMoveToSuperview: self];
827  [aView setNextResponder: self];
828  [_sub_views insertObject: aView atIndex: index];
829  _rFlags.has_subviews = 1;
830  [aView resetCursorRects];
831  [aView setNeedsDisplay: YES];
832  [aView _viewDidMoveToWindow];
833  [aView viewDidMoveToSuperview];
834  [self didAddSubview: aView];
835  RELEASE(aView);
836}
837
838/**
839 * Returns self if aView is the receiver or aView is a subview of the receiver,
840 * the ancestor view shared by aView and the receiver if any, or
841 * aView if it is an ancestor of the receiver, otherwise returns nil.
842 */
843- (NSView*) ancestorSharedWithView: (NSView*)aView
844{
845  if (self == aView)
846    return self;
847
848  if ([self isDescendantOf: aView])
849    return aView;
850
851  if ([aView isDescendantOf: self])
852    return self;
853
854  /*
855   * If neither are descendants of each other and either does not have a
856   * superview then they cannot have a common ancestor
857   */
858  if (!_super_view)
859    return nil;
860
861  if (![aView superview])
862    return nil;
863
864  /* Find the common ancestor of superviews */
865  return [_super_view ancestorSharedWithView: [aView superview]];
866}
867
868/**
869 * Returns YES if aView is an ancestor of the receiver.
870 */
871- (BOOL) isDescendantOf: (NSView*)aView
872{
873  if (aView == self)
874    return YES;
875
876  if (!_super_view)
877    return NO;
878
879  if (_super_view == aView)
880    return YES;
881
882  return [_super_view isDescendantOf: aView];
883}
884
885- (NSView*) opaqueAncestor
886{
887  NSView	*next = _super_view;
888  NSView	*current = self;
889
890  while (next != nil)
891    {
892      if ([current isOpaque] == YES)
893	{
894	  break;
895	}
896      current = next;
897      next = current->_super_view;
898    }
899  return current;
900}
901
902/**
903 * Removes the receiver from its superviews list of subviews.
904 */
905- (void) removeFromSuperviewWithoutNeedingDisplay
906{
907  if (_super_view != nil)
908    {
909      [_super_view removeSubview: self];
910    }
911}
912
913/**
914  <p> Removes the receiver from its superviews list of subviews
915  and marks the rectangle that the reciever occupied in the
916  superview as needing redisplay.  </p>
917
918  <p> This is dangerous to use during display, since it alters the
919  rectangles needing display. In this case, you can use the
920  -removeFromSuperviewWithoutNeedingDisplay method instead.</p> */
921- (void) removeFromSuperview
922{
923  if (_super_view != nil)
924    {
925      [_super_view setNeedsDisplayInRect: _frame];
926      [self removeFromSuperviewWithoutNeedingDisplay];
927    }
928}
929
930/**
931  <p> Removes aSubview from the receivers list of subviews and from
932  the responder chain.  </p>
933
934  <p> Also invokes -viewWillMoveToWindow: on aView with a nil argument,
935  to handle
936  removal of aView (and recursively, its children) from its window -
937  performing tidyup by invalidating cursor rects etc.  </p>
938*/
939- (void) removeSubview: (NSView*)aView
940{
941  id view;
942  /*
943   * This must be first because it invokes -resignFirstResponder:,
944   * which assumes the view is still in the view hierarchy
945   */
946  for (view = [_window firstResponder];
947    view != nil && [view respondsToSelector: @selector(superview)];
948    view = [view superview])
949    {
950      if (view == aView)
951	{
952	  [_window makeFirstResponder: _window];
953	  break;
954	}
955    }
956  [self willRemoveSubview: aView];
957  aView->_super_view = nil;
958  [aView _viewWillMoveToWindow: nil];
959  [aView _viewWillMoveToSuperview: nil];
960  [aView setNextResponder: nil];
961  RETAIN(aView);
962  [_sub_views removeObjectIdenticalTo: aView];
963  [aView setNeedsDisplay: NO];
964  [aView _viewDidMoveToWindow];
965  [aView viewDidMoveToSuperview];
966  RELEASE(aView);
967  if ([_sub_views count] == 0)
968    {
969      _rFlags.has_subviews = 0;
970    }
971}
972
973/**
974 * Removes oldView, which should be a subview of the receiver, from the
975 * receiver and places newView in its place. If newView is nil, just
976 * removes oldView. If oldView is nil, just adds newView.
977 */
978- (void) replaceSubview: (NSView*)oldView with: (NSView*)newView
979{
980  if (newView == oldView)
981    {
982      return;
983    }
984  /*
985   * NB. we implement the replacement in full rather than calling addSubview:
986   * since classes like NSBox override these methods but expect to be able to
987   * call [super replaceSubview:with:] safely.
988   */
989  if (oldView == nil)
990    {
991      /*
992       * Strictly speaking, the docs say that if 'oldView' is not a subview
993       * of the receiver then we do nothing - but here we add newView anyway.
994       * So a replacement with no oldView is an addition.
995       */
996      RETAIN(newView);
997      [newView removeFromSuperview];
998      [newView _viewWillMoveToWindow: _window];
999      [newView _viewWillMoveToSuperview: self];
1000      [newView setNextResponder: self];
1001      [_sub_views addObject: newView];
1002      _rFlags.has_subviews = 1;
1003      [newView resetCursorRects];
1004      [newView setNeedsDisplay: YES];
1005      [newView _viewDidMoveToWindow];
1006      [newView viewDidMoveToSuperview];
1007      [self didAddSubview: newView];
1008      RELEASE(newView);
1009    }
1010  else if ([_sub_views indexOfObjectIdenticalTo: oldView] != NSNotFound)
1011    {
1012      if (newView == nil)
1013	{
1014	  /*
1015	   * If there is no new view to add - we just remove the old one.
1016	   * So a replacement with no newView is a removal.
1017	   */
1018	  [oldView removeFromSuperview];
1019	}
1020      else
1021	{
1022	  NSUInteger index;
1023
1024	  /*
1025	   * Ok - the standard case - we remove the newView from wherever it
1026	   * was (which may have been in this view), locate the position of
1027	   * the oldView (which may have changed due to the removal of the
1028	   * newView), remove the oldView, and insert the newView in it's
1029	   * place.
1030	   */
1031	  RETAIN(newView);
1032	  [newView removeFromSuperview];
1033	  index = [_sub_views indexOfObjectIdenticalTo: oldView];
1034	  [oldView removeFromSuperview];
1035	  [newView _viewWillMoveToWindow: _window];
1036	  [newView _viewWillMoveToSuperview: self];
1037	  [newView setNextResponder: self];
1038          [_sub_views insertObject: newView
1039                           atIndex: index];
1040	  _rFlags.has_subviews = 1;
1041	  [newView resetCursorRects];
1042	  [newView setNeedsDisplay: YES];
1043	  [newView _viewDidMoveToWindow];
1044	  [newView viewDidMoveToSuperview];
1045	  [self didAddSubview: newView];
1046	  RELEASE(newView);
1047	}
1048    }
1049}
1050
1051- (void) setSubviews: (NSArray *)newSubviews
1052{
1053  NSEnumerator *en;
1054  NSView *aView;
1055  NSMutableArray *uniqNew = [NSMutableArray array];
1056
1057  if (nil == newSubviews)
1058    {
1059      [NSException raise: NSInvalidArgumentException
1060                  format: @"Setting nil as new subviews."];
1061    }
1062
1063  // Use a copy as we remove from the subviews array
1064  en = [[NSArray arrayWithArray: _sub_views] objectEnumerator];
1065  while ((aView = [en nextObject]))
1066    {
1067      if (NO == [newSubviews containsObject: aView])
1068        {
1069          [aView removeFromSuperview];
1070        }
1071    }
1072
1073  en = [newSubviews objectEnumerator];
1074  while ((aView = [en nextObject]))
1075    {
1076      id supersub = [aView superview];
1077
1078      if (supersub != nil && supersub != self)
1079        {
1080          [NSException raise: NSInvalidArgumentException
1081                      format: @"Superviews of new subviews must be either nil or receiver."];
1082        }
1083
1084      if ([uniqNew containsObject: aView])
1085        {
1086          [NSException raise: NSInvalidArgumentException
1087                      format: @"Duplicated new subviews."];
1088        }
1089
1090      if (NO == [_sub_views containsObject: aView])
1091        {
1092          [self addSubview: aView];
1093        }
1094
1095      [uniqNew addObject: aView];
1096    }
1097
1098  ASSIGN(_sub_views, uniqNew);
1099
1100  // The order of the subviews may have changed
1101  [self setNeedsDisplay: YES];
1102}
1103
1104- (void) sortSubviewsUsingFunction: (NSComparisonResult (*)(id ,id ,void*))compare
1105			   context: (void*)context
1106{
1107  [_sub_views sortUsingFunction: compare context: context];
1108}
1109
1110/**
1111 * Notifies the receiver that its superview is being changed to newSuper.
1112 */
1113- (void) viewWillMoveToSuperview: (NSView*)newSuper
1114{
1115}
1116
1117/**
1118 * Notifies the receiver that it will now be a view of newWindow.
1119 * Note, this method is also used when removing a view from a window
1120 * (in which case, newWindow is nil) to let all the subviews know
1121 * that they have also been removed from the window.
1122 */
1123- (void) viewWillMoveToWindow: (NSWindow*)newWindow
1124{
1125}
1126
1127- (void) didAddSubview: (NSView *)subview
1128{}
1129
1130- (void) viewDidMoveToSuperview
1131{}
1132
1133- (void) viewDidMoveToWindow
1134{}
1135
1136- (void) willRemoveSubview: (NSView *)subview
1137{}
1138
1139static NSSize _computeScale(NSSize fs, NSSize bs)
1140{
1141  NSSize scale;
1142
1143  if (bs.width == 0)
1144    {
1145      if (fs.width == 0)
1146        scale.width = 1;
1147      else
1148        scale.width = FLT_MAX;
1149    }
1150  else
1151    {
1152      scale.width = fs.width / bs.width;
1153    }
1154  if (bs.height == 0)
1155    {
1156      if (fs.height == 0)
1157        scale.height = 1;
1158      else
1159        scale.height = FLT_MAX;
1160    }
1161  else
1162    {
1163      scale.height = fs.height / bs.height;
1164    }
1165
1166  return scale;
1167}
1168
1169- (void) _setFrameAndClearAutoresizingError: (NSRect)frameRect
1170{
1171  _frame = frameRect;
1172  _autoresizingFrameError = NSZeroRect;
1173}
1174
1175- (void) setFrame: (NSRect)frameRect
1176{
1177  BOOL	changedOrigin = NO;
1178  BOOL	changedSize = NO;
1179  NSSize old_size = _frame.size;
1180
1181  if (frameRect.size.width < 0)
1182    {
1183      NSWarnMLog(@"given negative width");
1184      frameRect.size.width = 0;
1185    }
1186  if (frameRect.size.height < 0)
1187    {
1188      NSWarnMLog(@"given negative height");
1189      frameRect.size.height = 0;
1190    }
1191
1192  if (NSEqualPoints(_frame.origin, frameRect.origin) == NO)
1193    {
1194      changedOrigin = YES;
1195    }
1196  if (NSEqualSizes(_frame.size, frameRect.size) == NO)
1197    {
1198      changedSize = YES;
1199    }
1200
1201  if (changedSize == YES || changedOrigin == YES)
1202    {
1203      [self _setFrameAndClearAutoresizingError: frameRect];
1204
1205      if (changedSize == YES)
1206        {
1207          if (_is_rotated_or_scaled_from_base == YES)
1208            {
1209              NSAffineTransform *matrix;
1210              NSRect frame = _frame;
1211
1212              frame.origin = NSMakePoint(0, 0);
1213              matrix = [_boundsMatrix copy];
1214              [matrix invert];
1215              [matrix boundingRectFor: frame result: &_bounds];
1216              RELEASE(matrix);
1217            }
1218          else
1219            {
1220              _bounds.size = frameRect.size;
1221            }
1222        }
1223
1224      if (_coordinates_valid)
1225        {
1226          (*invalidateImp)(self, invalidateSel);
1227        }
1228      [self resetCursorRects];
1229      [self resizeSubviewsWithOldSize: old_size];
1230      if (_post_frame_changes)
1231        {
1232          [nc postNotificationName: NSViewFrameDidChangeNotification
1233              object: self];
1234        }
1235    }
1236}
1237
1238- (void) setFrameOrigin: (NSPoint)newOrigin
1239{
1240  if (NSEqualPoints(_frame.origin, newOrigin) == NO)
1241    {
1242      NSRect newFrame = _frame;
1243      newFrame.origin = newOrigin;
1244
1245      if (_coordinates_valid)
1246        {
1247          (*invalidateImp)(self, invalidateSel);
1248        }
1249      [self _setFrameAndClearAutoresizingError: newFrame];
1250      [self resetCursorRects];
1251      if (_post_frame_changes)
1252        {
1253          [nc postNotificationName: NSViewFrameDidChangeNotification
1254              object: self];
1255        }
1256    }
1257}
1258
1259- (void) setFrameSize: (NSSize)newSize
1260{
1261  NSRect newFrame = _frame;
1262  if (newSize.width < 0)
1263    {
1264      NSWarnMLog(@"given negative width");
1265      newSize.width = 0;
1266    }
1267  if (newSize.height < 0)
1268    {
1269      NSWarnMLog(@"given negative height");
1270      newSize.height = 0;
1271    }
1272  if (NSEqualSizes(_frame.size, newSize) == NO)
1273    {
1274      NSSize old_size = _frame.size;
1275
1276      if (_is_rotated_or_scaled_from_base)
1277        {
1278          if (_boundsMatrix == nil)
1279            {
1280              CGFloat sx = _bounds.size.width  / _frame.size.width;
1281              CGFloat sy = _bounds.size.height / _frame.size.height;
1282
1283              newFrame.size = newSize;
1284	      [self _setFrameAndClearAutoresizingError: newFrame];
1285              _bounds.size.width  = _frame.size.width  * sx;
1286              _bounds.size.height = _frame.size.height * sy;
1287            }
1288          else
1289            {
1290              NSAffineTransform *matrix;
1291              NSRect frame;
1292
1293              newFrame.size = newSize;
1294	      [self _setFrameAndClearAutoresizingError: newFrame];
1295
1296              frame = _frame;
1297              frame.origin = NSMakePoint(0, 0);
1298              matrix = [_boundsMatrix copy];
1299              [matrix invert];
1300              [matrix boundingRectFor: frame result: &_bounds];
1301              RELEASE(matrix);
1302            }
1303        }
1304      else
1305        {
1306          newFrame.size = _bounds.size = newSize;
1307	  [self _setFrameAndClearAutoresizingError: newFrame];
1308        }
1309
1310      if (_coordinates_valid)
1311        {
1312          (*invalidateImp)(self, invalidateSel);
1313        }
1314      [self resetCursorRects];
1315      [self resizeSubviewsWithOldSize: old_size];
1316      if (_post_frame_changes)
1317        {
1318          [nc postNotificationName: NSViewFrameDidChangeNotification
1319              object: self];
1320        }
1321    }
1322}
1323
1324- (void) setFrameRotation: (CGFloat)angle
1325{
1326  CGFloat oldAngle = [self frameRotation];
1327
1328  if (oldAngle != angle)
1329    {
1330      /* no frame matrix, create one since it is needed for rotation */
1331      if (_frameMatrix == nil)
1332        {
1333          // Map from superview to frame
1334          _frameMatrix = [NSAffineTransform new];
1335        }
1336
1337      [_frameMatrix rotateByDegrees: angle - oldAngle];
1338      _is_rotated_from_base = _is_rotated_or_scaled_from_base = YES;
1339
1340      if (_coordinates_valid)
1341        {
1342          (*invalidateImp)(self, invalidateSel);
1343        }
1344      [self resetCursorRects];
1345      if (_post_frame_changes)
1346        {
1347          [nc postNotificationName: NSViewFrameDidChangeNotification
1348              object: self];
1349        }
1350    }
1351}
1352
1353- (BOOL) isRotatedFromBase
1354{
1355  if (_is_rotated_from_base)
1356    {
1357      return YES;
1358    }
1359  else if (_super_view)
1360    {
1361      return [_super_view isRotatedFromBase];
1362    }
1363  else
1364    {
1365      return NO;
1366    }
1367}
1368
1369- (BOOL) isRotatedOrScaledFromBase
1370{
1371  if (_is_rotated_or_scaled_from_base)
1372    {
1373      return YES;
1374    }
1375  else if (_super_view)
1376    {
1377      return [_super_view isRotatedOrScaledFromBase];
1378    }
1379  else
1380    {
1381      return NO;
1382    }
1383}
1384
1385- (void) setBounds: (NSRect)aRect
1386{
1387  NSDebugLLog(@"NSView", @"setBounds %@", NSStringFromRect(aRect));
1388  if (aRect.size.width < 0)
1389    {
1390      NSWarnMLog(@"given negative width");
1391      aRect.size.width = 0;
1392    }
1393  if (aRect.size.height < 0)
1394    {
1395      NSWarnMLog(@"given negative height");
1396      aRect.size.height = 0;
1397    }
1398
1399  if (_is_rotated_from_base || (NSEqualRects(_bounds, aRect) == NO))
1400    {
1401      NSAffineTransform *matrix;
1402      NSPoint oldOrigin;
1403      NSSize scale;
1404
1405      if (_boundsMatrix == nil)
1406        {
1407          _boundsMatrix = [NSAffineTransform new];
1408        }
1409
1410      // Adjust scale
1411      scale = _computeScale(_frame.size, aRect.size);
1412      if (scale.width != 1 || scale.height != 1)
1413        {
1414          _is_rotated_or_scaled_from_base = YES;
1415        }
1416      [_boundsMatrix scaleTo: scale.width : scale.height];
1417        {
1418          matrix = [_boundsMatrix copy];
1419          [matrix invert];
1420          oldOrigin = [matrix transformPoint: NSMakePoint(0, 0)];
1421          RELEASE(matrix);
1422        }
1423      [_boundsMatrix translateXBy: oldOrigin.x - aRect.origin.x
1424                     yBy: oldOrigin.y - aRect.origin.y];
1425      if (!_is_rotated_from_base)
1426        {
1427          // Adjust bounds
1428          _bounds = aRect;
1429        }
1430      else
1431        {
1432          // Adjust bounds
1433          NSRect frame = _frame;
1434
1435          frame.origin = NSMakePoint(0, 0);
1436          matrix = [_boundsMatrix copy];
1437          [matrix invert];
1438          [matrix boundingRectFor: frame result: &_bounds];
1439          RELEASE(matrix);
1440       }
1441
1442      if (_coordinates_valid)
1443        {
1444          (*invalidateImp)(self, invalidateSel);
1445        }
1446      [self resetCursorRects];
1447      if (_post_bounds_changes)
1448        {
1449          [nc postNotificationName: NSViewBoundsDidChangeNotification
1450              object: self];
1451        }
1452    }
1453}
1454
1455- (void) setBoundsOrigin: (NSPoint)newOrigin
1456{
1457  NSPoint oldOrigin;
1458
1459  if (_boundsMatrix == nil)
1460    {
1461      oldOrigin = NSMakePoint(NSMinX(_bounds), NSMinY(_bounds));
1462    }
1463  else
1464    {
1465      NSAffineTransform *matrix = [_boundsMatrix copy];
1466
1467      [matrix invert];
1468      oldOrigin = [matrix transformPoint: NSMakePoint(0, 0)];
1469      RELEASE(matrix);
1470    }
1471  [self translateOriginToPoint: NSMakePoint(oldOrigin.x - newOrigin.x,
1472                                            oldOrigin.y - newOrigin.y)];
1473}
1474
1475- (void) setBoundsSize: (NSSize)newSize
1476{
1477  NSSize scale;
1478
1479  NSDebugLLog(@"NSView", @"%@ setBoundsSize: %@", self,
1480              NSStringFromSize(newSize));
1481
1482  if (newSize.width < 0)
1483    {
1484      NSWarnMLog(@"given negative width");
1485      newSize.width = 0;
1486    }
1487  if (newSize.height < 0)
1488    {
1489      NSWarnMLog(@"given negative height");
1490      newSize.height = 0;
1491    }
1492
1493  scale = _computeScale(_frame.size, newSize);
1494  if (scale.width != 1 || scale.height != 1)
1495    {
1496      _is_rotated_or_scaled_from_base = YES;
1497    }
1498
1499  if (_boundsMatrix == nil)
1500    {
1501      _boundsMatrix = [NSAffineTransform new];
1502    }
1503  [_boundsMatrix scaleTo: scale.width : scale.height];
1504  if (!_is_rotated_from_base)
1505    {
1506      scale = _computeScale(_bounds.size, newSize);
1507      _bounds.origin.x = _bounds.origin.x / scale.width;
1508      _bounds.origin.y = _bounds.origin.y / scale.height;
1509      _bounds.size = newSize;
1510    }
1511  else
1512    {
1513      NSAffineTransform *matrix;
1514      NSRect frame = _frame;
1515
1516      frame.origin = NSMakePoint(0, 0);
1517      matrix = [_boundsMatrix copy];
1518      [matrix invert];
1519      [matrix boundingRectFor: frame result: &_bounds];
1520      RELEASE(matrix);
1521    }
1522
1523  if (_coordinates_valid)
1524    {
1525      (*invalidateImp)(self, invalidateSel);
1526    }
1527  [self resetCursorRects];
1528  if (_post_bounds_changes)
1529    {
1530      [nc postNotificationName: NSViewBoundsDidChangeNotification
1531                        object: self];
1532    }
1533}
1534
1535- (void) setBoundsRotation: (CGFloat)angle
1536{
1537  [self rotateByAngle: angle - [self boundsRotation]];
1538}
1539
1540- (void) translateOriginToPoint: (NSPoint)point
1541{
1542  NSDebugLLog(@"NSView", @"%@ translateOriginToPoint: %@", self,
1543              NSStringFromPoint(point));
1544  if (NSEqualPoints(NSZeroPoint, point) == NO)
1545    {
1546      if (_boundsMatrix == nil)
1547        {
1548          _boundsMatrix = [NSAffineTransform new];
1549        }
1550      [_boundsMatrix translateXBy: point.x
1551                     yBy: point.y];
1552      // Adjust bounds
1553      _bounds.origin.x -= point.x;
1554      _bounds.origin.y -= point.y;
1555
1556      if (_coordinates_valid)
1557        {
1558          (*invalidateImp)(self, invalidateSel);
1559        }
1560      [self resetCursorRects];
1561      if (_post_bounds_changes)
1562        {
1563          [nc postNotificationName: NSViewBoundsDidChangeNotification
1564              object: self];
1565        }
1566    }
1567}
1568
1569- (void) scaleUnitSquareToSize: (NSSize)newSize
1570{
1571  if (newSize.width != 1.0 || newSize.height != 1.0)
1572    {
1573      if (newSize.width < 0)
1574        {
1575          NSWarnMLog(@"given negative width");
1576          newSize.width = 0;
1577        }
1578      if (newSize.height < 0)
1579        {
1580          NSWarnMLog(@"given negative height");
1581          newSize.height = 0;
1582        }
1583
1584      if (_boundsMatrix == nil)
1585        {
1586          _boundsMatrix = [NSAffineTransform new];
1587        }
1588      [_boundsMatrix scaleXBy: newSize.width yBy: newSize.height];
1589      // Adjust bounds
1590      _bounds.origin.x = _bounds.origin.x / newSize.width;
1591      _bounds.origin.y = _bounds.origin.y / newSize.height;
1592      _bounds.size.width  = _bounds.size.width  / newSize.width;
1593      _bounds.size.height = _bounds.size.height / newSize.height;
1594
1595      _is_rotated_or_scaled_from_base = YES;
1596
1597      if (_coordinates_valid)
1598        {
1599          (*invalidateImp)(self, invalidateSel);
1600        }
1601      [self resetCursorRects];
1602      if (_post_bounds_changes)
1603        {
1604          [nc postNotificationName: NSViewBoundsDidChangeNotification
1605              object: self];
1606        }
1607    }
1608}
1609
1610- (void) rotateByAngle: (CGFloat)angle
1611{
1612  if (angle != 0.0)
1613    {
1614      NSAffineTransform *matrix;
1615      NSRect frame = _frame;
1616
1617      frame.origin = NSMakePoint(0, 0);
1618      if (_boundsMatrix == nil)
1619        {
1620          _boundsMatrix = [NSAffineTransform new];
1621        }
1622      [_boundsMatrix rotateByDegrees: angle];
1623      // Adjust bounds
1624      matrix = [_boundsMatrix copy];
1625      [matrix invert];
1626      [matrix boundingRectFor: frame result: &_bounds];
1627      RELEASE(matrix);
1628
1629      _is_rotated_from_base = _is_rotated_or_scaled_from_base = YES;
1630
1631      if (_coordinates_valid)
1632        {
1633          (*invalidateImp)(self, invalidateSel);
1634        }
1635      [self resetCursorRects];
1636      if (_post_bounds_changes)
1637        {
1638          [nc postNotificationName: NSViewBoundsDidChangeNotification
1639              object: self];
1640        }
1641    }
1642}
1643
1644
1645- (CGFloat) alphaValue
1646{
1647  return _alphaValue;
1648}
1649
1650- (void)setAlphaValue: (CGFloat)alpha
1651{
1652  _alphaValue = alpha;
1653}
1654
1655- (CGFloat) frameCenterRotation
1656{
1657  // FIXME this is dummy, we don't have layers yet
1658  return 0.0;
1659}
1660
1661- (void) setFrameCenterRotation:(CGFloat)rot;
1662{
1663  // FIXME this is dummy, we don't have layers yet
1664  // we probably need a Matrix akin frame rotation.
1665}
1666
1667
1668- (NSRect) centerScanRect: (NSRect)aRect
1669{
1670  NSAffineTransform	*matrix;
1671  CGFloat x_org;
1672  CGFloat y_org;
1673
1674  /*
1675   *	Hmm - we assume that the windows coordinate system is centered on the
1676   *	pixels of the screen - this may not be correct of course.
1677   *	Plus - this is all pretty meaningless is we are not in a window!
1678   */
1679  matrix = [self _matrixToWindow];
1680  aRect.origin = [matrix transformPoint: aRect.origin];
1681  aRect.size = [matrix transformSize: aRect.size];
1682  if (aRect.size.height < 0.0)
1683    {
1684      aRect.size.height = -aRect.size.height;
1685    }
1686
1687  x_org = aRect.origin.x;
1688  y_org = aRect.origin.y;
1689  aRect.origin.x = GSRoundTowardsInfinity(aRect.origin.x);
1690  aRect.origin.y = [self isFlipped] ? GSRoundTowardsNegativeInfinity(aRect.origin.y) : GSRoundTowardsInfinity(aRect.origin.y);
1691  aRect.size.width = GSRoundTowardsInfinity(aRect.size.width + (x_org - aRect.origin.x) / 2.0);
1692  aRect.size.height = GSRoundTowardsInfinity(aRect.size.height + (y_org - aRect.origin.y) / 2.0);
1693
1694  matrix = [self _matrixFromWindow];
1695  aRect.origin = [matrix transformPoint: aRect.origin];
1696  aRect.size = [matrix transformSize: aRect.size];
1697  if (aRect.size.height < 0.0)
1698    {
1699      aRect.size.height = -aRect.size.height;
1700    }
1701
1702  return aRect;
1703}
1704
1705- (NSPoint) convertPoint: (NSPoint)aPoint fromView: (NSView*)aView
1706{
1707  NSPoint inBase;
1708
1709  if (aView == self)
1710    {
1711      return aPoint;
1712    }
1713
1714  if (aView != nil)
1715    {
1716      NSAssert(_window == [aView window], NSInvalidArgumentException);
1717      inBase = [[aView _matrixToWindow] transformPoint: aPoint];
1718    }
1719  else
1720    {
1721      inBase = aPoint;
1722    }
1723
1724  return [[self _matrixFromWindow] transformPoint: inBase];
1725}
1726
1727- (NSPoint) convertPoint: (NSPoint)aPoint toView: (NSView*)aView
1728{
1729  NSPoint inBase;
1730
1731  if (aView == self)
1732    return aPoint;
1733
1734  inBase = [[self _matrixToWindow] transformPoint: aPoint];
1735
1736  if (aView != nil)
1737    {
1738      NSAssert(_window == [aView window], NSInvalidArgumentException);
1739      return [[aView _matrixFromWindow] transformPoint: inBase];
1740    }
1741  else
1742    {
1743      return inBase;
1744    }
1745}
1746
1747
1748/* Helper for -convertRect:fromView: and -convertRect:toView:. */
1749static NSRect
1750convert_rect_using_matrices(NSRect aRect, NSAffineTransform *matrix1,
1751					  NSAffineTransform *matrix2)
1752{
1753  NSRect r;
1754  NSPoint p[4], min, max;
1755  int i;
1756
1757  for (i = 0; i < 4; i++)
1758    p[i] = aRect.origin;
1759  p[1].x += aRect.size.width;
1760  p[2].y += aRect.size.height;
1761  p[3].x += aRect.size.width;
1762  p[3].y += aRect.size.height;
1763
1764  for (i = 0; i < 4; i++)
1765    p[i] = [matrix1 transformPoint: p[i]];
1766
1767  min = max = p[0] = [matrix2 transformPoint: p[0]];
1768  for (i = 1; i < 4; i++)
1769    {
1770      p[i] = [matrix2 transformPoint: p[i]];
1771      min.x = MIN(min.x, p[i].x);
1772      min.y = MIN(min.y, p[i].y);
1773      max.x = MAX(max.x, p[i].x);
1774      max.y = MAX(max.y, p[i].y);
1775    }
1776
1777  r.origin = min;
1778  r.size.width = max.x - min.x;
1779  r.size.height = max.y - min.y;
1780
1781  return r;
1782}
1783
1784/**
1785 * Converts aRect from the coordinate system of aView to the coordinate
1786 * system of the receiver, ie. returns the bounding rectangle in the
1787 * receiver of aRect in aView.
1788 * <br />
1789 * aView and the receiver must be in the same window. If aView is nil,
1790 * converts from the receiver's window's coordinate system.
1791 */
1792- (NSRect) convertRect: (NSRect)aRect fromView: (NSView*)aView
1793{
1794  NSAffineTransform *matrix1, *matrix2;
1795
1796  if (aView == self || _window == nil || (aView != nil && [aView window] == nil))
1797    {
1798      return aRect;
1799    }
1800
1801  if (aView != nil)
1802    {
1803      NSAssert(_window == [aView window], NSInvalidArgumentException);
1804      matrix1 = [aView _matrixToWindow];
1805    }
1806  else
1807    {
1808      matrix1 = [NSAffineTransform transform];
1809    }
1810
1811  matrix2 = [self _matrixFromWindow];
1812
1813  return convert_rect_using_matrices(aRect, matrix1, matrix2);
1814}
1815
1816/**
1817 * Converts aRect from the coordinate system of the receiver to the
1818 * coordinate system of aView, ie. returns the bounding rectangle in
1819 * aView of aRect in the receiver.
1820 * <br />
1821 * aView and the receiver must be in the same window. If aView is nil,
1822 * converts to the receiver's window's coordinate system.
1823 */
1824- (NSRect) convertRect: (NSRect)aRect toView: (NSView*)aView
1825{
1826  NSAffineTransform *matrix1, *matrix2;
1827
1828  if (aView == self || _window == nil || (aView != nil && [aView window] == nil))
1829    {
1830      return aRect;
1831    }
1832
1833  matrix1 = [self _matrixToWindow];
1834
1835  if (aView != nil)
1836    {
1837      NSAssert(_window == [aView window], NSInvalidArgumentException);
1838      matrix2 = [aView _matrixFromWindow];
1839    }
1840  else
1841    {
1842      matrix2 = [NSAffineTransform transform];
1843    }
1844
1845  return convert_rect_using_matrices(aRect, matrix1, matrix2);
1846}
1847
1848- (NSSize) convertSize: (NSSize)aSize fromView: (NSView*)aView
1849{
1850  NSSize inBase;
1851  NSSize inSelf;
1852
1853  if (aView)
1854    {
1855      NSAssert(_window == [aView window], NSInvalidArgumentException);
1856      inBase = [[aView _matrixToWindow] transformSize: aSize];
1857      if (inBase.height < 0.0)
1858	{
1859	  inBase.height = -inBase.height;
1860	}
1861    }
1862  else
1863    {
1864      inBase = aSize;
1865    }
1866
1867  inSelf = [[self _matrixFromWindow] transformSize: inBase];
1868  if (inSelf.height < 0.0)
1869    {
1870      inSelf.height = -inSelf.height;
1871    }
1872  return inSelf;
1873}
1874
1875- (NSSize) convertSize: (NSSize)aSize toView: (NSView*)aView
1876{
1877  NSSize inBase = [[self _matrixToWindow] transformSize: aSize];
1878  if (inBase.height < 0.0)
1879    {
1880      inBase.height = -inBase.height;
1881    }
1882
1883  if (aView)
1884    {
1885      NSSize inOther;
1886      NSAssert(_window == [aView window], NSInvalidArgumentException);
1887      inOther = [[aView _matrixFromWindow] transformSize: inBase];
1888      if (inOther.height < 0.0)
1889	{
1890	  inOther.height = -inOther.height;
1891	}
1892      return inOther;
1893    }
1894  else
1895    {
1896      return inBase;
1897    }
1898}
1899
1900- (NSPoint) convertPointFromBase: (NSPoint)aPoint
1901{
1902  return [self convertPoint: aPoint fromView: nil];
1903}
1904
1905- (NSPoint) convertPointToBase: (NSPoint)aPoint
1906{
1907  return [self convertPoint: aPoint toView: nil];
1908}
1909
1910- (NSRect) convertRectFromBase: (NSRect)aRect
1911{
1912  return [self convertRect: aRect fromView: nil];
1913}
1914
1915- (NSRect) convertRectToBase: (NSRect)aRect
1916{
1917  return [self convertRect: aRect toView: nil];
1918}
1919
1920- (NSSize) convertSizeFromBase: (NSSize)aSize
1921{
1922  return [self convertSize: aSize fromView: nil];
1923}
1924
1925- (NSSize) convertSizeToBase: (NSSize)aSize
1926{
1927  return [self convertSize: aSize toView: nil];
1928}
1929
1930/**
1931 * Sets whether the receiver should post NSViewFrameDidChangeNotification
1932 * when its frame changed.
1933 */
1934- (void) setPostsFrameChangedNotifications: (BOOL)flag
1935{
1936  _post_frame_changes = flag;
1937}
1938
1939/**
1940 * Sets whether the receiver should post NSViewBoundsDidChangeNotification
1941 * when its bound changed.
1942 */
1943- (void) setPostsBoundsChangedNotifications: (BOOL)flag
1944{
1945  _post_bounds_changes = flag;
1946}
1947
1948/*
1949 * resize subviews only if we are supposed to and we have never been rotated
1950 */
1951- (void) resizeSubviewsWithOldSize: (NSSize)oldSize
1952{
1953  if (_rFlags.has_subviews)
1954    {
1955      id e, o;
1956
1957      if (_autoresizes_subviews == NO || _is_rotated_from_base == YES)
1958          return;
1959
1960      e = [_sub_views objectEnumerator];
1961      o = [e nextObject];
1962      while (o)
1963        {
1964          [o resizeWithOldSuperviewSize: oldSize];
1965          o = [e nextObject];
1966        }
1967    }
1968}
1969
1970static void autoresize(CGFloat oldContainerSize,
1971		       CGFloat newContainerSize,
1972		       CGFloat *contentPositionInOut,
1973		       CGFloat *contentSizeInOut,
1974		       BOOL minMarginFlexible,
1975		       BOOL sizeFlexible,
1976		       BOOL maxMarginFlexible)
1977{
1978  const CGFloat change = newContainerSize - oldContainerSize;
1979  const CGFloat oldContentSize = *contentSizeInOut;
1980  const CGFloat oldContentPosition = *contentPositionInOut;
1981  CGFloat flexibleSpace = 0.0;
1982
1983  // See how much flexible space we have to distrube the change over
1984
1985  if (sizeFlexible)
1986    flexibleSpace += oldContentSize;
1987
1988  if (minMarginFlexible)
1989    flexibleSpace += oldContentPosition;
1990
1991  if (maxMarginFlexible)
1992    flexibleSpace += oldContainerSize - oldContentPosition - oldContentSize;
1993
1994
1995  if (flexibleSpace <= 0.0)
1996    {
1997      /**
1998       * In this code path there is no flexible space so we divide
1999       * the available space equally among the flexible portions of the view
2000       */
2001      int subdivisions = (sizeFlexible ? 1 : 0) +
2002	(minMarginFlexible ? 1 : 0) +
2003	(maxMarginFlexible ? 1 : 0);
2004
2005      if (subdivisions > 0)
2006	{
2007	  const CGFloat changePerOption = change / subdivisions;
2008
2009	  if (sizeFlexible)
2010	    {
2011	      *contentSizeInOut += changePerOption;
2012	    }
2013	  if (minMarginFlexible)
2014	    {
2015	      *contentPositionInOut += changePerOption;
2016	    }
2017	}
2018    }
2019  else
2020    {
2021      /**
2022       * In this code path we distribute the change proportionately
2023       * over the flexible spaces
2024       */
2025      const CGFloat changePerPoint = change / flexibleSpace;
2026
2027      if (sizeFlexible)
2028	{
2029          *contentSizeInOut += changePerPoint * oldContentSize;
2030	}
2031      if (minMarginFlexible)
2032	{
2033	  *contentPositionInOut += changePerPoint * oldContentPosition;
2034	}
2035    }
2036}
2037
2038- (void) resizeWithOldSuperviewSize: (NSSize)oldSize
2039{
2040  NSSize superViewFrameSize;
2041  NSRect newFrame = _frame;
2042  NSRect newFrameRounded;
2043
2044  if (_autoresizingMask == NSViewNotSizable)
2045    return;
2046
2047  if (!NSEqualRects(NSZeroRect, _autoresizingFrameError))
2048    {
2049      newFrame.origin.x -= _autoresizingFrameError.origin.x;
2050      newFrame.origin.y -= _autoresizingFrameError.origin.y;
2051      newFrame.size.width -= _autoresizingFrameError.size.width;
2052      newFrame.size.height -= _autoresizingFrameError.size.height;
2053    }
2054
2055  superViewFrameSize = NSMakeSize(0,0);
2056  if (_super_view)
2057    superViewFrameSize = [_super_view frame].size;
2058
2059  autoresize(oldSize.width,
2060	     superViewFrameSize.width,
2061	     &newFrame.origin.x,
2062	     &newFrame.size.width,
2063	     (_autoresizingMask & NSViewMinXMargin),
2064	     (_autoresizingMask & NSViewWidthSizable),
2065	     (_autoresizingMask & NSViewMaxXMargin));
2066
2067  {
2068    const BOOL flipped = (_super_view && [_super_view isFlipped]);
2069
2070    autoresize(oldSize.height,
2071	       superViewFrameSize.height,
2072	       &newFrame.origin.y,
2073	       &newFrame.size.height,
2074	       flipped ? (_autoresizingMask & NSViewMaxYMargin) : (_autoresizingMask & NSViewMinYMargin),
2075	       (_autoresizingMask & NSViewHeightSizable),
2076	       flipped ? (_autoresizingMask & NSViewMinYMargin) : (_autoresizingMask & NSViewMaxYMargin));
2077  }
2078
2079  newFrameRounded = newFrame;
2080
2081  /**
2082   * Perform rounding to pixel-align the frame if we are not rotated
2083   */
2084  if (![self isRotatedFromBase] && [self superview] != nil)
2085    {
2086      newFrameRounded = [[self superview] centerScanRect: newFrameRounded];
2087    }
2088
2089  [self setFrame: newFrameRounded];
2090
2091  _autoresizingFrameError.origin.x = (newFrameRounded.origin.x - newFrame.origin.x);
2092  _autoresizingFrameError.origin.y = (newFrameRounded.origin.y - newFrame.origin.y);
2093  _autoresizingFrameError.size.width = (newFrameRounded.size.width - newFrame.size.width);
2094  _autoresizingFrameError.size.height = (newFrameRounded.size.height - newFrame.size.height);
2095}
2096
2097- (void) _lockFocusInContext: (NSGraphicsContext *)ctxt inRect: (NSRect)rect
2098{
2099  NSRect wrect;
2100  NSInteger window_gstate = 0;
2101
2102  if (viewIsPrinting == nil)
2103    {
2104      NSAssert(_window != nil, NSInternalInconsistencyException);
2105      /* Check for deferred window */
2106      if ((window_gstate = [_window gState]) == 0)
2107        {
2108          return;
2109        }
2110    }
2111
2112  if (ctxt == nil)
2113    {
2114      if (viewIsPrinting != nil)
2115        {
2116          NSPrintOperation *printOp = [NSPrintOperation currentOperation];
2117
2118          ctxt = [printOp context];
2119        }
2120      else
2121        {
2122          ctxt = [_window graphicsContext];
2123        }
2124    }
2125
2126  // Set current context
2127  [NSGraphicsContext saveGraphicsState];
2128  [NSGraphicsContext setCurrentContext: ctxt];
2129
2130  [ctxt lockFocusView: self inRect: rect];
2131  wrect = [self convertRect: rect toView: nil];
2132  NSDebugLLog(@"NSView", @"-lockFocusInRect: %@\n"
2133	      @"\t for view %@ in window %p (%@)\n"
2134	      @"\t frame %@, flip %d",
2135	      NSStringFromRect(wrect),
2136	      self, _window, NSStringFromRect([_window frame]),
2137	      NSStringFromRect(_frame), [self isFlipped]);
2138  if (viewIsPrinting == nil)
2139    {
2140      [_window->_rectsBeingDrawn addObject: [NSValue valueWithRect: wrect]];
2141    }
2142
2143  /* Make sure we don't modify superview's gstate */
2144  DPSgsave(ctxt);
2145
2146  if (viewIsPrinting != nil)
2147    {
2148      if (viewIsPrinting == self)
2149        {
2150          /* Make sure coordinates are valid, then fake that we don't have
2151             a superview so we get printed correctly */
2152          [self _matrixToWindow];
2153          [_matrixToWindow makeIdentityMatrix];
2154        }
2155      else
2156        {
2157          [[self _matrixToWindow] concat];
2158        }
2159
2160      /* Allow subclases to make other modifications */
2161      [self setUpGState];
2162    }
2163  else
2164    {
2165      if (_gstate && !_renew_gstate)
2166        {
2167          DPSsetgstate(ctxt, _gstate);
2168          DPSgsave(ctxt);
2169        }
2170      else
2171        {
2172          // This only works, when the context comes from the window
2173          DPSsetgstate(ctxt, window_gstate);
2174          DPSgsave(ctxt);
2175          [[self _matrixToWindow] concat];
2176
2177          /* Allow subclases to make other modifications */
2178          [self setUpGState];
2179          _renew_gstate = NO;
2180          if (_allocate_gstate)
2181            {
2182              if (_gstate)
2183                {
2184                  GSReplaceGState(ctxt, _gstate);
2185                }
2186              else
2187                {
2188                  _gstate = GSDefineGState(ctxt);
2189                }
2190              /* Balance the previous gsave and install our own gstate */
2191              DPSgrestore(ctxt);
2192              DPSsetgstate(ctxt, _gstate);
2193              DPSgsave(ctxt);
2194            }
2195        }
2196    }
2197
2198  if ([self wantsDefaultClipping])
2199    {
2200      /*
2201       * Clip to the visible rectangle - which will never be greater
2202       * than the bounds of the view. This prevents drawing outside
2203       * our bounds.
2204       */
2205      // Normally the second test is not needed, it can differ only
2206      // when the view is loaded from a NIB file.
2207      if (_is_rotated_from_base && (_boundsMatrix != nil))
2208        {
2209          // When the view is rotated, we clip to the frame.
2210          NSAffineTransform *matrix;
2211          NSRect frame = _frame;
2212          NSBezierPath *bp;
2213
2214          frame.origin = NSMakePoint(0, 0);
2215          bp = [NSBezierPath bezierPathWithRect: frame];
2216
2217          matrix = [_boundsMatrix copy];
2218          [matrix invert];
2219          [bp transformUsingAffineTransform: matrix];
2220          [bp addClip];
2221          RELEASE(matrix);
2222        }
2223      else
2224        {
2225          // FIXME: Should we use _bounds or visibleRect here?
2226          DPSrectclip(ctxt, NSMinX(rect), NSMinY(rect),
2227                      NSWidth(rect), NSHeight(rect));
2228        }
2229    }
2230
2231  /* Tell backends that images are drawn upside down. Obsolete?
2232     This is needed when a backend is able to handle full image transformation. */
2233  GSWSetViewIsFlipped(ctxt, [self isFlipped]);
2234}
2235
2236- (void) _setIgnoresBacking: (BOOL) flag
2237{
2238  _rFlags.ignores_backing = flag;
2239}
2240
2241- (BOOL) _ignoresBacking
2242{
2243  return _rFlags.ignores_backing;
2244}
2245
2246- (void) unlockFocusNeedsFlush: (BOOL)flush
2247{
2248  NSGraphicsContext *ctxt = GSCurrentContext();
2249
2250  NSDebugLLog(@"NSView_details", @"-unlockFocusNeedsFlush: %i for view %@\n",
2251	      flush, self);
2252
2253  if (viewIsPrinting == nil)
2254    {
2255      NSAssert(_window != nil, NSInternalInconsistencyException);
2256      /* Check for deferred window */
2257      if ([_window gState] == 0)
2258        return;
2259
2260      /* Restore our original gstate */
2261      DPSgrestore(ctxt);
2262    }
2263
2264  /* Restore state of nesting lockFocus */
2265  DPSgrestore(ctxt);
2266  if (!_allocate_gstate)
2267    _gstate = 0;
2268
2269  if (viewIsPrinting == nil)
2270    {
2271      NSRect        rect;
2272      if (flush && !_rFlags.ignores_backing)
2273        {
2274          rect = [[_window->_rectsBeingDrawn lastObject] rectValue];
2275          _window->_rectNeedingFlush =
2276              NSUnionRect(_window->_rectNeedingFlush, rect);
2277          _window->_f.needs_flush = YES;
2278        }
2279      [_window->_rectsBeingDrawn removeLastObject];
2280    }
2281  [ctxt unlockFocusView: self needsFlush: YES ];
2282  [NSGraphicsContext restoreGraphicsState];
2283}
2284
2285/**
2286  <p> Tell the view to maintain a private gstate object which
2287  encapsulates all the information about drawing, such as coordinate
2288  transforms, line widths, etc. If you do not invoke this method, a
2289  gstate object is constructed each time the view is lockFocused.
2290  Allocating a private gstate may improve the performance of views
2291  that are focused a lot and have a lot of customized drawing
2292  parameters.  </p>
2293
2294  <p> View subclasses should override the
2295  setUpGstate method to set these custom parameters.
2296  </p>
2297*/
2298- (void) allocateGState
2299{
2300  _allocate_gstate = YES;
2301  _renew_gstate = YES;
2302}
2303
2304/**
2305  Frees the gstate object, if there is one.
2306*/
2307- (void) releaseGState
2308{
2309  if (_allocate_gstate && _gstate &&
2310      _window && ([_window graphicsContext] != nil))
2311    {
2312      GSUndefineGState([_window graphicsContext], _gstate);
2313    }
2314  _gstate = 0;
2315  _allocate_gstate = NO;
2316}
2317
2318/**
2319  Returns an identifier that represents the view's gstate object,
2320  which is used to encapsulate drawing information about the view.
2321  Most of the time a gstate object is created from scratch when the
2322  view is focused, so if the view is not currently focused or
2323  allocateGState has not been called, then this method will return 0.
2324  FIXME: The above is what the OpenStep and Cocoa specification say, but
2325  gState is 0 unless allocateGState has been called.
2326*/
2327- (NSInteger) gState
2328{
2329  if (_allocate_gstate && (!_gstate || _renew_gstate))
2330    {
2331      // Set the gstate by locking and unlocking focus.
2332      [self lockFocus];
2333      [self unlockFocusNeedsFlush: NO];
2334    }
2335
2336  return _gstate;
2337}
2338
2339/**
2340  Invalidates the view's gstate object so it will be set up again
2341  using setUpGState the next time the view is focused.  */
2342- (void) renewGState
2343{
2344  _renew_gstate = YES;
2345  /* Note that the next time we lock focus, we'll realloc a gstate (if
2346     _allocate_gstate). This seems to make sense, and also allows us
2347     to call this method each time we invalidate the coordinates */
2348}
2349
2350/* Overridden by subclasses to setup custom gstate */
2351- (void) setUpGState
2352{
2353}
2354
2355- (void) lockFocusInRect: (NSRect)rect
2356{
2357  [self _lockFocusInContext: nil inRect: rect];
2358}
2359
2360- (void) lockFocus
2361{
2362  [self lockFocusInRect: [self visibleRect]];
2363}
2364
2365- (void) unlockFocus
2366{
2367  [self unlockFocusNeedsFlush: YES];
2368}
2369
2370- (BOOL) lockFocusIfCanDraw
2371{
2372  return [self lockFocusIfCanDrawInContext: nil];
2373}
2374
2375- (BOOL) lockFocusIfCanDrawInContext: (NSGraphicsContext *)context
2376{
2377  if ([self canDraw])
2378    {
2379      [self _lockFocusInContext: context inRect: [self visibleRect]];
2380      return YES;
2381    }
2382  else
2383    {
2384      return NO;
2385    }
2386}
2387
2388- (BOOL) canDraw
2389{
2390  if (((viewIsPrinting != nil) && [self isDescendantOf: viewIsPrinting]) ||
2391      ((_window != nil) && ([_window windowNumber] != 0) &&
2392       ![self isHiddenOrHasHiddenAncestor]))
2393    {
2394      return YES;
2395    }
2396  else
2397    {
2398      return NO;
2399    }
2400}
2401
2402/*
2403 * The following display* methods work based on these invariants:
2404 * - When a view is marked as needing display, all views above it
2405 *   in the hierarchy are marked as well.
2406 * - When a view has an invalid rectangle, all views above it up
2407 *   to the next opaque view also include this invalid rectangle.
2408 *
2409 * After drawing an area in a view give, subviews a chance to draw
2410 * there too.
2411 * When drawing a non-opaque subview we need to make sure any area
2412 * we draw in has been drawn by the opaque superview as well.
2413 *
2414 * When drawing the invalid area of a view, we need to make sure
2415 * that invalid areas in opaque subviews get drawn as well. These
2416 * areas will not be included in the invalid area of the view.
2417 *
2418 * IfNeeded means we only draw if the view is marked as needing display
2419 * and will only draw in the _invalidRect of this view and that of all
2420 * the opaque subviews. For non-opaque subviews we need to draw where
2421 * ever a superview has already drawn.
2422 *
2423 * InRect means we will only draw in this rectangle. If non is given the
2424 * visibleRect gets used.
2425 *
2426 * IgnoringOpacity means we start drawing at the current view. Otherwise
2427 * we go up to the next opaque view.
2428 *
2429 */
2430
2431- (void) display
2432{
2433  [self displayRect: [self visibleRect]];
2434}
2435
2436- (void) displayIfNeeded
2437{
2438  if (_rFlags.needs_display == YES)
2439    {
2440      [self displayIfNeededInRect: [self visibleRect]];
2441    }
2442}
2443
2444- (void) displayIfNeededIgnoringOpacity
2445{
2446  if (_rFlags.needs_display == YES)
2447    {
2448      [self displayIfNeededInRectIgnoringOpacity: [self visibleRect]];
2449    }
2450}
2451
2452- (void) displayIfNeededInRect: (NSRect)aRect
2453{
2454  if (_rFlags.needs_display == YES)
2455    {
2456      if ([self isOpaque] == YES)
2457        {
2458          [self displayIfNeededInRectIgnoringOpacity: aRect];
2459        }
2460      else
2461        {
2462          NSView *firstOpaque = [self opaqueAncestor];
2463
2464          aRect = [firstOpaque convertRect: aRect fromView: self];
2465          [firstOpaque displayIfNeededInRectIgnoringOpacity: aRect];
2466        }
2467    }
2468}
2469
2470- (void) displayIfNeededInRectIgnoringOpacity: (NSRect)aRect
2471{
2472  if (_rFlags.needs_display == YES)
2473    {
2474      NSRect rect;
2475
2476      /*
2477       * Restrict the drawing of self onto the invalid rectangle.
2478       */
2479      rect = NSIntersectionRect(aRect, _invalidRect);
2480      [self displayRectIgnoringOpacity: rect];
2481
2482      /*
2483       * If we still need display after displaying the invalid rectangle,
2484       * this means that some subviews still need to display.
2485       * For opaque subviews their invalid rectangle may even overlap the
2486       * original aRect.
2487       * Display any subview that need display.
2488       */
2489      if (_rFlags.needs_display == YES)
2490        {
2491          NSEnumerator *enumerator = [_sub_views objectEnumerator];
2492          NSView *subview;
2493          BOOL subviewNeedsDisplay = NO;
2494
2495          while ((subview = [enumerator nextObject]) != nil)
2496            {
2497              if (subview->_rFlags.needs_display)
2498                {
2499                  NSRect subviewFrame = [subview _frameExtend];
2500                  NSRect isect;
2501
2502                  isect = NSIntersectionRect(aRect, subviewFrame);
2503                  if (NSIsEmptyRect(isect) == NO)
2504                    {
2505                      isect = [subview convertRect: isect fromView: self];
2506                      [subview displayIfNeededInRectIgnoringOpacity: isect];
2507                    }
2508
2509                  if (subview->_rFlags.needs_display)
2510                    {
2511                      subviewNeedsDisplay = YES;
2512                    }
2513                }
2514            }
2515          /*
2516           * Make sure our needs_display flag matches that of the subviews.
2517           * Only set to NO when there is no _invalidRect.
2518           */
2519          if (NSIsEmptyRect(_invalidRect))
2520            {
2521              _rFlags.needs_display = subviewNeedsDisplay;
2522            }
2523        }
2524    }
2525}
2526
2527/**
2528 * Causes the area of the view specified by aRect to be displayed.
2529 * This is done by moving up the view hierarchy until an opaque view
2530 * is found, then asking that view to update the appropriate area.
2531 */
2532- (void) displayRect: (NSRect)aRect
2533{
2534  if ([self isOpaque] == YES)
2535    {
2536      [self displayRectIgnoringOpacity: aRect];
2537    }
2538  else
2539    {
2540      NSView *firstOpaque = [self opaqueAncestor];
2541
2542      aRect = [firstOpaque convertRect: aRect fromView: self];
2543      [firstOpaque displayRectIgnoringOpacity: aRect];
2544    }
2545}
2546
2547- (void) displayRectIgnoringOpacity: (NSRect)aRect
2548{
2549  [self displayRectIgnoringOpacity: aRect inContext: nil];
2550}
2551
2552- (void) displayRectIgnoringOpacity: (NSRect)aRect
2553                          inContext: (NSGraphicsContext *)context
2554{
2555  NSGraphicsContext *wContext;
2556  BOOL flush = NO;
2557  BOOL subviewNeedsDisplay = NO;
2558
2559  if (![self canDraw])
2560    {
2561      return;
2562    }
2563
2564  wContext = [_window graphicsContext];
2565  if (context == nil)
2566    {
2567      context = wContext;
2568    }
2569
2570  if (context == wContext)
2571    {
2572      NSRect neededRect;
2573      NSRect visibleRect = [self visibleRect];
2574
2575      flush = YES;
2576      [_window disableFlushWindow];
2577      aRect = NSIntersectionRect(aRect, visibleRect);
2578      neededRect = NSIntersectionRect(_invalidRect, visibleRect);
2579
2580      /*
2581       * If the rect we are going to display contains the _invalidRect
2582       * then we can empty _invalidRect. Do this before the drawing,
2583       * as drawRect: may change this value.
2584       * FIXME: If the drawn rectangle cuts of a complete part of the
2585       * _invalidRect, we should try to reduce this.
2586       */
2587      if (NSEqualRects(aRect, NSUnionRect(neededRect, aRect)) == YES)
2588        {
2589          _invalidRect = NSZeroRect;
2590          _rFlags.needs_display = NO;
2591        }
2592    }
2593
2594  if (NSIsEmptyRect(aRect) == NO)
2595    {
2596      /*
2597       * Now we draw this view.
2598       */
2599      [self _lockFocusInContext: context inRect: aRect];
2600      [self drawRect: aRect];
2601      [self unlockFocusNeedsFlush: flush];
2602    }
2603
2604  /*
2605   * Even when aRect is empty we need to loop over the subviews to see,
2606   * if there is anything left to draw.
2607   */
2608  if (_rFlags.has_subviews == YES)
2609    {
2610      NSUInteger count = [_sub_views count];
2611
2612      if (count > 0)
2613        {
2614          NSView *array[count];
2615          NSUInteger i;
2616
2617          [_sub_views getObjects: array];
2618
2619          for (i = 0; i < count; ++i)
2620            {
2621              NSView *subview = array[i];
2622              NSRect subviewFrame = [subview _frameExtend];
2623              NSRect isect;
2624
2625              /*
2626               * Having drawn ourself into the rect, we must make sure that
2627               * subviews overlapping the area are redrawn.
2628               */
2629              isect = NSIntersectionRect(aRect, subviewFrame);
2630              if (NSIsEmptyRect(isect) == NO)
2631                {
2632                  isect = [subview convertRect: isect fromView: self];
2633                  [subview displayRectIgnoringOpacity: isect
2634                                            inContext: context];
2635                }
2636              /*
2637               * Is there still something to draw in the subview?
2638               * This keeps the invariant that views further up are marked
2639               * for redraw when ever a view further down needs to redraw.
2640               */
2641              if (subview->_rFlags.needs_display == YES)
2642                {
2643                  subviewNeedsDisplay = YES;
2644                }
2645            }
2646        }
2647    }
2648
2649  if (context == wContext)
2650    {
2651      if (subviewNeedsDisplay)
2652        {
2653          /*
2654           * If not all subviews have been fully displayed, we cannot turn off
2655           * the 'needs_display' flag. This is to keep the invariant that when
2656           * a view is marked as needing to display, all its ancestors will be
2657           * marked too.
2658           */
2659          _rFlags.needs_display = YES;
2660        }
2661      [_window enableFlushWindow];
2662      [_window flushWindowIfNeeded];
2663    }
2664}
2665
2666/**
2667  This method is invoked to handle drawing inside the view.  The
2668  default NSView's implementation does nothing; subclasses might
2669  override it to draw something inside the view.  Since NSView's
2670  implementation is guaranteed to be empty, you should not call
2671  super's implementation when you override it in subclasses.
2672  drawRect: is invoked when the focus has already been locked on the
2673  view; you can use arbitrary postscript functions in drawRect: to
2674  draw inside your view; the coordinate system in which you draw is
2675  the view's own coordinate system (this means for example that you
2676  should refer to the rectangle covered by the view using its bounds,
2677  and not its frame).  The argument of drawRect: is the rectangle
2678  which needs to be redrawn.  In a lossy implementation, you can
2679  ignore the argument and redraw the whole view; if you are aiming at
2680  performance, you may want to redraw only what is inside the
2681  rectangle which needs to be redrawn; this usually improves drawing
2682  performance considerably.  */
2683- (void) drawRect: (NSRect)rect
2684{}
2685
2686- (NSRect) visibleRect
2687{
2688  if ([self isHiddenOrHasHiddenAncestor])
2689    {
2690      return NSZeroRect;
2691    }
2692
2693  if (_coordinates_valid == NO)
2694    {
2695      [self _rebuildCoordinates];
2696    }
2697  return _visibleRect;
2698}
2699
2700- (BOOL) wantsDefaultClipping
2701{
2702  return YES;
2703}
2704
2705- (BOOL) needsToDrawRect: (NSRect)aRect
2706{
2707  const NSRect *rects;
2708  NSInteger i, count;
2709
2710  [self getRectsBeingDrawn: &rects count: &count];
2711  for (i = 0; i < count; i++)
2712    {
2713      if (NSIntersectsRect(aRect, rects[i]))
2714	return YES;
2715    }
2716  return NO;
2717}
2718
2719- (void) getRectsBeingDrawn: (const NSRect **)rects count: (NSInteger *)count
2720{
2721  // FIXME
2722  static NSRect rect;
2723
2724  rect = [[_window->_rectsBeingDrawn lastObject] rectValue];
2725  rect = [self convertRect: rect fromView: nil];
2726
2727  if (rects != NULL)
2728    {
2729      *rects = &rect;
2730    }
2731
2732  if (count != NULL)
2733    {
2734      *count = 1;
2735    }
2736}
2737
2738- (NSBitmapImageRep *) bitmapImageRepForCachingDisplayInRect: (NSRect)rect
2739{
2740  NSBitmapImageRep *bitmap;
2741
2742  [self lockFocus];
2743  bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect];
2744  [self unlockFocus];
2745
2746  return AUTORELEASE(bitmap);
2747}
2748
2749- (void) cacheDisplayInRect: (NSRect)rect
2750           toBitmapImageRep: (NSBitmapImageRep *)bitmap
2751{
2752  NSDictionary *dict;
2753  NSData *imageData;
2754
2755  [self lockFocus];
2756  dict = [GSCurrentContext() GSReadRect: rect];
2757  [self unlockFocus];
2758  imageData = [dict objectForKey: @"Data"];
2759
2760  if (imageData != nil)
2761    {
2762      // Copy the image data to the bitmap
2763      memcpy([bitmap bitmapData], [imageData bytes], [imageData length]);
2764    }
2765}
2766
2767
2768extern NSThread *GSAppKitThread; /* TODO */
2769
2770/*
2771For -setNeedsDisplay*, the real work is done in the ..._real methods, and
2772the actual public method simply calls it, but makes sure that the call is
2773in the main thread.
2774*/
2775
2776- (void) _setNeedsDisplay_real: (NSNumber *)n
2777{
2778  BOOL flag = [n boolValue];
2779
2780  if (flag)
2781    {
2782      [self setNeedsDisplayInRect: _bounds];
2783    }
2784  else
2785    {
2786      _rFlags.needs_display = NO;
2787      _invalidRect = NSZeroRect;
2788    }
2789}
2790
2791/**
2792 * As an exception to the general rules for threads and gui, this
2793 * method is thread-safe and may be called from any thread. Display
2794 * will always be done in the main thread. (Note that other methods are
2795 * in general not thread-safe; if you want to access other properties of
2796 * views from multiple threads, you need to provide the synchronization.)
2797 */
2798- (void) setNeedsDisplay: (BOOL)flag
2799{
2800  NSNumber *n = [[NSNumber alloc] initWithBool: flag];
2801  if (GSCurrentThread() != GSAppKitThread)
2802    {
2803      NSDebugMLLog (@"MacOSXCompatibility",
2804                    @"setNeedsDisplay: called on secondary thread");
2805      [self performSelectorOnMainThread: @selector(_setNeedsDisplay_real:)
2806            withObject: n
2807            waitUntilDone: NO];
2808    }
2809  else
2810    {
2811      [self _setNeedsDisplay_real: n];
2812    }
2813  DESTROY(n);
2814}
2815
2816
2817- (void) _setNeedsDisplayInRect_real: (NSValue *)v
2818{
2819  NSRect invalidRect = [v rectValue];
2820  NSView *currentView = _super_view;
2821
2822  /*
2823   *	Limit to bounds, combine with old _invalidRect, and then check to see
2824   *	if the result is the same as the old _invalidRect - if it isn't then
2825   *	set the new _invalidRect.
2826   */
2827  invalidRect = NSIntersectionRect(invalidRect, _bounds);
2828  invalidRect = NSUnionRect(_invalidRect, invalidRect);
2829  if (NSEqualRects(invalidRect, _invalidRect) == NO)
2830    {
2831      NSView	*firstOpaque = [self opaqueAncestor];
2832
2833      _rFlags.needs_display = YES;
2834      _invalidRect = invalidRect;
2835      if (firstOpaque == self)
2836        {
2837	  /**
2838	   * Enlarge (if necessary) _invalidRect so it lies on integral device pixels
2839	   */
2840	  const NSRect inBase =  [self convertRectToBase: _invalidRect];
2841	  const NSRect inBaseRounded = NSIntegralRect(inBase);
2842	  _invalidRect = [self convertRectFromBase: inBaseRounded];
2843
2844          [_window setViewsNeedDisplay: YES];
2845        }
2846      else
2847        {
2848          invalidRect = [firstOpaque convertRect: _invalidRect fromView: self];
2849          [firstOpaque setNeedsDisplayInRect: invalidRect];
2850        }
2851    }
2852
2853 /*
2854   * Must make sure that superviews know that we need display.
2855   * NB. we may have been marked as needing display and then moved to another
2856   * parent, so we can't assume that our parent is marked simply because we are.
2857   */
2858  while (currentView)
2859    {
2860      currentView->_rFlags.needs_display = YES;
2861      currentView = currentView->_super_view;
2862    }
2863  // Also mark the window, as this may not happen above
2864  [_window setViewsNeedDisplay: YES];
2865}
2866
2867/**
2868 * Inform the view system that the specified rectangle is invalid and
2869 * requires updating.  This automatically informs any superviews of
2870 * any updating they need to do.
2871 *
2872 * As an exception to the general rules for threads and gui, this
2873 * method is thread-safe and may be called from any thread. Display
2874 * will always be done in the main thread. (Note that other methods are
2875 * in general not thread-safe; if you want to access other properties of
2876 * views from multiple threads, you need to provide the synchronization.)
2877 */
2878- (void) setNeedsDisplayInRect: (NSRect)invalidRect
2879{
2880  NSValue *v;
2881
2882  if (NSIsEmptyRect(invalidRect))
2883    return; // avoid unnecessary work when rectangle is empty
2884
2885  v = [[NSValue alloc]
2886		 initWithBytes: &invalidRect
2887		 objCType: @encode(NSRect)];
2888
2889  if (GSCurrentThread() != GSAppKitThread)
2890    {
2891      NSDebugMLLog (@"MacOSXCompatibility",
2892                    @"setNeedsDisplayInRect: called on secondary thread");
2893      [self performSelectorOnMainThread: @selector(_setNeedsDisplayInRect_real:)
2894            withObject: v
2895            waitUntilDone: NO];
2896    }
2897  else
2898    {
2899      [self _setNeedsDisplayInRect_real: v];
2900    }
2901  DESTROY(v);
2902}
2903
2904+ (NSFocusRingType) defaultFocusRingType
2905{
2906  return NSFocusRingTypeDefault;
2907}
2908
2909- (void) setKeyboardFocusRingNeedsDisplayInRect: (NSRect)rect
2910{
2911  // FIXME For external type special handling is needed
2912  [self setNeedsDisplayInRect: rect];
2913}
2914
2915- (void) setFocusRingType: (NSFocusRingType)focusRingType
2916{
2917  _focusRingType = focusRingType;
2918}
2919
2920- (NSFocusRingType) focusRingType
2921{
2922  return _focusRingType;
2923}
2924
2925/*
2926 * Hidding Views
2927 */
2928- (void) setHidden: (BOOL)flag
2929{
2930  id view;
2931
2932  if (_is_hidden == flag)
2933      return;
2934
2935  _is_hidden = flag;
2936
2937  if (_is_hidden)
2938    {
2939      for (view = [_window firstResponder];
2940           view != nil && [view respondsToSelector: @selector(superview)];
2941           view = [view superview])
2942        {
2943          if (view == self)
2944            {
2945              [_window makeFirstResponder: [self nextValidKeyView]];
2946              break;
2947            }
2948        }
2949      if (_rFlags.has_draginfo)
2950        {
2951          if (_window != nil)
2952            {
2953              NSArray *t = GSGetDragTypes(self);
2954
2955              [GSDisplayServer removeDragTypes: t fromWindow: _window];
2956            }
2957        }
2958      [[self superview] setNeedsDisplay: YES];
2959    }
2960  else
2961    {
2962      if (_rFlags.has_draginfo)
2963        {
2964          if (_window != nil)
2965            {
2966              NSArray *t = GSGetDragTypes(self);
2967
2968              [GSDisplayServer addDragTypes: t toWindow: _window];
2969            }
2970        }
2971      if (_rFlags.has_subviews)
2972        {
2973          // The _visibleRect of subviews will be NSZeroRect, because when they
2974          // were calculated in -[_rebuildCoordinates], they were intersected
2975          // with the result of calling -[visibleRect] on the hidden superview,
2976          // which returns NSZeroRect for hidden views.
2977          //
2978          // So, recalculate the subview coordinates now to make them correct.
2979
2980          [_sub_views makeObjectsPerformSelector:
2981            @selector(_invalidateCoordinates)];
2982        }
2983      [self setNeedsDisplay: YES];
2984    }
2985}
2986
2987- (BOOL) isHidden
2988{
2989  return _is_hidden;
2990}
2991
2992- (BOOL) isHiddenOrHasHiddenAncestor
2993{
2994  return ([self isHidden] || [_super_view isHiddenOrHasHiddenAncestor]);
2995}
2996
2997/*
2998 * Live resize support
2999 */
3000- (BOOL) inLiveResize
3001{
3002  return _in_live_resize;
3003}
3004
3005- (void) viewWillStartLiveResize
3006{
3007  // FIXME
3008  _in_live_resize = YES;
3009}
3010
3011- (void) viewDidEndLiveResize
3012{
3013  // FIXME
3014  _in_live_resize = NO;
3015}
3016
3017- (BOOL) preservesContentDuringLiveResize
3018{
3019  return NO;
3020}
3021
3022- (void) getRectsExposedDuringLiveResize: (NSRect[4])exposedRects count: (NSInteger *)count
3023{
3024  // FIXME
3025  if (count != NULL)
3026    {
3027      *count = 1;
3028    }
3029  exposedRects[0] = _bounds;
3030}
3031
3032- (NSRect) rectPreservedDuringLiveResize
3033{
3034  return NSZeroRect;
3035}
3036
3037/*
3038 * Scrolling
3039 */
3040- (NSRect) adjustScroll: (NSRect)newVisible
3041{
3042  return newVisible;
3043}
3044
3045/**
3046 * Finds the nearest enclosing NSClipView and, if the location of the event
3047 * is outside it, scrolls the NSClipView in the direction of the event. The
3048 * amount scrolled is proportional to how far outside the NSClipView the
3049 * event's location is.
3050 *
3051 * This method is suitable for calling periodically from a modal event
3052 * tracking loop when the mouse is dragged outside the tracking view. The
3053 * suggested period of the calls is 0.1 seconds.
3054 */
3055- (BOOL) autoscroll: (NSEvent*)theEvent
3056{
3057  if (_super_view)
3058    return [_super_view autoscroll: theEvent];
3059
3060  return NO;
3061}
3062
3063- (void) reflectScrolledClipView: (NSClipView*)aClipView
3064{
3065}
3066
3067- (void) scrollClipView: (NSClipView*)aClipView toPoint: (NSPoint)aPoint
3068{
3069  [aClipView scrollToPoint: aPoint];
3070}
3071
3072- (NSClipView*) _enclosingClipView
3073{
3074  static Class clipViewClass;
3075  id aView = [self superview];
3076
3077  if (!clipViewClass)
3078    {
3079      clipViewClass = [NSClipView class];
3080    }
3081
3082  while (aView != nil)
3083    {
3084      if ([aView isKindOfClass: clipViewClass])
3085	{
3086	  break;
3087	}
3088      aView = [aView superview];
3089    }
3090
3091  return aView;
3092}
3093
3094- (void) scrollPoint: (NSPoint)aPoint
3095{
3096  NSClipView *s = [self _enclosingClipView];
3097
3098  if (s == nil)
3099    return;
3100
3101  aPoint = [self convertPoint: aPoint toView: s];
3102  if (NSEqualPoints(aPoint, [s bounds].origin) == NO)
3103    {
3104      [s scrollToPoint: aPoint];
3105    }
3106}
3107
3108/**
3109   Copy on scroll method, should be called from [NSClipView setBoundsOrigin].
3110 */
3111- (void) scrollRect: (NSRect)aRect by: (NSSize)delta
3112{
3113  NSPoint destPoint;
3114
3115  aRect = NSIntersectionRect(aRect, _bounds);   // Don't copy stuff outside.
3116  destPoint = aRect.origin;
3117  destPoint.x += delta.width;
3118  destPoint.y += delta.height;
3119  if ([self isFlipped])
3120    {
3121      destPoint.y += aRect.size.height;
3122    }
3123
3124  //NSLog(@"destPoint %@ in %@", NSStringFromPoint(destPoint), NSStringFromRect(_bounds));
3125
3126  [self lockFocus];
3127  //NSCopyBits(0, aRect, destPoint);
3128  NSCopyBits([[self window] gState], [self convertRect: aRect toView: nil], destPoint);
3129  [self unlockFocus];
3130}
3131
3132/**
3133Scrolls the nearest enclosing clip view the minimum required distance
3134necessary to make aRect (or as much of it possible) in the receiver visible.
3135Returns YES iff any scrolling was done.
3136*/
3137- (BOOL) scrollRectToVisible: (NSRect)aRect
3138{
3139  NSClipView *s = [self _enclosingClipView];
3140
3141  if (s != nil)
3142    {
3143      NSRect	vRect = [s documentVisibleRect];
3144      NSPoint	aPoint = vRect.origin;
3145      // Ok we assume that the rectangle is origined at the bottom left
3146      // and goes to the top and right as it grows in size for the naming
3147      // of these variables
3148      CGFloat ldiff, rdiff, tdiff, bdiff;
3149
3150      if (vRect.size.width == 0 && vRect.size.height == 0)
3151	return NO;
3152
3153      aRect = [self convertRect: aRect toView: [s documentView]];
3154
3155      // Find the differences on each side.
3156      ldiff = NSMinX(vRect) - NSMinX(aRect);
3157      rdiff = NSMaxX(aRect) - NSMaxX(vRect);
3158      bdiff = NSMinY(vRect) - NSMinY(aRect);
3159      tdiff = NSMaxY(aRect) - NSMaxY(vRect);
3160
3161      // If the diff's have the same sign then nothing needs to be scrolled
3162      if ((ldiff * rdiff) >= 0.0) ldiff = rdiff = 0.0;
3163      if ((bdiff * tdiff) >= 0.0) bdiff = tdiff = 0.0;
3164
3165      // Move the smallest difference
3166      aPoint.x += (fabs(ldiff) < fabs(rdiff)) ? (-ldiff) : rdiff;
3167      aPoint.y += (fabs(bdiff) < fabs(tdiff)) ? (-bdiff) : tdiff;
3168
3169      if (aPoint.x != vRect.origin.x || aPoint.y != vRect.origin.y)
3170	{
3171	  aPoint = [[s documentView] convertPoint: aPoint toView: s];
3172	  [s scrollToPoint: aPoint];
3173	  return YES;
3174	}
3175    }
3176  return NO;
3177}
3178
3179- (NSScrollView*) enclosingScrollView
3180{
3181  static Class scrollViewClass;
3182  id	aView = [self superview];
3183
3184  if (!scrollViewClass)
3185    {
3186      scrollViewClass = [NSScrollView class];
3187    }
3188
3189  while (aView != nil)
3190    {
3191      if ([aView isKindOfClass: scrollViewClass])
3192	{
3193	  break;
3194	}
3195      aView = [aView superview];
3196    }
3197
3198  return aView;
3199}
3200
3201/*
3202 * Managing the Cursor
3203 *
3204 * We use the tracking rectangle class to maintain the cursor rects
3205 */
3206- (void) addCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject
3207{
3208  if (_window != nil)
3209    {
3210      GSTrackingRect	*m;
3211
3212      aRect = [self convertRect: aRect toView: nil];
3213      m = [rectClass allocWithZone: NSDefaultMallocZone()];
3214      m = [m initWithRect: aRect
3215		      tag: 0
3216		    owner: RETAIN(anObject)
3217		 userData: NULL
3218		   inside: YES];
3219      [_cursor_rects addObject: m];
3220      RELEASE(m);
3221      _rFlags.has_currects = 1;
3222      _rFlags.valid_rects = 1;
3223    }
3224}
3225
3226- (void) discardCursorRects
3227{
3228  if (_rFlags.has_currects != 0)
3229    {
3230      NSUInteger count = [_cursor_rects count];
3231
3232      if (count > 0)
3233        {
3234	  GSTrackingRect *rects[count];
3235
3236	  [_cursor_rects getObjects: rects];
3237	  if (_rFlags.valid_rects != 0)
3238	    {
3239	      NSPoint loc = _window->_lastPoint;
3240	      NSUInteger i;
3241
3242	      for (i = 0; i < count; ++i)
3243		{
3244		  GSTrackingRect *r = rects[i];
3245		  if (NSMouseInRect(loc, r->rectangle, NO))
3246		    {
3247		      [r->owner mouseExited: nil];
3248		    }
3249		  [r invalidate];
3250		}
3251	      _rFlags.valid_rects = 0;
3252	    }
3253	  while (count-- > 0)
3254	    {
3255	      RELEASE([rects[count] owner]);
3256	    }
3257	  [_cursor_rects removeAllObjects];
3258	}
3259      _rFlags.has_currects = 0;
3260    }
3261}
3262
3263- (void) removeCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject
3264{
3265  id e = [_cursor_rects objectEnumerator];
3266  GSTrackingRect	*o;
3267  NSCursor		*c;
3268  NSPoint loc = [_window mouseLocationOutsideOfEventStream];
3269
3270  /* Base remove test upon cursor object */
3271  o = [e nextObject];
3272  while (o)
3273    {
3274      c = [o owner];
3275      if (c == anObject)
3276	{
3277	  if (NSMouseInRect(loc, o->rectangle, NO))
3278	    {
3279	      [c mouseExited: nil];
3280	    }
3281	  [o invalidate];
3282	  [_cursor_rects removeObject: o];
3283	  if ([_cursor_rects count] == 0)
3284	    {
3285	      _rFlags.has_currects = 0;
3286	      _rFlags.valid_rects = 0;
3287	    }
3288	  RELEASE(c);
3289	  break;
3290	}
3291      else
3292	{
3293	  o = [e nextObject];
3294	}
3295    }
3296}
3297
3298- (void) resetCursorRects
3299{
3300}
3301
3302static NSView* findByTag(NSView *view, NSInteger aTag, NSUInteger *level)
3303{
3304  NSUInteger i, count;
3305  NSArray *sub = [view subviews];
3306
3307  count = [sub count];
3308  if (count > 0)
3309    {
3310      NSView	*array[count];
3311
3312      [sub getObjects: array];
3313
3314      for (i = 0; i < count; i++)
3315	{
3316	  if ([array[i] tag] == aTag)
3317	    return array[i];
3318	}
3319      *level += 1;
3320      for (i = 0; i < count; i++)
3321	{
3322	  NSView	*v;
3323
3324	  v = findByTag(array[i], aTag, level);
3325	  if (v != nil)
3326	    return v;
3327	}
3328      *level -= 1;
3329    }
3330  return nil;
3331}
3332
3333- (id) viewWithTag: (NSInteger)aTag
3334{
3335  NSView	*view = nil;
3336
3337  /*
3338   * If we have the specified tag - return self.
3339   */
3340  if ([self tag] == aTag)
3341    {
3342      view = self;
3343    }
3344  else if (_rFlags.has_subviews)
3345    {
3346      NSUInteger count = [_sub_views count];
3347
3348      if (count > 0)
3349	{
3350	  NSView *array[count];
3351	  NSUInteger i;
3352
3353	  [_sub_views getObjects: array];
3354
3355	  /*
3356	   * Quick check to see if any of our direct descendents has the tag.
3357	   */
3358	  for (i = 0; i < count; i++)
3359	    {
3360	      NSView *subView = array[i];
3361
3362	      if ([subView tag] == aTag)
3363	        {
3364		  view = subView;
3365		  break;
3366		}
3367	    }
3368
3369	  if (view == nil)
3370	    {
3371	      NSUInteger level = 0xffffffff;
3372
3373	      /*
3374	       * Ok - do it the long way - search the whole tree for each of
3375	       * our descendents and see which has the closest view matching
3376	       * the tag.
3377	       */
3378	      for (i = 0; i < count; i++)
3379		{
3380		  NSUInteger l = 0;
3381		  NSView *v;
3382
3383		  v = findByTag(array[i], aTag, &l);
3384
3385		  if (v != nil && l < level)
3386		    {
3387		      view = v;
3388		      level = l;
3389		    }
3390		}
3391	    }
3392	}
3393    }
3394  return view;
3395}
3396
3397/*
3398 * Aiding Event Handling
3399 */
3400
3401/**
3402 * Returns YES if the view object will accept the first
3403 * click received when in an inactive window, and NO
3404 * otherwise.
3405 */
3406- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent
3407{
3408  return NO;
3409}
3410
3411/**
3412 * Returns the subview, lowest in the receiver's hierarchy, which
3413 * contains aPoint, or nil if there is no such view.
3414 */
3415- (NSView*) hitTest: (NSPoint)aPoint
3416{
3417  NSPoint p;
3418  NSView *v = nil, *w;
3419
3420  /* If not within our frame then it can't be a hit.
3421
3422  As a special case, always assume that it's a hit if our _super_view is nil,
3423  ie. if we're the top-level view in a window.
3424  */
3425
3426  if ([self isHidden])
3427    {
3428      return nil;
3429    }
3430
3431  if (_is_rotated_or_scaled_from_base)
3432    {
3433      p = [self convertPoint: aPoint fromView: _super_view];
3434      if (!NSPointInRect (p, _bounds))
3435        {
3436          return nil;
3437        }
3438    }
3439  else if (_super_view && ![_super_view mouse: aPoint inRect: _frame])
3440    {
3441      return nil;
3442    }
3443  else
3444    {
3445      p = [self convertPoint: aPoint fromView: _super_view];
3446    }
3447
3448  if (_rFlags.has_subviews)
3449    {
3450      NSUInteger count;
3451
3452      count = [_sub_views count];
3453      if (count > 0)
3454        {
3455          NSView *array[count];
3456
3457          [_sub_views getObjects: array];
3458
3459          while (count > 0)
3460            {
3461              w = array[--count];
3462              v = [w hitTest: p];
3463              if (v)
3464                break;
3465            }
3466        }
3467    }
3468  /*
3469   * mouse is either in the subview or within self
3470   */
3471  if (v)
3472    return v;
3473  else
3474    return self;
3475}
3476
3477/**
3478 * Returns whether or not aPoint lies within aRect.
3479 */
3480- (BOOL) mouse: (NSPoint)aPoint  inRect: (NSRect)aRect
3481{
3482  return NSMouseInRect (aPoint, aRect, [self isFlipped]);
3483}
3484
3485- (BOOL) performKeyEquivalent: (NSEvent*)theEvent
3486{
3487  NSUInteger i;
3488
3489  for (i = 0; i < [_sub_views count]; i++)
3490    if ([[_sub_views objectAtIndex: i] performKeyEquivalent: theEvent] == YES)
3491      return YES;
3492  return NO;
3493}
3494
3495- (BOOL) performMnemonic: (NSString *)aString
3496{
3497  NSUInteger i;
3498
3499  for (i = 0; i < [_sub_views count]; i++)
3500    if ([[_sub_views objectAtIndex: i] performMnemonic: aString] == YES)
3501      return YES;
3502  return NO;
3503}
3504
3505- (BOOL) mouseDownCanMoveWindow
3506{
3507  return ![self isOpaque];
3508}
3509
3510- (void) removeTrackingRect: (NSTrackingRectTag)tag
3511{
3512  NSUInteger i, j;
3513  GSTrackingRect	*m;
3514
3515  j = [_tracking_rects count];
3516  for (i = 0;i < j; ++i)
3517    {
3518      m = (GSTrackingRect*)[_tracking_rects objectAtIndex: i];
3519      if ([m tag] == tag)
3520	{
3521	  [m invalidate];
3522	  [_tracking_rects removeObjectAtIndex: i];
3523	  if ([_tracking_rects count] == 0)
3524	    {
3525	      _rFlags.has_trkrects = 0;
3526	    }
3527	  return;
3528	}
3529    }
3530}
3531
3532- (BOOL) shouldDelayWindowOrderingForEvent: (NSEvent*)anEvent
3533{
3534  return NO;
3535}
3536
3537- (NSTrackingRectTag) addTrackingRect: (NSRect)aRect
3538				owner: (id)anObject
3539			     userData: (void*)data
3540			 assumeInside: (BOOL)flag
3541{
3542  NSTrackingRectTag	t;
3543  NSUInteger		i, j;
3544  GSTrackingRect	*m;
3545
3546  t = 0;
3547  j = [_tracking_rects count];
3548  for (i = 0; i < j; ++i)
3549    {
3550      m = (GSTrackingRect*)[_tracking_rects objectAtIndex: i];
3551      if ([m tag] > t)
3552	t = [m tag];
3553    }
3554  ++t;
3555
3556  m = [[rectClass alloc] initWithRect: aRect
3557				  tag: t
3558				owner: anObject
3559			     userData: data
3560			       inside: flag];
3561  [_tracking_rects addObject: m];
3562  RELEASE(m);
3563  _rFlags.has_trkrects = 1;
3564  return t;
3565}
3566
3567-(BOOL) needsPanelToBecomeKey
3568{
3569  return NO;
3570}
3571
3572
3573/**
3574 * <p>The effect of the -setNextKeyView: method is to set aView to be the
3575 * value returned by subsequent calls to the receivers -nextKeyView method.
3576 * This also has the effect of setting the previous key view of aView,
3577 * so that subsequent calls to its -previousKeyView method will return
3578 * the receiver.
3579 * </p>
3580 * <p>As a special case, if you pass nil as aView then the -previousKeyView
3581 * of the receivers current -nextKeyView is set to nil as well as the
3582 * receivers -nextKeyView being set to nil.<br />
3583 * This behavior provides MacOS-X compatibility.
3584 * </p>
3585 * <p>If you pass a non-view object other than nil, an
3586 * NSInternaInconsistencyException is raised.
3587 * </p>
3588 * <p><strong>NB</strong> This method does <em>NOT</em> cause aView to be
3589 * retained, and if aView is deallocated, the [NSView-dealloc] method will
3590 * automatically remove it from the key view chain it is in.
3591 * </p>
3592 * <p>For keyboard navigation, views are linked together in a chain, so that
3593 * the current first responder view can be changed by stepping backward
3594 * and forward in that chain.  This is the method for building and modifying
3595 * that chain.
3596 * </p>
3597 * <p>The MacOS-X documentation refers to this chain as a <em>loop</em>, but
3598 * the actual implementation is not a loop at all (except as a special case
3599 * when you make the chain into a loop).  In fact, while each view may have
3600 * only zero or one <em>next</em> view, and zero or one <em>previous</em>
3601 * view, several views may have their <em>next</em> view set to a single
3602 * view and/or their <em>previous</em> views set to a single view.  So the
3603 * actual setup is a directed graph rather than a loop.
3604 * </p>
3605 * <p>While a directed graph is a very powerful and flexible way of managing
3606 * the way views get keyboard focus in response to  tabs etc, it can be
3607 * confusing if misused.  It is probably best therefore, to set your views
3608 * up as a single loop within each window.
3609 * </p>
3610 * <example>
3611 *   [a setNextKeyView: b];
3612 *   [b setNextKeyView: c];
3613 *   [c setNextKeyView: d];
3614 *   [d setNextKeyView: a];
3615 * </example>
3616 */
3617- (void) setNextKeyView: (NSView *)aView
3618{
3619  NSView	*tmp;
3620  NSUInteger	count;
3621
3622  if (aView != nil && [aView isKindOfClass: viewClass] == NO)
3623    {
3624      [NSException raise: NSInternalInconsistencyException
3625	format: @"[NSView -setNextKeyView:] passed non-view object %@", aView];
3626    }
3627
3628  if (aView == nil)
3629    {
3630      if (nKV(self) != 0)
3631	{
3632	  tmp = GSIArrayItemAtIndex(nKV(self), 0).obj;
3633	  if (tmp != nil)
3634	    {
3635	      /*
3636	       * Remove all reference to self from our next key view.
3637	       */
3638	      if (pKV(tmp) != 0)
3639		{
3640		  count = GSIArrayCount(pKV(tmp));
3641		  while (count-- > 1)
3642		    {
3643		      if (GSIArrayItemAtIndex(pKV(tmp), count).obj == self)
3644			{
3645			  GSIArrayRemoveItemAtIndex(pKV(tmp), count);
3646			}
3647		    }
3648		  if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self)
3649		    {
3650		      GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0);
3651		    }
3652		}
3653	      /*
3654	       * Clear link to the next key view.
3655	       */
3656	      GSIArraySetItemAtIndex(nKV(self), (GSIArrayItem)nil, 0);
3657	    }
3658	}
3659      return;
3660    }
3661
3662  if (nKV(self) == 0)
3663    {
3664      /*
3665       * Create array and ensure that it has a nil item at index 0 ...
3666       * so we always have room for the pointer to the next view.
3667       */
3668      _nextKeyView = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
3669      GSIArrayInitWithZoneAndCapacity(nKV(self), NSDefaultMallocZone(), 1);
3670      GSIArrayAddItem(nKV(self), (GSIArrayItem)nil);
3671    }
3672  else
3673    {
3674      /* A safety measure against recursion.  */
3675      tmp = GSIArrayItemAtIndex(nKV(self), 0).obj;
3676      if (tmp == aView)
3677	{
3678	  return;
3679	}
3680    }
3681
3682  if (pKV(aView) == 0)
3683    {
3684      /*
3685       * Create array and ensure that it has a nil item at index 0 ...
3686       * so we always have room for the pointer to the previous view.
3687       */
3688      aView->_previousKeyView = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
3689      GSIArrayInitWithZoneAndCapacity(pKV(aView), NSDefaultMallocZone(), 1);
3690      GSIArrayAddItem(pKV(aView), (GSIArrayItem)nil);
3691    }
3692
3693  /*
3694   * Tell the old previous view of aView that aView no longer points to it.
3695   */
3696  tmp = GSIArrayItemAtIndex(pKV(aView), 0).obj;
3697  if (tmp != nil)
3698    {
3699      count = GSIArrayCount(nKV(tmp));
3700      while (count-- > 1)
3701	{
3702	  if (GSIArrayItemAtIndex(nKV(tmp), count).obj == aView)
3703	    {
3704	      GSIArrayRemoveItemAtIndex(nKV(tmp), count);
3705	    }
3706	}
3707      /*
3708       * If the view still points to aView, make a note of it in the
3709       * 'previous' array of aView while making space for the new link.
3710       */
3711      if (GSIArrayItemAtIndex(nKV(tmp), 0).obj == aView)
3712	{
3713	  GSIArrayInsertItem(pKV(aView), (GSIArrayItem)nil, 0);
3714	}
3715    }
3716
3717  /*
3718   * Set up 'previous' link in aView to point to us.
3719   */
3720  GSIArraySetItemAtIndex(pKV(aView), (GSIArrayItem)((id)self), 0);
3721
3722  /*
3723   * Tell our current 'next' view that we are no longer pointing to it.
3724   */
3725  tmp = GSIArrayItemAtIndex(nKV(self), 0).obj;
3726  if (tmp != nil)
3727    {
3728      count = GSIArrayCount(pKV(tmp));
3729      while (count-- > 1)
3730	{
3731	  if (GSIArrayItemAtIndex(pKV(tmp), count).obj == self)
3732	    {
3733	      GSIArrayRemoveItemAtIndex(pKV(tmp), count);
3734	    }
3735	}
3736      if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self)
3737	{
3738	  GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0);
3739	}
3740    }
3741
3742  /*
3743   * Set up 'next' link to point to aView.
3744   */
3745  GSIArraySetItemAtIndex(nKV(self), (GSIArrayItem)((id)aView), 0);
3746}
3747
3748/**
3749 * Returns the next view after the receiver in the key view chain.<br />
3750 * Returns nil if there is no view after the receiver.<br />
3751 * The next view is set up using the -setNextKeyView: method.<br />
3752 * The key view chain is used to determine the order in which views become
3753 * first responder when using keyboard navigation.
3754 */
3755- (NSView *) nextKeyView
3756{
3757  if (nKV(self) == 0)
3758    {
3759      return nil;
3760    }
3761  return GSIArrayItemAtIndex(nKV(self), 0).obj;
3762}
3763
3764/**
3765 * Returns the first available view after the receiver which is
3766 * actually able to become first responder. See -nextKeyView and
3767 * [NSResponder-acceptsFirstResponder]
3768 */
3769- (NSView *) nextValidKeyView
3770{
3771  NSView *theView;
3772
3773  theView = [self nextKeyView];
3774  while (1)
3775    {
3776      if ((theView == nil) || (theView == self) ||
3777	  [theView canBecomeKeyView])
3778	{
3779	  return theView;
3780	}
3781      theView = [theView nextKeyView];
3782    }
3783}
3784
3785/**
3786 * GNUstep addition ... a conveninece method to insert a view in the
3787 * key view chain before the receiver, using the -previousKeyView and
3788 * -setNextKeyView: methods.
3789 */
3790- (void) setPreviousKeyView: (NSView *)aView
3791{
3792  NSView	*p = [self previousKeyView];
3793
3794  if (aView == p || aView == self)
3795    {
3796      return;
3797    }
3798  [p setNextKeyView: aView];
3799  [aView setNextKeyView: self];
3800}
3801
3802/**
3803 * Returns the view before the receiver in the key view chain.<br />
3804 * Returns nil if there is no view before the receiver in the chain.<br />
3805 * The previous view of the receiver was set up by passing it as the
3806 * argument to a call of -setNextKeyView: on that view.<br />
3807 * The key view chain is used to determine the order in which views become
3808 * first responder when using keyboard navigation.
3809 */
3810- (NSView *) previousKeyView
3811{
3812  if (pKV(self) == 0)
3813    {
3814      return nil;
3815    }
3816  return GSIArrayItemAtIndex(pKV(self), 0).obj;
3817}
3818
3819/**
3820 * Returns the first available view before the receiver which is
3821 * actually able to become first responder. See -nextKeyView and
3822 * [NSResponder-acceptsFirstResponder]
3823 */
3824- (NSView *) previousValidKeyView
3825{
3826  NSView *theView;
3827
3828  theView = [self previousKeyView];
3829  while (1)
3830    {
3831      if ((theView == nil) || (theView == self) ||
3832	  [theView canBecomeKeyView])
3833	{
3834	  return theView;
3835	}
3836      theView = [theView previousKeyView];
3837    }
3838}
3839
3840- (BOOL) canBecomeKeyView
3841{
3842  // FIXME
3843  return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor];
3844}
3845
3846/*
3847 * Dragging
3848 */
3849- (BOOL) dragFile: (NSString*)filename
3850	 fromRect: (NSRect)rect
3851	slideBack: (BOOL)slideFlag
3852	    event: (NSEvent*)event
3853{
3854  NSImage *anImage = [[NSWorkspace sharedWorkspace] iconForFile: filename];
3855  NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
3856
3857  if (anImage == nil)
3858    return NO;
3859
3860  [pboard declareTypes: [NSArray arrayWithObject: NSFilenamesPboardType]
3861	  owner: self];
3862  if (![pboard setPropertyList: [NSArray arrayWithObject: filename]
3863	       forType: NSFilenamesPboardType])
3864    return NO;
3865
3866  [self dragImage: anImage
3867	at: rect.origin
3868	offset: NSMakeSize(0, 0)
3869	event: event
3870	pasteboard: pboard
3871	source: self
3872	slideBack: slideFlag];
3873  return YES;
3874}
3875
3876- (void) dragImage: (NSImage*)anImage
3877		at: (NSPoint)viewLocation
3878	    offset: (NSSize)initialOffset
3879	     event: (NSEvent*)event
3880	pasteboard: (NSPasteboard*)pboard
3881	    source: (id)sourceObject
3882	 slideBack: (BOOL)slideFlag
3883{
3884  [_window dragImage: anImage
3885	   at: [self convertPoint: viewLocation toView: nil]
3886	   offset: initialOffset
3887	   event: event
3888	   pasteboard: pboard
3889	   source: sourceObject
3890	   slideBack: slideFlag];
3891}
3892
3893/**
3894 * Registers the fact that the receiver should accept dragged data
3895 * of any of the specified types.  You need to do this if you want
3896 * your view to support drag and drop.
3897 */
3898- (void) registerForDraggedTypes: (NSArray*)newTypes
3899{
3900  NSArray	*o;
3901  NSArray	*t;
3902
3903  if (newTypes == nil || [newTypes count] == 0)
3904    [NSException raise: NSInvalidArgumentException
3905		format: @"Types information missing"];
3906
3907  /*
3908   * Get the old drag types for this view if we need to tell the context
3909   * to change the registered types for the window.
3910   */
3911  if (_rFlags.has_draginfo == 1 && _window != nil)
3912    {
3913      o = TEST_RETAIN(GSGetDragTypes(self));
3914    }
3915  else
3916    {
3917      o = nil;
3918    }
3919
3920  t = GSSetDragTypes(self, newTypes);
3921  _rFlags.has_draginfo = 1;
3922  if (_window != nil)
3923    {
3924      // Remove the old types first, that way overlapping types stay assigned.
3925      if (o != nil)
3926	{
3927	  [GSDisplayServer removeDragTypes: o fromWindow: _window];
3928	}
3929      [GSDisplayServer addDragTypes: t toWindow: _window];
3930    }
3931  TEST_RELEASE(o);
3932}
3933
3934- (void) unregisterDraggedTypes
3935{
3936  if (_rFlags.has_draginfo)
3937    {
3938      if (_window != nil)
3939	{
3940	  NSArray		*t = GSGetDragTypes(self);
3941
3942	  [GSDisplayServer removeDragTypes: t fromWindow: _window];
3943	}
3944      GSRemoveDragTypes(self);
3945      _rFlags.has_draginfo = 0;
3946    }
3947}
3948
3949- (NSArray *) registeredDraggedTypes
3950{
3951  return GSGetDragTypes(self);
3952}
3953
3954- (BOOL) dragPromisedFilesOfTypes: (NSArray *)typeArray
3955                         fromRect: (NSRect)aRect
3956                           source: (id)sourceObject
3957                        slideBack: (BOOL)slideBack
3958                            event: (NSEvent *)theEvent
3959{
3960  // FIXME: Where to get the image from?
3961  NSImage *anImage = nil;
3962  NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
3963
3964  if (anImage == nil)
3965    return NO;
3966
3967  [pboard declareTypes: [NSArray arrayWithObject: NSFilesPromisePboardType]
3968	  owner: sourceObject];
3969  // FIXME: Not sure if this is correct.
3970  if (![pboard setPropertyList: typeArray
3971	       forType: NSFilesPromisePboardType])
3972    return NO;
3973
3974  [self dragImage: anImage
3975	at: aRect.origin
3976	offset: NSMakeSize(0, 0)
3977	event: theEvent
3978	pasteboard: pboard
3979	source: sourceObject
3980	slideBack: slideBack];
3981  return YES;
3982}
3983
3984/*
3985 * Printing
3986 */
3987- (void) fax: (id)sender
3988{
3989  NSPrintInfo *aPrintInfo = [NSPrintInfo sharedPrintInfo];
3990
3991  [aPrintInfo setJobDisposition: NSPrintFaxJob];
3992  [[NSPrintOperation printOperationWithView: self
3993		     printInfo: aPrintInfo] runOperation];
3994}
3995
3996- (void) print: (id)sender
3997{
3998  [[NSPrintOperation printOperationWithView: self] runOperation];
3999}
4000
4001- (NSData*) dataWithEPSInsideRect: (NSRect)aRect
4002{
4003  NSMutableData *data = [NSMutableData data];
4004
4005  if ([[NSPrintOperation EPSOperationWithView: self
4006			 insideRect: aRect
4007			 toData: data] runOperation])
4008    {
4009      return data;
4010    }
4011  else
4012    {
4013      return nil;
4014    }
4015}
4016
4017- (void) writeEPSInsideRect: (NSRect)rect
4018	       toPasteboard: (NSPasteboard*)pasteboard
4019{
4020  NSData *data = [self dataWithEPSInsideRect: rect];
4021
4022  if (data != nil)
4023    [pasteboard setData: data
4024		forType: NSPostScriptPboardType];
4025}
4026
4027- (NSData *) dataWithPDFInsideRect: (NSRect)aRect
4028{
4029  NSMutableData *data = [NSMutableData data];
4030
4031  if ([[NSPrintOperation PDFOperationWithView: self
4032			 insideRect: aRect
4033			 toData: data] runOperation])
4034    {
4035      return data;
4036    }
4037  else
4038    {
4039      return nil;
4040    }
4041}
4042
4043- (void) writePDFInsideRect: (NSRect)aRect
4044	       toPasteboard: (NSPasteboard *)pboard
4045{
4046  NSData *data = [self dataWithPDFInsideRect: aRect];
4047
4048  if (data != nil)
4049    [pboard setData: data
4050	    forType: NSPDFPboardType];
4051}
4052
4053- (NSString *) printJobTitle
4054{
4055  id doc;
4056  NSString *title;
4057  doc = [[NSDocumentController sharedDocumentController] documentForWindow:
4058							   [self window]];
4059  if (doc)
4060    title = [doc displayName];
4061  else
4062    title = [[self window] title];
4063  return title;
4064}
4065
4066/*
4067 * Pagination
4068 */
4069- (void) adjustPageHeightNew: (CGFloat*)newBottom
4070			 top: (CGFloat)oldTop
4071		      bottom: (CGFloat)oldBottom
4072		       limit: (CGFloat)bottomLimit
4073{
4074  CGFloat bottom = oldBottom;
4075
4076  if (_rFlags.has_subviews)
4077    {
4078      id e, o;
4079
4080      e = [_sub_views objectEnumerator];
4081      while ((o = [e nextObject]) != nil)
4082	{
4083          // FIXME: We have to convert this values for the subclass
4084
4085	  CGFloat oTop, oBottom, oLimit;
4086	  /* Don't ask me why, but gcc-2.91.66 crashes if we use
4087	     NSMakePoint in the following expressions.  We avoid this
4088	     compiler internal bug by using an auxiliary aPoint
4089	     variable, and setting it manually to the NSPoints we
4090	     need.  */
4091	  {
4092	    NSPoint aPoint = {0, oldTop};
4093	    oTop = ([self convertPoint: aPoint  toView: o]).y;
4094	  }
4095
4096	  {
4097	    NSPoint aPoint = {0, bottom};
4098	    oBottom = ([self convertPoint: aPoint  toView: o]).y;
4099	  }
4100
4101	  {
4102	    NSPoint aPoint = {0, bottomLimit};
4103	    oLimit = ([self convertPoint: aPoint  toView: o]).y;
4104	  }
4105
4106	  [o adjustPageHeightNew: &oBottom
4107	     top: oTop
4108	     bottom: oBottom
4109	     limit: oLimit];
4110
4111	  {
4112	    NSPoint aPoint = {0, oBottom};
4113	    bottom = ([self convertPoint: aPoint  fromView: o]).y;
4114	  }
4115	}
4116    }
4117
4118  *newBottom = bottom;
4119}
4120
4121- (void) adjustPageWidthNew: (CGFloat*)newRight
4122		       left: (CGFloat)oldLeft
4123		      right: (CGFloat)oldRight
4124		      limit: (CGFloat)rightLimit
4125{
4126  CGFloat right = oldRight;
4127
4128  if (_rFlags.has_subviews)
4129    {
4130      id e, o;
4131
4132      e = [_sub_views objectEnumerator];
4133      while ((o = [e nextObject]) != nil)
4134	{
4135          // FIXME: We have to convert this values for the subclass
4136
4137	  /* See comments in adjustPageHeightNew:top:bottom:limit:
4138	     about why code is structured in this funny way.  */
4139	  CGFloat oLeft, oRight, oLimit;
4140	  /* Don't ask me why, but gcc-2.91.66 crashes if we use
4141	     NSMakePoint in the following expressions.  We avoid this
4142	     compiler internal bug by using an auxiliary aPoint
4143	     variable, and setting it manually to the NSPoints we
4144	     need.  */
4145	  {
4146	    NSPoint aPoint = {oldLeft, 0};
4147	    oLeft = ([self convertPoint: aPoint  toView: o]).x;
4148	  }
4149
4150	  {
4151	    NSPoint aPoint = {right, 0};
4152	    oRight = ([self convertPoint: aPoint  toView: o]).x;
4153	  }
4154
4155	  {
4156	    NSPoint aPoint = {rightLimit, 0};
4157	    oLimit = ([self convertPoint: aPoint  toView: o]).x;
4158	  }
4159
4160	  [o adjustPageHeightNew: &oRight
4161	     top: oLeft
4162	     bottom: oRight
4163	     limit: oLimit];
4164
4165	  {
4166	    NSPoint aPoint = {oRight, 0};
4167	    right = ([self convertPoint: aPoint  fromView: o]).x;
4168	  }
4169	}
4170    }
4171
4172  *newRight = right;
4173}
4174
4175- (CGFloat) heightAdjustLimit
4176{
4177  return 0.0;
4178}
4179
4180- (BOOL) knowsPagesFirst: (int*)firstPageNum last: (int*)lastPageNum
4181{
4182  return NO;
4183}
4184
4185- (BOOL) knowsPageRange: (NSRange*)range
4186{
4187  return NO;
4188}
4189
4190- (NSPoint) locationOfPrintRect: (NSRect)aRect
4191{
4192  int pages;
4193  NSPoint location;
4194  NSRect bounds;
4195  NSMutableDictionary *dict;
4196  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4197  NSPrintInfo *printInfo = [printOp printInfo];
4198  dict = [printInfo dictionary];
4199
4200  pages = [[dict objectForKey: @"NSPrintTotalPages"] intValue];
4201  if ([dict objectForKey: @"NSPrintPaperBounds"])
4202    bounds = [[dict objectForKey: @"NSPrintPaperBounds"] rectValue];
4203  else
4204    bounds = aRect;
4205  location = NSMakePoint(0, NSHeight(bounds)-NSHeight(aRect));
4206  /* FIXME:  I can't figure out how the location for a multi-page document
4207     is computed. Just ignore centering? */
4208  if (pages == 1)
4209    {
4210      if ([printInfo isHorizontallyCentered])
4211        location.x = (NSWidth(bounds) - NSWidth(aRect))/2;
4212      if ([printInfo isVerticallyCentered])
4213        location.y = (NSHeight(bounds) - NSHeight(aRect))/2;
4214    }
4215
4216  return location;
4217}
4218
4219- (NSRect) rectForPage: (NSInteger)page
4220{
4221  return NSZeroRect;
4222}
4223
4224- (CGFloat) widthAdjustLimit
4225{
4226  return 0.0;
4227}
4228
4229/*
4230 * Writing Conforming PostScript
4231 */
4232- (void) beginPage: (int)ordinalNum
4233             label: (NSString*)aString
4234              bBox: (NSRect)pageRect
4235             fonts: (NSString*)fontNames
4236{
4237  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4238  NSGraphicsContext *ctxt = [printOp context];
4239
4240  [ctxt  beginPage: ordinalNum
4241         label: aString
4242         bBox: pageRect
4243         fonts: fontNames];
4244}
4245
4246- (void) beginPageSetupRect: (NSRect)aRect placement: (NSPoint)location
4247{
4248  [self beginPageInRect: aRect atPlacement: location];
4249}
4250
4251- (void) beginPrologueBBox: (NSRect)boundingBox
4252              creationDate: (NSString*)dateCreated
4253                 createdBy: (NSString*)anApplication
4254                     fonts: (NSString*)fontNames
4255                   forWhom: (NSString*)user
4256                     pages: (int)numPages
4257                     title: (NSString*)aTitle
4258{
4259  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4260  NSGraphicsContext *ctxt = [printOp context];
4261
4262  [ctxt beginPrologueBBox: boundingBox
4263	      creationDate: dateCreated
4264        createdBy: anApplication
4265        fonts: fontNames
4266        forWhom: user
4267        pages: numPages
4268        title: aTitle];
4269}
4270
4271- (void) addToPageSetup
4272{
4273}
4274
4275- (void) beginSetup
4276{
4277  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4278  NSGraphicsContext *ctxt = [printOp context];
4279
4280  [ctxt beginSetup];
4281}
4282
4283- (void) beginTrailer
4284{
4285  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4286  NSGraphicsContext *ctxt = [printOp context];
4287
4288  [ctxt beginTrailer];
4289}
4290
4291- (void) drawPageBorderWithSize: (NSSize)borderSize
4292{
4293}
4294
4295- (void) drawSheetBorderWithSize: (NSSize)borderSize
4296{
4297}
4298
4299- (void) endHeaderComments
4300{
4301  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4302  NSGraphicsContext *ctxt = [printOp context];
4303
4304  [ctxt endHeaderComments];
4305}
4306
4307- (void) endPrologue
4308{
4309  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4310  NSGraphicsContext *ctxt = [printOp context];
4311
4312  [ctxt endPrologue];
4313}
4314
4315- (void) endSetup
4316{
4317  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4318  NSGraphicsContext *ctxt = [printOp context];
4319
4320  [ctxt endSetup];
4321}
4322
4323- (void) endPageSetup
4324{
4325  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4326  NSGraphicsContext *ctxt = [printOp context];
4327
4328  [ctxt endPageSetup];
4329}
4330
4331- (void) endPage
4332{
4333  int nup;
4334  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4335  NSGraphicsContext *ctxt = [printOp context];
4336  NSDictionary *dict = [[printOp printInfo] dictionary];
4337
4338  // Balance gsave in beginPageInRect:
4339  DPSgrestore(ctxt);
4340
4341  nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue];
4342  if (nup > 1)
4343    {
4344      DPSPrintf(ctxt, "__GSpagesaveobject restore\n\n");
4345    }
4346
4347  // [self unlockFocus];
4348}
4349
4350- (void) endTrailer
4351{
4352  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4353  NSGraphicsContext *ctxt = [printOp context];
4354
4355  [ctxt endTrailer];
4356}
4357
4358- (NSAttributedString *) pageFooter
4359{
4360  return [[[NSAttributedString alloc] initWithString:
4361		  [NSString stringWithFormat:@"Page %d",
4362			    [[NSPrintOperation currentOperation] currentPage]]]
4363	     autorelease];
4364}
4365
4366- (NSAttributedString *) pageHeader
4367{
4368  return [[[NSAttributedString alloc] initWithString:
4369		  [NSString stringWithFormat:@"%@ %@", [self printJobTitle],
4370			    [[NSCalendarDate calendarDate] description]]] autorelease];
4371}
4372
4373
4374/**
4375    Writes header and job information for the PostScript document. This
4376    includes at a minimum, PostScript header information. It may also
4377    include job setup information if the output is intended for a printer
4378    (i.e. not an EPS file). Most of the information for writing the
4379    header comes from the NSPrintOperation and NSPrintInfo objects
4380    associated with the current print operation.
4381
4382    There isn't normally anything that the program needs to override
4383    at the beginning of a document, although if there is additional
4384    setup that needs to be done, you can override the NSView's methods
4385    endHeaderComments, endPrologue, beginSetup, and/or endSetup.
4386
4387    This method calls the above methods in the listed order before
4388    or after writing the required information. For an EPS operation, the
4389    beginSetup and endSetup methods aren't used.  */
4390- (void)beginDocument
4391{
4392  int first, last, pages, nup;
4393  NSRect bbox;
4394  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4395  NSGraphicsContext *ctxt = [printOp context];
4396  NSDictionary *dict = [[printOp printInfo] dictionary];
4397
4398  if (printOp == nil)
4399    {
4400      [NSException raise: NSInternalInconsistencyException
4401		  format: @"beginDocument called without a current print op"];
4402    }
4403  /* Inform ourselves and subviews that we're printing so we adjust
4404     the PostScript accordingly. Perhaps this could be in the thread
4405     dictionary, but that's probably overkill and slow */
4406  viewIsPrinting = self;
4407
4408  /* Get pagination information */
4409  nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue];
4410  bbox = NSZeroRect;
4411  if ([dict objectForKey: @"NSPrintSheetBounds"])
4412    bbox = [[dict objectForKey: @"NSPrintSheetBounds"] rectValue];
4413  first = [[dict objectForKey: NSPrintFirstPage] intValue];
4414  last  = [[dict objectForKey: NSPrintLastPage] intValue];
4415  pages = last - first + 1;
4416  if (nup > 1)
4417    pages = ceil((float)pages / nup);
4418
4419  /* Begin document structure */
4420  [self beginPrologueBBox: bbox
4421	     creationDate: [[NSCalendarDate calendarDate] description]
4422	        createdBy: [[NSProcessInfo processInfo] processName]
4423		    fonts: nil
4424	          forWhom: NSUserName()
4425		    pages: pages
4426	            title: [self printJobTitle]];
4427  [self endHeaderComments];
4428
4429  [ctxt printerProlog];
4430  [self endPrologue];
4431  if ([printOp isEPSOperation] == NO)
4432    {
4433      [self beginSetup];
4434      // Setup goes here !
4435      [self endSetup];
4436    }
4437
4438  [ctxt resetUsedFonts];
4439  /* Make sure we set the visible rect so everything is printed. */
4440  [self _invalidateCoordinates];
4441  _visibleRect = _bounds;
4442}
4443
4444- (void) beginPageInRect: (NSRect)aRect
4445             atPlacement: (NSPoint)location
4446{
4447  int nup;
4448  NSRect bounds;
4449  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4450  NSGraphicsContext *ctxt = [printOp context];
4451  NSDictionary *dict = [[printOp printInfo] dictionary];
4452
4453  if (NSIsEmptyRect(aRect))
4454    {
4455      if ([dict objectForKey: @"NSPrintPaperBounds"])
4456        {
4457          bounds = [[dict objectForKey: @"NSPrintPaperBounds"] rectValue];
4458        }
4459      else
4460        {
4461          // FIXME: What should we use here?
4462          bounds = aRect;
4463        }
4464    }
4465  else
4466    {
4467      bounds = aRect;
4468    }
4469
4470  nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue];
4471  if (nup > 1)
4472    {
4473      int page;
4474      float xoff, yoff;
4475      float scale;
4476
4477      DPSPrintf(ctxt, "/__GSpagesaveobject save def\n");
4478
4479      scale = [[dict objectForKey: @"NSNupScale"] floatValue];
4480      page = [printOp currentPage]
4481          - [[dict objectForKey: NSPrintFirstPage] intValue];
4482      page = page % nup;
4483      if (nup == 2)
4484        xoff = page;
4485      else
4486        xoff = (page % (nup/2));
4487      xoff *= NSWidth(bounds) * scale;
4488      if (nup == 2)
4489        yoff = 0;
4490      else
4491        yoff = (int)((nup-page-1) / (nup/2));
4492      yoff *= NSHeight(bounds) * scale;
4493      DPStranslate(ctxt, xoff, yoff);
4494      DPSgsave(ctxt);
4495      DPSscale(ctxt, scale, scale);
4496    }
4497  else
4498    {
4499      DPSgsave(ctxt);
4500    }
4501
4502  /* Translate to placement */
4503  if (location.x != 0 || location.y != 0)
4504    {
4505      DPStranslate(ctxt, location.x, location.y);
4506    }
4507}
4508
4509- (void) _endSheet
4510{
4511  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4512  NSGraphicsContext *ctxt = [printOp context];
4513
4514  [ctxt endSheet];
4515}
4516
4517- (void) endDocument
4518{
4519  int first, last, current, pages;
4520  NSPrintOperation *printOp = [NSPrintOperation currentOperation];
4521  NSGraphicsContext *ctxt = [printOp context];
4522  NSDictionary *dict = [[printOp printInfo] dictionary];
4523
4524  first = [[dict objectForKey: NSPrintFirstPage] intValue];
4525  last  = [[dict objectForKey: NSPrintLastPage] intValue];
4526  pages = last - first + 1;
4527  [self beginTrailer];
4528
4529  if (pages == 0)
4530    {
4531      int nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue];
4532      current = [printOp currentPage];
4533      pages = current - first; // Current is 1 more than the last page
4534      if (nup > 1)
4535        pages = ceil((float)pages / nup);
4536    }
4537  else
4538    {
4539      // Already reported at start of document
4540      pages = 0;
4541    }
4542  [ctxt endDocumentPages: pages documentFonts: [ctxt usedFonts]];
4543
4544  [self endTrailer];
4545  [self _invalidateCoordinates];
4546  viewIsPrinting = nil;
4547}
4548
4549/* An exception occurred while printing. Clean up */
4550- (void) _cleanupPrinting
4551{
4552  [self _invalidateCoordinates];
4553  viewIsPrinting = nil;
4554}
4555
4556/*
4557 * NSCoding protocol
4558 */
4559- (void) encodeWithCoder: (NSCoder*)aCoder
4560{
4561  if ([aCoder allowsKeyedCoding])
4562    {
4563      NSUInteger vFlags = 0;
4564
4565      // encoding
4566      [aCoder encodeConditionalObject: [self nextKeyView]
4567	      forKey: @"NSNextKeyView"];
4568      [aCoder encodeConditionalObject: [self previousKeyView]
4569	      forKey: @"NSPreviousKeyView"];
4570      [aCoder encodeObject: _sub_views
4571	      forKey: @"NSSubviews"];
4572      [aCoder encodeRect: _frame
4573	      forKey: @"NSFrame"];
4574
4575      // autosizing masks.
4576      vFlags = _autoresizingMask;
4577
4578      // add the autoresize flag.
4579      if (_autoresizes_subviews)
4580        {
4581          vFlags |= 0x100;
4582        }
4583
4584      // add the hidden flag
4585      if (_is_hidden)
4586        {
4587          vFlags |= 0x80000000;
4588        }
4589
4590      [aCoder encodeInt: vFlags
4591	      forKey: @"NSvFlags"];
4592
4593      //
4594      // Don't attempt to archive the superview of a view which is the
4595      // content view for a window.
4596      //
4597      if (([[self window] contentView] != self) && _super_view != nil)
4598        {
4599          [aCoder encodeConditionalObject: _super_view forKey: @"NSSuperview"];
4600        }
4601    }
4602  else
4603    {
4604      NSDebugLLog(@"NSView", @"NSView: start encoding\n");
4605      [super encodeWithCoder: aCoder];
4606
4607      [aCoder encodeRect: _frame];
4608      [aCoder encodeRect: _bounds];
4609      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_is_rotated_from_base];
4610      [aCoder encodeValueOfObjCType: @encode(BOOL)
4611	      at: &_is_rotated_or_scaled_from_base];
4612      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_post_frame_changes];
4613      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autoresizes_subviews];
4614      [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_autoresizingMask];
4615      [aCoder encodeConditionalObject: [self nextKeyView]];
4616      [aCoder encodeConditionalObject: [self previousKeyView]];
4617      [aCoder encodeObject: _sub_views];
4618      NSDebugLLog(@"NSView", @"NSView: finish encoding\n");
4619    }
4620}
4621
4622- (id) initWithCoder: (NSCoder*)aDecoder
4623{
4624  NSEnumerator *e;
4625  NSView	*sub;
4626  NSArray	*subs;
4627
4628  // decode the superclass...
4629  self = [super initWithCoder: aDecoder];
4630  if (!self)
4631    return nil;
4632
4633  // initialize these here, since they're needed in either case.
4634  // _frameMatrix = [NSAffineTransform new];    // Map fromsuperview to frame
4635  // _boundsMatrix = [NSAffineTransform new];   // Map from superview to bounds
4636  _matrixToWindow = [NSAffineTransform new];  // Map to window coordinates
4637  _matrixFromWindow = [NSAffineTransform new];// Map from window coordinates
4638
4639  if ([aDecoder allowsKeyedCoding])
4640    {
4641      NSView *prevKeyView = nil;
4642      NSView *nextKeyView = nil;
4643
4644      if ([aDecoder containsValueForKey: @"NSFrame"])
4645        {
4646          _frame = [aDecoder decodeRectForKey: @"NSFrame"];
4647        }
4648      else
4649        {
4650          _frame = NSZeroRect;
4651          if ([aDecoder containsValueForKey: @"NSFrameSize"])
4652            {
4653              _frame.size = [aDecoder decodeSizeForKey: @"NSFrameSize"];
4654            }
4655        }
4656
4657      // Set bounds rectangle
4658      _bounds.origin = NSZeroPoint;
4659      _bounds.size = _frame.size;
4660      if ([aDecoder containsValueForKey: @"NSBounds"])
4661        {
4662          [self setBounds: [aDecoder decodeRectForKey: @"NSBounds"]];
4663        }
4664
4665      _sub_views = [NSMutableArray new];
4666      _tracking_rects = [NSMutableArray new];
4667      _cursor_rects = [NSMutableArray new];
4668
4669      _is_rotated_from_base = NO;
4670      _is_rotated_or_scaled_from_base = NO;
4671      _rFlags.needs_display = YES;
4672      _post_bounds_changes = YES;
4673      _post_frame_changes = YES;
4674      _autoresizes_subviews = YES;
4675      _autoresizingMask = NSViewNotSizable;
4676      _coordinates_valid = NO;
4677      /*
4678       * Note: don't zero _nextKeyView and _previousKeyView, as the key view
4679       * chain may already have been established by super's initWithCoder:
4680       *
4681       * _nextKeyView = 0;
4682       * _previousKeyView = 0;
4683       */
4684
4685      // previous and next key views...
4686      prevKeyView = [aDecoder decodeObjectForKey: @"NSPreviousKeyView"];
4687      nextKeyView = [aDecoder decodeObjectForKey: @"NSNextKeyView"];
4688      if (nextKeyView != nil)
4689        {
4690          [self setNextKeyView: nextKeyView];
4691        }
4692      if (prevKeyView != nil)
4693        {
4694          [self setPreviousKeyView: prevKeyView];
4695        }
4696      if ([aDecoder containsValueForKey: @"NSvFlags"])
4697        {
4698          NSUInteger vFlags = [aDecoder decodeIntForKey: @"NSvFlags"];
4699
4700          // We are lucky here, Apple use the same constants
4701          // in the lower bits of the flags
4702          [self setAutoresizingMask: vFlags & 0x3F];
4703          [self setAutoresizesSubviews: ((vFlags & 0x100) == 0x100)];
4704          [self setHidden: ((vFlags & 0x80000000) == 0x80000000)];
4705        }
4706
4707      // iterate over subviews and put them into the view...
4708      subs = [aDecoder decodeObjectForKey: @"NSSubviews"];
4709      e = [subs objectEnumerator];
4710      while ((sub = [e nextObject]) != nil)
4711	{
4712	  NSAssert([sub class] != [NSCustomView class],
4713		   NSInternalInconsistencyException);
4714	  NSAssert([sub window] == nil,
4715		   NSInternalInconsistencyException);
4716          NSAssert([sub superview] == nil,
4717                   NSInternalInconsistencyException);
4718	  [sub _viewWillMoveToWindow: _window];
4719	  [sub _viewWillMoveToSuperview: self];
4720	  [sub setNextResponder: self];
4721	  [_sub_views addObject: sub];
4722	  _rFlags.has_subviews = 1;
4723	  [sub resetCursorRects];
4724	  [sub setNeedsDisplay: YES];
4725	  [sub _viewDidMoveToWindow];
4726	  [sub viewDidMoveToSuperview];
4727	  [self didAddSubview: sub];
4728	}
4729
4730      // the superview...
4731      //[aDecoder decodeObjectForKey: @"NSSuperview"];
4732    }
4733  else
4734    {
4735      NSRect	rect;
4736
4737      NSDebugLLog(@"NSView", @"NSView: start decoding\n");
4738
4739      _frame = [aDecoder decodeRect];
4740
4741      _bounds.origin = NSZeroPoint;
4742      _bounds.size = _frame.size;
4743
4744      rect = [aDecoder decodeRect];
4745      [self setBounds: rect];
4746
4747      _sub_views = [NSMutableArray new];
4748      _tracking_rects = [NSMutableArray new];
4749      _cursor_rects = [NSMutableArray new];
4750
4751      _super_view = nil;
4752      _window = nil;
4753      _rFlags.needs_display = YES;
4754      [aDecoder decodeValueOfObjCType: @encode(BOOL)
4755				   at: &_is_rotated_from_base];
4756      [aDecoder decodeValueOfObjCType: @encode(BOOL)
4757				   at: &_is_rotated_or_scaled_from_base];
4758      _post_bounds_changes = YES;
4759      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_post_frame_changes];
4760      [aDecoder decodeValueOfObjCType: @encode(BOOL)
4761				   at: &_autoresizes_subviews];
4762      [aDecoder decodeValueOfObjCType: @encode(NSUInteger)
4763				   at: &_autoresizingMask];
4764      _coordinates_valid = NO;
4765      [self setNextKeyView: [aDecoder decodeObject]];
4766      [[aDecoder decodeObject] setNextKeyView: self];
4767
4768      [aDecoder decodeValueOfObjCType: @encode(id) at: &subs];
4769      NSDebugLLog(@"NSView", @"NSView: finish decoding\n");
4770
4771      // iterate over subviews and put them into the view...
4772      e = [subs objectEnumerator];
4773      while ((sub = [e nextObject]) != nil)
4774	{
4775	  NSAssert([sub window] == nil,
4776	    NSInternalInconsistencyException);
4777	  NSAssert([sub superview] == nil,
4778	    NSInternalInconsistencyException);
4779	  [sub _viewWillMoveToWindow: _window];
4780	  [sub _viewWillMoveToSuperview: self];
4781	  [sub setNextResponder: self];
4782	  [_sub_views addObject: sub];
4783	  _rFlags.has_subviews = 1;
4784	  [sub resetCursorRects];
4785	  [sub setNeedsDisplay: YES];
4786	  [sub _viewDidMoveToWindow];
4787	  [sub viewDidMoveToSuperview];
4788	  [self didAddSubview: sub];
4789	}
4790      RELEASE(subs);
4791    }
4792
4793  return self;
4794}
4795
4796/*
4797 * Accessor methods
4798 */
4799- (void) setAutoresizesSubviews: (BOOL)flag
4800{
4801  _autoresizes_subviews = flag;
4802}
4803
4804- (void) setAutoresizingMask: (NSUInteger)mask
4805{
4806  _autoresizingMask = mask;
4807}
4808
4809/** Returns the window in which the receiver resides. */
4810- (NSWindow*) window
4811{
4812  return _window;
4813}
4814
4815- (BOOL) autoresizesSubviews
4816{
4817  return _autoresizes_subviews;
4818}
4819
4820- (NSUInteger) autoresizingMask
4821{
4822  return _autoresizingMask;
4823}
4824
4825- (NSArray*) subviews
4826{
4827  /*
4828   * Return a mutable copy 'cos we know that a mutable copy of an array or
4829   * a mutable array does a shallow copy - which is what we want to give
4830   * away - we don't want people to mess with our actual subviews array.
4831   */
4832  return AUTORELEASE([_sub_views mutableCopyWithZone: NSDefaultMallocZone()]);
4833}
4834
4835- (NSView*) superview
4836{
4837  return _super_view;
4838}
4839
4840- (BOOL) shouldDrawColor
4841{
4842  return YES;
4843}
4844
4845- (BOOL) isOpaque
4846{
4847  return NO;
4848}
4849
4850- (BOOL) needsDisplay
4851{
4852  return _rFlags.needs_display;
4853}
4854
4855- (NSInteger) tag
4856{
4857  return -1;
4858}
4859
4860- (BOOL) isFlipped
4861{
4862  return NO;
4863}
4864
4865- (NSRect) bounds
4866{
4867  return _bounds;
4868}
4869
4870- (NSRect) frame
4871{
4872  return _frame;
4873}
4874
4875- (CGFloat) boundsRotation
4876{
4877  if (_boundsMatrix != nil)
4878    {
4879      return [_boundsMatrix rotationAngle];
4880    }
4881
4882  return 0.0;
4883}
4884
4885- (CGFloat) frameRotation
4886{
4887  if (_frameMatrix != nil)
4888    {
4889      return [_frameMatrix rotationAngle];
4890    }
4891
4892  return 0.0;
4893}
4894
4895/**
4896 * Returns whether the receiver posts NSViewFrameDidChangeNotification when
4897 * its frame changed.
4898 *
4899 * Returns YES by default (as documented in Cocoa View Programming Guide).
4900 */
4901- (BOOL) postsFrameChangedNotifications
4902{
4903  return _post_frame_changes;
4904}
4905
4906/**
4907 * Returns whether the receiver posts NSViewBoundsDidChangeNotification when
4908 * its bound changed.
4909 *
4910 * Returns YES by default (as documented in Cocoa View Programming Guide).
4911 */
4912- (BOOL) postsBoundsChangedNotifications
4913{
4914  return _post_bounds_changes;
4915}
4916
4917
4918/**
4919 * <p>Returns the default menu to be used for instances of the
4920 *    current class; if no menu has been set through setMenu:
4921 *    this default menu will be used.
4922 * </p>
4923 * <p>NSView's implementation returns nil. You should override
4924 *    this method if you want all instances of your custom view
4925 *    to use the same menu.
4926 * </p>
4927 */
4928+ (NSMenu *)defaultMenu
4929{
4930  return nil;
4931}
4932
4933/**
4934 * <p>NSResponder's method, overriden by NSView.</p>
4935 * <p>If no menu has been set through the use of setMenu:, or
4936 *    if a nil value has been set through setMenu:, then the
4937 *    value returned by defaultMenu is used. Otherwise this
4938 *    method returns the menu set through NSResponder.
4939 * <p>
4940 * <p> see [NSResponder -menu], [NSResponder -setMenu:],
4941 *     [NSView +defaultMenu] and [NSView -menuForEvent:].
4942 * </p>
4943 */
4944- (NSMenu *)menu
4945{
4946  NSMenu *m = [super menu];
4947  if (m)
4948    {
4949      return m;
4950    }
4951  else
4952    {
4953      return [[self class] defaultMenu];
4954    }
4955}
4956
4957/**
4958 * <p>Returns the menu that it appropriates for the given
4959 *    event. NSView's implementation returns the default menu of
4960 *    the view.</p>
4961 * <p>This methods is intended to be overriden so that it can
4962 *    return a context-sensitive for appropriate mouse's events. (
4963 *    (although it seems it can be used for any kind of event)</p>
4964 * <p>This method is used by NSView's rightMouseDown: method,
4965 *    and the returned NSMenu is displayed as a context menu</p>
4966 * <p>Use of this method is discouraged in GNUstep as it breaks many
4967 *    user interface guidelines. At the very least, menu items that appear
4968 *    in a context sensitive menu should also always appear in a normal
4969 *    menu. Otherwise, users are faced with an inconsistant interface where
4970 *    the menu items they want are only available in certain (possibly
4971 *    unknown) cases, making it difficult for the user to understand how
4972 *    the application operates</p>
4973 * <p> see [NSResponder -menu], [NSResponder -setMenu:],
4974 *     [NSView +defaultMenu] and [NSView -menu].
4975 * </p>
4976 */
4977- (NSMenu *)menuForEvent: (NSEvent *)theEvent
4978{
4979  return [self menu];
4980}
4981
4982/*
4983 * Tool Tips
4984 */
4985
4986- (NSToolTipTag) addToolTipRect: (NSRect)aRect
4987			  owner: (id)anObject
4988		       userData: (void *)data
4989{
4990  GSToolTips	*tt = [GSToolTips tipsForView: self];
4991
4992  _rFlags.has_tooltips = 1;
4993  return [tt addToolTipRect: aRect owner: anObject userData: data];
4994}
4995
4996- (void) removeAllToolTips
4997{
4998  if (_rFlags.has_tooltips == 1)
4999    {
5000      GSToolTips	*tt = [GSToolTips tipsForView: self];
5001
5002      [tt removeAllToolTips];
5003    }
5004}
5005
5006- (void) removeToolTip: (NSToolTipTag)tag
5007{
5008  if (_rFlags.has_tooltips == 1)
5009    {
5010      GSToolTips	*tt = [GSToolTips tipsForView: self];
5011
5012      [tt removeToolTip: tag];
5013    }
5014}
5015
5016- (void) setToolTip: (NSString *)string
5017{
5018  if (_rFlags.has_tooltips == 1 || [string length] > 0)
5019    {
5020      GSToolTips	*tt = [GSToolTips tipsForView: self];
5021
5022      _rFlags.has_tooltips = 1;
5023      [tt setToolTip: string];
5024    }
5025}
5026
5027- (NSString *) toolTip
5028{
5029  if (_rFlags.has_tooltips == 1)
5030    {
5031      GSToolTips	*tt = [GSToolTips tipsForView: self];
5032
5033      return [tt toolTip];
5034    }
5035  return nil;
5036}
5037
5038- (void) rightMouseDown: (NSEvent *) theEvent
5039{
5040  NSMenu *m;
5041  m = [self menuForEvent: theEvent];
5042  if (m)
5043    {
5044      [NSMenu popUpContextMenu: m
5045	      withEvent: theEvent
5046	      forView: self];
5047    }
5048  else
5049    {
5050      [super rightMouseDown: theEvent];
5051    }
5052}
5053
5054- (BOOL) shouldBeTreatedAsInkEvent: (NSEvent *)theEvent
5055{
5056  return YES;
5057}
5058
5059- (void) bind: (NSString *)binding
5060     toObject: (id)anObject
5061  withKeyPath: (NSString *)keyPath
5062      options: (NSDictionary *)options
5063{
5064  if ([binding hasPrefix: NSHiddenBinding])
5065    {
5066      GSKeyValueBinding *kvb;
5067
5068      [self unbind: binding];
5069      kvb = [[GSKeyValueOrBinding alloc] initWithBinding: NSHiddenBinding
5070                                   withName: binding
5071                                   toObject: anObject
5072                                   withKeyPath: keyPath
5073                                   options: options
5074                                   fromObject: self];
5075      // The binding will be retained in the binding table
5076      RELEASE(kvb);
5077    }
5078  else
5079    {
5080      [super bind: binding
5081             toObject: anObject
5082             withKeyPath: keyPath
5083             options: options];
5084    }
5085}
5086
5087- (NSViewLayerContentsPlacement) layerContentsPlacement
5088{
5089  // FIXME (when views have CALayer support)
5090  return NSViewLayerContentsPlacementScaleAxesIndependently;
5091}
5092
5093- (void) setLayerContentsPlacement: (NSViewLayerContentsPlacement)placement
5094{
5095  // FIXME (when views have CALayer support)
5096  static BOOL logged = NO;
5097  if (!logged)
5098    {
5099      NSLog(@"warning: stub no-op implementation of -[NSView setLayerContentsPlacement:]");
5100      logged = YES;
5101    }
5102}
5103
5104- (NSViewLayerContentsRedrawPolicy) layerContentsRedrawPolicy
5105{
5106  // FIXME (when views have CALayer support)
5107  return NSViewLayerContentsRedrawNever;
5108}
5109
5110- (void) setLayerContentsRedrawPolicy: (NSViewLayerContentsRedrawPolicy) pol
5111{
5112  // FIXME (when views have CALayer support)
5113  static BOOL logged = NO;
5114  if (!logged)
5115    {
5116      NSLog(@"warning: stub no-op implementation of -[NSView setLayerContentsRedrawPolicy:]");
5117      logged = YES;
5118    }
5119}
5120
5121- (NSUserInterfaceLayoutDirection) userInterfaceLayoutDirection
5122{
5123  // FIXME
5124  return NSUserInterfaceLayoutDirectionLeftToRight;
5125}
5126
5127- (void) setUserInterfaceLayoutDirection: (NSUserInterfaceLayoutDirection)dir
5128{
5129  // FIXME: implement this
5130  return;
5131}
5132
5133@end
5134
5135@implementation NSView(KeyViewLoop)
5136
5137static NSComparisonResult
5138cmpFrame(id view1, id view2, void *context)
5139{
5140  BOOL flippedSuperView = [(NSView *)context isFlipped];
5141  NSRect frame1 = [view1 frame];
5142  NSRect frame2 = [view2 frame];
5143
5144  if (NSMinY(frame1) < NSMinY(frame2))
5145    return flippedSuperView ? NSOrderedAscending : NSOrderedDescending;
5146  if (NSMaxY(frame1) > NSMaxY(frame2))
5147    return flippedSuperView ? NSOrderedDescending : NSOrderedAscending;
5148
5149  // FIXME Should use NSMaxX in a Hebrew or Arabic locale
5150  if (NSMinX(frame1) < NSMinX(frame2))
5151    return NSOrderedAscending;
5152  if (NSMinX(frame1) > NSMinX(frame2))
5153    return NSOrderedDescending;
5154  return NSOrderedSame;
5155}
5156
5157- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
5158{
5159  if (_rFlags.has_subviews)
5160    {
5161      [self _recursiveSetUpKeyViewLoopWithNextKeyView: nextKeyView];
5162    }
5163  else
5164    {
5165      [self setNextKeyView: nextKeyView];
5166    }
5167}
5168
5169- (void) _recursiveSetUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
5170{
5171  NSArray *sortedViews;
5172  NSView *aView;
5173  NSEnumerator *e;
5174
5175  sortedViews = [_sub_views sortedArrayUsingFunction: cmpFrame context: self];
5176  e = [sortedViews reverseObjectEnumerator];
5177  while ((aView = [e nextObject]) != nil)
5178    {
5179      [aView _setUpKeyViewLoopWithNextKeyView: nextKeyView];
5180      nextKeyView = aView;
5181    }
5182  [self setNextKeyView: nextKeyView];
5183}
5184
5185@end
5186