1//
2//  CenteringClipView.m
3//  avida/apps/viewer-macos
4//
5//  Created by David on 4/22/11.
6//  Copyright 2011 Michigan State University. All rights reserved.
7//  http://avida.devosoft.org/viewer-macos
8//
9//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
10//  following conditions are met:
11//
12//  1.  Redistributions of source code must retain the above copyright notice, this list of conditions and the
13//      following disclaimer.
14//  2.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
15//      following disclaimer in the documentation and/or other materials provided with the distribution.
16//  3.  Neither the name of Michigan State University, nor the names of contributors may be used to endorse or promote
17//      products derived from this software without specific prior written permission.
18//
19//  THIS SOFTWARE IS PROVIDED BY MICHIGAN STATE UNIVERSITY AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21//  DISCLAIMED. IN NO EVENT SHALL MICHIGAN STATE UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
25//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26//
27//  Authors: David M. Bryson <david@programerror.com>
28//
29
30#import "CenteringClipView.h"
31
32#include <stdio.h>
33
34@implementation CenteringClipView
35
36- (id) initWithFrame:(NSRect)frame {
37  self = [super initWithFrame:frame];
38  if (self) {
39    viewPoint = NSMakePoint(0, 0);
40    hasHScroll = NO;
41    hasVScroll = NO;
42    adjustingScrollers = NO;
43    [self setAutoresizesSubviews:NO];
44  }
45  return self;
46}
47
48
49- (void) centerView {
50  NSRect docRect = [[self documentView] frame];
51  NSRect clipRect = [self bounds];
52
53  // Center the clipping rect origin x
54  if (docRect.size.width < clipRect.size.width) {
55    clipRect.origin.x = roundf((docRect.size.width - clipRect.size.width) / 2.0);
56  } else {
57    clipRect.origin.x = roundf(viewPoint.x * docRect.size.width - (clipRect.size.width / 2.0));
58  }
59
60  // Center the clipping rect origin y
61  if (docRect.size.height < clipRect.size.height) {
62    clipRect.origin.y = roundf((docRect.size.height - clipRect.size.height) / 2.0);
63  } else {
64    clipRect.origin.y = roundf(viewPoint.y * docRect.size.width - (clipRect.size.height / 2.0));
65  }
66
67//  printf("-----centerView-----\n");
68//  printf("doc : %f x %f @ %f, %f\n", docRect.size.width, docRect.size.height, docRect.origin.x, docRect.origin.y);
69//  printf("clip: %f x %f @ %f, %f\n", clipRect.size.width, clipRect.size.height, clipRect.origin.x, clipRect.origin.y);
70
71  // Scroll the document to the selected center point
72  NSScrollView* scrollView = (NSScrollView*)[self superview];
73  [self scrollToPoint:[self constrainScrollPoint:clipRect.origin]];
74  [scrollView reflectScrolledClipView:self];
75
76
77	if (!adjustingScrollers) {
78    BOOL hScroller = NO;
79    BOOL vScroller = NO;
80
81    // Determine scroll view frame dimensions (without scrollers)
82//    const CGFloat scrollerWidth = ([scrollView scrollerStyle] == NSScrollerStyleLegacy) ? [NSScroller scrollerWidth] : 0.0f;
83    const CGFloat scrollerWidth = [NSScroller scrollerWidth];
84    NSRect frameRect = [super frame];
85    CGFloat frameWidth = frameRect.size.width;
86    CGFloat frameHeight = frameRect.size.height;
87    if (hasVScroll) frameWidth += scrollerWidth;
88    if (hasHScroll) frameHeight += scrollerWidth;
89
90		// Determine needed scrollers
91		if (docRect.size.width <= frameWidth && docRect.size.height <= frameHeight) {
92			hScroller = NO;
93			vScroller = NO;
94		} else if (docRect.size.width > frameWidth && docRect.size.height <= (frameHeight - scrollerWidth)) {
95			hScroller = YES;
96			vScroller = NO;
97		} else if (docRect.size.height > frameHeight && docRect.size.width <= (frameWidth - scrollerWidth)) {
98			hScroller = NO;
99			vScroller = YES;
100		} else {
101			hScroller = YES;
102			vScroller = YES;
103		}
104
105    // Adjust horizontal scroller visibility
106		if (hScroller != hasHScroll) {
107			hasHScroll = !hasHScroll;
108			adjustingScrollers = YES;
109      NSScroller* horizontalScroller = [scrollView horizontalScroller];
110      [horizontalScroller setKnobProportion:1.0f];
111      [horizontalScroller setDoubleValue:0.0];
112			[scrollView setHasHorizontalScroller:hasHScroll];
113			adjustingScrollers = NO;
114		}
115
116    // Adjust vertical scroller visibility
117		if (vScroller != hasVScroll) {
118			hasVScroll = !hasVScroll;
119			adjustingScrollers = YES;
120      NSScroller* verticalScroller = [scrollView verticalScroller];
121      [verticalScroller setKnobProportion:1.0f];
122      [verticalScroller setDoubleValue:0.0];
123			[scrollView setHasVerticalScroller:hasVScroll];
124			adjustingScrollers = NO;
125		}
126
127//    NSLog(@"hscroll %@ %d", [scrollView horizontalScroller], [[scrollView horizontalScroller] isHidden]);
128//    NSLog(@"vscroll %@ %d", [scrollView verticalScroller], [[scrollView verticalScroller] isHidden]);
129
130//    [scrollView flashScrollers];
131  }
132}
133
134
135// NSClipView Method Overrides
136
137- (NSPoint) constrainScrollPoint:(NSPoint)proposedNewOrigin {
138  NSRect docRect = [[self documentView] frame];
139  NSRect clipRect = [self bounds];
140  CGFloat maxX = docRect.size.width - clipRect.size.width;
141  CGFloat maxY = docRect.size.height - clipRect.size.height;
142
143//  printf("-----constrainScrollPoint-----\n");
144//  printf("prop: %f, %f\n", proposedNewOrigin.x, proposedNewOrigin.y);
145//  printf("doc : %f x %f @ %f, %f\n", docRect.size.width, docRect.size.height, docRect.origin.x, docRect.origin.y);
146//  printf("clip: %f x %f @ %f, %f\n", clipRect.size.width, clipRect.size.height, clipRect.origin.x, clipRect.origin.y);
147
148  clipRect.origin = proposedNewOrigin;
149
150  if (docRect.size.width < clipRect.size.width) {
151    clipRect.origin.x = roundf(maxX / 2.0);
152  } else {
153    clipRect.origin.x = roundf(MAX(0, MIN(clipRect.origin.x, maxX)));
154  }
155
156  if (docRect.size.height < clipRect.size.height) {
157    clipRect.origin.y = roundf(maxY / 2.0);
158  } else {
159    clipRect.origin.y = roundf(MAX(0, MIN(clipRect.origin.y, maxY)));
160  }
161
162  viewPoint.x = NSMidX(clipRect) / docRect.size.width;
163  viewPoint.y = NSMidY(clipRect) / docRect.size.height;
164
165//  printf("orig: %f, %f\n", clipRect.origin.x, clipRect.origin.y);
166  return clipRect.origin;
167}
168
169
170- (BOOL) copiesOnScroll
171{
172	NSRect docRect = [[self documentView] frame];
173	NSRect clipRect = [self bounds];
174
175	return (roundf(docRect.size.width - clipRect.size.width) >= 0) && (roundf(docRect.size.height - clipRect.size.height) >= 0);
176}
177
178
179- (void) viewBoundsChanged:(NSNotification*)notification {
180  [super viewBoundsChanged:notification];
181  [self centerView];
182}
183
184- (void) viewFrameChanged:(NSNotification*)notification {
185  [super viewBoundsChanged:notification];
186  [self centerView];
187}
188
189- (void) setFrame:(NSRect)frameRect {
190  [super setFrame:frameRect];
191  [self centerView];
192}
193
194- (void) setFrameOrigin:(NSPoint)newOrigin {
195  [super setFrameOrigin:newOrigin];
196  [self centerView];
197}
198
199- (void) setFrameSize:(NSSize)newSize {
200  [super setFrameSize:newSize];
201  [self centerView];
202}
203
204- (void) setFrameRotation:(CGFloat)angle {
205  [super setFrameRotation:angle];
206  [self centerView];
207}
208
209- (void) setDocumentView:(NSView*)docView {
210  [super setDocumentView:docView];
211  [self centerView];
212}
213
214@end
215