1/*
2 * TileScrollView.m
3 *
4 * Copyright (C) 1996-2013 by vhf interservice GmbH
5 * Author: Georg Fleischmann
6 *
7 * Created:  1993
8 * Modified: 2013-06-29 (-scaleTo: new - scale to saved scaleFactor)
9 *           2012-08-13 (call of [document -scale:...] with NSSize, scaleFactor, scale -> VFloat)
10 *           2009-09-22 (-magnifyRegion: zero region -> scale to next zoom step, not 5000%)
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the vhf Public License as
14 * published by vhf interservice GmbH. Among other things, the
15 * License requires that the copyright notices and this notice
16 * be preserved on all copies.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21 * See the vhf Public License for more details.
22 *
23 * You should have received a copy of the vhf Public License along
24 * with this program; see the file LICENSE. If not, write to vhf.
25 *
26 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
27 * eMail: info@vhf.de
28 * http://www.vhf.de
29 */
30
31#include <AppKit/AppKit.h>
32#include <VHFShared/types.h>
33#include "TileScrollView.h"
34#include "App.h"
35
36@implementation TileScrollView
37
38
39/* instance methods */
40
41/*+ (NSSize)frameSizeForContentSize:(NSSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(NSBorderType)borderType
42{   NSSize	size;
43
44    size = [super frameSizeForContentSize:contentSize hasHorizontalScroller:hFlag hasVerticalScroller:vFlag borderType:borderType];
45    size.height += 28.0;
46
47    return size;
48}
49
50+ (NSSize)contentSizeForFrameSize:(NSSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(NSBorderType)borderType
51{   NSSize	size;
52
53    size = [super contentSizeForFrameSize:frameSize hasHorizontalScroller:hFlag hasVerticalScroller:vFlag borderType:borderType];
54    size.height -= 28.0;
55
56    return size;
57}*/
58
59- initWithFrame:(NSRect)theFrame
60{
61    [super initWithFrame:theFrame];
62
63    /* remember the current scale factor */
64    oldScaleFactor = 1.0;
65
66#ifdef __APPLE__
67    [self setDrawsBackground:NO];
68#endif
69
70    return self;
71}
72
73- (void)setDocument:docu
74{   int i;
75
76    document = docu;
77
78    [resPopupListButton setTarget:self];
79    [resPopupListButton setAction:@selector(changeScale:)];
80    if ( (i = [resPopupListButton indexOfItemWithTag:100]) >= 0 )   // GNUstep: set to 100%
81        [resPopupListButton selectItemAtIndex:i];
82}
83
84/* created:  1993-07-04
85 * modified: 2012-08-13
86 *
87 * increment and decrement an entry in the popup menu
88 */
89- (void)zoomIn:sender
90{   int		row;
91    VFloat	scaleFactor;
92    NSPoint	center;
93    NSRect	bRect;
94
95    for (row=0; row<[resPopupListButton numberOfItems]; row++)
96        if (Diff((VFloat)[[resPopupListButton itemAtIndex:row] tag] / 100.0, oldScaleFactor) < 0.001)
97            break;
98    row++;
99    if (row >= [resPopupListButton numberOfItems])
100        return;
101    [resPopupListButton setTitle:[resPopupListButton itemTitleAtIndex:row]];
102
103    scaleFactor = [[resPopupListButton itemAtIndex:row] tag] / 100.0;
104
105    bRect = [[self documentView] visibleRect];
106    center.x = bRect.origin.x+bRect.size.width/2.0;
107    center.y = bRect.origin.y+bRect.size.height/2.0;
108
109    [document scale:NSMakeSize(scaleFactor/oldScaleFactor, scaleFactor/oldScaleFactor) withCenter:center];
110    oldScaleFactor = scaleFactor;
111
112    [[self window] makeFirstResponder:[document documentView]];
113}
114
115- (void)zoomOut:sender
116{   int		row;
117    VFloat	scaleFactor;
118    NSPoint	center;
119    NSRect	bRect;
120
121    for (row=0; row<[resPopupListButton numberOfItems]; row++)
122        if (Diff((float)[[resPopupListButton itemAtIndex:row] tag] / 100.0, oldScaleFactor) < 0.001)
123            break;
124    row--;
125    if (row < 0)
126        return;
127
128    scaleFactor = (VFloat)[[resPopupListButton itemAtIndex:row] tag] / 100.0;
129
130#if !defined(GNUSTEP_BASE_VERSION) && !defined(__APPLE__)	// OpenStep 4.2
131    bRect = [[self documentView] bounds];
132    if ( ![[self documentView] caching] &&
133         Max(bRect.size.width, bRect.size.height)/scaleFactor >= 10000.0)
134        return;
135#endif
136
137    [resPopupListButton setTitle:[resPopupListButton itemTitleAtIndex:row]];
138
139    bRect = [[self documentView] visibleRect];
140    center.x = bRect.origin.x+bRect.size.width /2.0;
141    center.y = bRect.origin.y+bRect.size.height/2.0;
142
143    [document scale:NSMakeSize(scaleFactor/oldScaleFactor, scaleFactor/oldScaleFactor) withCenter:center];
144    oldScaleFactor = scaleFactor;
145
146    [[self window] makeFirstResponder:[document documentView]];
147}
148
149- (void)magnify:sender
150{   BOOL	flag = ([[self documentView] magnify]) ? NO : YES;
151
152    [[self documentView] setMagnify:flag];
153    if ( flag )
154        [self setDocumentCursor:[[NSCursor alloc] initWithImage:[NSImage imageNamed:@"cursorMagnify.tiff"]
155                                                        hotSpot:NSMakePoint(7.0, 7.0)]];
156}
157
158/* modified: 2012-08-13
159 */
160- (void)magnifyRegion:(NSRect)region
161{   NSPoint	center;
162    int		row;
163    VFloat	scaleFactor, scale = 0.0;
164    NSRect	bRect;
165
166    bRect = [[self documentView] visibleRect];
167    if ( region.size.width && region.size.height )
168        scale = (bRect.size.width+bRect.size.height) / (region.size.width+region.size.height);
169    center.x = region.origin.x+region.size.width/2.0;
170    center.y = region.origin.y+region.size.height/2.0;
171
172    /* get row of popup relating to current scale */
173    for (row=0; row<[resPopupListButton numberOfItems]; row++)
174        if (Diff((VFloat)[[resPopupListButton itemAtIndex:row] tag] / 100.0, oldScaleFactor) < 0.001)
175            break;
176    row++;
177    if (row >= [resPopupListButton numberOfItems])
178        return;
179
180    /* climb up the popup entries and get the new row */
181    for ( ; scale > 0.0 && row<[resPopupListButton numberOfItems]-1; row++ )
182    {
183        scaleFactor = [[resPopupListButton itemAtIndex:row] tag] / 100.0;
184        if (scaleFactor / oldScaleFactor >= scale)
185            break;
186    }
187
188    [resPopupListButton setTitle:[resPopupListButton itemTitleAtIndex:row]];
189    scaleFactor = [[resPopupListButton itemAtIndex:row] tag] / 100.0;
190
191    [document scale:NSMakeSize(scaleFactor/oldScaleFactor, scaleFactor/oldScaleFactor) withCenter:center];
192    oldScaleFactor = scaleFactor;
193}
194
195- (void)changeScale:sender
196{   NSPoint	center;
197    NSRect	bRect;
198    VFloat	scaleFactor = [[sender selectedItem] tag] / 100.0;
199
200    if (scaleFactor != oldScaleFactor)
201    {
202
203#if !defined(GNUSTEP_BASE_VERSION) && !defined(__APPLE__)	// OpenStep 4.2
204        bRect = [[self documentView] bounds];
205        if ( ![[self documentView] caching] &&
206             Max(bRect.size.width, bRect.size.height)/scaleFactor >= 10000.0)
207            return;
208#endif
209
210        //bRect = [[self documentView] bounds];
211        bRect = [[self documentView] visibleRect];
212        center.x = bRect.origin.x+bRect.size.width /2.0;
213        center.y = bRect.origin.y+bRect.size.height/2.0;
214        //[window disableDisplay];
215        [document scale:NSMakeSize(scaleFactor/oldScaleFactor, scaleFactor/oldScaleFactor) withCenter:center];
216        //[[self window] reenableDisplay];
217        //[self display];
218        oldScaleFactor = scaleFactor;
219    }
220
221    [[self window] makeFirstResponder:[document documentView]];
222}
223
224- (void)scaleTo:(float)scaleFactor
225{   NSPoint	center;
226    NSRect	bRect;
227
228    if (scaleFactor != oldScaleFactor)
229    {   int row = -1;
230
231        for (row=0; row<[resPopupListButton numberOfItems]; row++)
232            if (Diff((float)[[resPopupListButton itemAtIndex:row] tag] / 100.0, scaleFactor) < 0.001)
233                [resPopupListButton selectItemAtIndex:row];
234
235#if !defined(GNUSTEP_BASE_VERSION) && !defined(__APPLE__)	// OpenStep 4.2
236        bRect = [[self documentView] bounds];
237        if ( ![[self documentView] caching] &&
238            Max(bRect.size.width, bRect.size.height)/scaleFactor >= 10000.0)
239            return;
240#endif
241
242        bRect = [[self documentView] visibleRect];
243        center.x = bRect.origin.x+bRect.size.width /2.0;
244        center.y = bRect.origin.y+bRect.size.height/2.0;
245        [document scale:NSMakeSize(scaleFactor/oldScaleFactor, scaleFactor/oldScaleFactor) withCenter:center];
246        oldScaleFactor = scaleFactor;
247    }
248
249    [[self window] makeFirstResponder:[document documentView]];
250}
251
252- (float)scaleFactor
253{
254    return oldScaleFactor;
255}
256
257- (void)setDocumentView:(NSView *)aView
258{
259    [super setDocumentView:aView];
260}
261
262/*
263 * tile gets called whenever the scrollView changes size.  Its job is to resize
264 * all of the scrollView's "tiled" views (scrollers, contentView and any other
265 * views we might want to place in the scrollView's bounds).
266 */
267- (void)tile
268{   static float    popupWidth = 0;
269    NSRect          scrollerRect, buttonRect;
270
271    /* resize and arrange the scrollers and contentView as usual */
272    [super tile];
273
274    /* FIXME: this is a hack to avoid crash with NSRulerView which is released too often
275     * I have no idea where this release comes from!
276     */
277    {   int	i;
278
279        for (i=[[self subviews] count]-1; i>=0; i-- )
280        {   id	subview = [[self subviews] objectAtIndex:i];
281
282            if ([subview isKindOfClass:[NSRulerView class]])
283            {
284                if ([subview retainCount] <= 2)
285                    [subview retain];
286                break;
287            }
288        }
289    }
290
291    if ( !box ) // on Apple we can't tile the scroller, it's too small (box = nil)
292        return;
293
294    if ([box superview] != self)	// make sure the popup list is subview of us
295        [self addSubview:box];
296
297    if (!popupWidth)			// get popup width
298    {	buttonRect = [box frame];
299        popupWidth = buttonRect.size.width;
300    }
301
302    scrollerRect = [[self horizontalScroller] frame];	// make the hScroller smaller + stick the popup next to it
303    NSDivideRect(scrollerRect, &buttonRect, &scrollerRect, popupWidth, 2);
304    [[self horizontalScroller] setFrame:scrollerRect];
305    [box setFrame:buttonRect];
306}
307
308@end
309