/* NSSplitView.h Allows multiple views to share a region in a window Copyright (C) 1996 Free Software Foundation, Inc. Author: Robert Vasvari Date: Jul 1998 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #import #import #import #include #include /* Backend protocol - methods that must be implemented by the backend to complete the class */ @protocol NSSplitViewBackend - (void) _fillRect: (NSRect)rect; @end @implementation NSSplitView /* API Methods */ - (void)mouseDown:(NSEvent *)theEvent { static NSRect oldRect; //only one can be dragged at a time NSPoint p; NSEvent *e; NSRect r, r1, bigRect, vis; id v, prev=nil; float minCoord,maxCoord; NSArray *subs=[self subviews]; int offset=0,i,count=[subs count]; float divVertical, divHorizontal, div=[self dividerThickness]; NSColor *divColor=[self dividerColor]; NSDate *farAway=[NSDate distantFuture]; unsigned int eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSMouseMovedMask | NSPeriodicMask; /* if there are less the two subviews, there is nothing to do */ if(count<2) return; [[self window] setAcceptsMouseMovedEvents:YES]; vis = [self visibleRect]; /* find out which divider it is */ p=[theEvent locationInWindow]; p = [self convertPoint:p fromView:nil]; for(i=0;iNSMinY(r)) && (p.yp.y) { offset=i; /* get the enclosing rect for the two views */ if(prev) r=[prev frame]; else r = NSZeroRect; if(v) r1=[v frame]; bigRect=r; bigRect=NSUnionRect(r1 , bigRect); break; } prev=v; } else { if((p.x>NSMinX(r)) && (p.xp.x) { offset=i; /* get the enclosing rect for the two views */ if(prev) r=[prev frame]; else r = NSZeroRect; if(v) r1=[v frame]; bigRect=r; bigRect=NSUnionRect(r1 , bigRect); break; } prev=v; } } if([self isVertical]) { divVertical=div; divHorizontal=NSWidth([self frame]); /* set the default limits on the dragging */ minCoord=NSMinY(bigRect)+divVertical; maxCoord=NSHeight(bigRect)+NSMinY(bigRect)-divVertical; } else { divHorizontal=div; divVertical=NSHeight([self frame]); /* set the default limits on the dragging */ minCoord=NSMinX(bigRect)+divHorizontal; maxCoord=NSWidth(bigRect)+NSMinX(bigRect)-divHorizontal; } /* find out what the dragging limit is */ if(delegate && [delegate respondsToSelector:@selector (splitView:constrainMinCoordinate:maxCoordinate:ofSubviewAt:)]) { if([self isVertical]) { float delMinY=minCoord, delMaxY=maxCoord; [delegate splitView:self constrainMinCoordinate:&delMinY maxCoordinate:&delMaxY ofSubviewAt:offset]; /* we are still constrained by the original bounds */ if(delMinY>minCoord) minCoord=delMinY; if(delMaxYminCoord) minCoord=delMinX; if(delMaxXmaxCoord) p.y=maxCoord; r.origin.y = p.y-(divVertical/2.); r.origin.x = NSMinX(vis); } else { if(p.xmaxCoord) p.x=maxCoord; r.origin.x = p.x-(divHorizontal/2.); r.origin.y = NSMinY(vis); } NSDebugLog(@"drawing divider at x:%d, y:%d, w:%d, h:%d\n", (int)NSMinX(r),(int)NSMinY(r),(int)NSWidth(r), (int)NSHeight(r)); [self _fillRect: r]; [[GPSDrawContext currentContext] flush]; oldRect=r; e=[[NSApplication sharedApplication] nextEventMatchingMask:eventMask untilDate:farAway inMode:NSEventTrackingRunLoopMode dequeue:YES]; } [self unlockFocus]; [NSEvent stopPeriodicEvents]; /* resize the subviews accordingly */ r = [prev frame]; if([self isVertical]) { r.size.height=p.y-NSMinY(bigRect)-(divVertical/2.); if(NSHeight(r) < 1.) r.size.height=1.; } else { r.size.width=p.x-NSMinX(bigRect)-(divHorizontal/2.); if(NSWidth(r) < 1.) r.size.width=1.; } [prev setFrame:r]; NSDebugLog(@"drawing PREV at x:%d, y:%d, w:%d, h:%d\n", (int)NSMinX(r),(int)NSMinY(r),(int)NSWidth(r),(int)NSHeight(r)); r1 = [v frame]; if([self isVertical]) { r1.origin.y=p.y+(divVertical/2.); if(NSMinY(r1) < 0.) r1.origin.y=0.; r1.size.height=NSHeight(bigRect)-NSHeight(r)-divVertical; if(NSHeight(r) < 1.) r.size.height=1.; } else { r1.origin.x=p.x+(divHorizontal/2.); if(NSMinX(r1) < 0.) r1.origin.x=0.; r1.size.width=NSWidth(bigRect)-NSWidth(r)-divHorizontal; if(NSWidth(r1) < 1.) r1.size.width=1.; } [v setFrame:r1]; NSDebugLog(@"drawing LAST at x:%d, y:%d, w:%d, h:%d\n", (int)NSMinX(r1),(int)NSMinY(r1),(int)NSWidth(r1), (int)NSHeight(r1)); [[self window] invalidateCursorRectsForView:self]; RETURN_LABEL: [[self window] setAcceptsMouseMovedEvents:NO]; [self setNeedsDisplay:YES]; } - (void)adjustSubviews { NSRect fr=[self frame]; NSSize newSize; NSPoint newPoint; float total=0.; if(delegate && [delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)]) { [delegate splitView:self resizeSubviewsWithOldSize:fr.size]; } else { /* split the area up evenly */ NSArray *subs=[self subviews]; int i, div, count=[subs count]; id v; int w,h; NSRect r, bd=[self bounds]; div=(int)([self dividerThickness]*(count-1)); w=(int)ceil((NSWidth(bd)-div)/count); h=(int)ceil((NSHeight(bd)-div)/count); for(i=0;i(NSHeight(bd)-div)) { newSize.height-=1.; } total+=newSize.height; newPoint=NSMakePoint(0,(float)ceil(i ? (i*([self dividerThickness]+h)) : 0)); if(newSize.height<1) newSize.height=1.; if(newPoint.y<1) newPoint.y=1.; } else { newSize=NSMakeSize(w, NSHeight(bd)); /* make sure nothing spills over */ while((total+newSize.width)>(NSWidth(bd)-div)) { newSize.width-=1.; } total+=newSize.width; newPoint=NSMakePoint((float)ceil(i ? (i*([self dividerThickness]+w)) : 0), 0); if(newSize.width<1) newSize.width=1.; if(newPoint.x<1) newPoint.x=1.; } [v setFrameSize: newSize]; [v setFrameOrigin: newPoint]; } } [[NSNotificationCenter defaultCenter] postNotificationName:NSSplitViewDidResizeSubviewsNotification object:self]; } - (void)addSubview:(NSView *)aView positioned:(NSWindowOrderingMode)place relativeTo:(NSView *)otherView { [super addSubview:aView positioned:place relativeTo:otherView]; /* register the subviews up for notification */ //[[NSNotificationCenter defaultCenter] addObserver:aView // selector:@selector(splitViewDidResizeSubviews:) // name:NSSplitViewDidResizeSubviewsNotification object:self]; [self adjustSubviews]; } - (void)addSubview:aView { [super addSubview:aView]; [self adjustSubviews]; } - (float)dividerThickness //defaults to 8 { return dividerWidth; } - (void)setDividerThickNess:(float)newWidth { dividerWidth=newWidth; } - (float)draggedBarWidth //defaults to 8 { return draggedBarWidth; } - (void)setDraggedBarWidth:(float)newWidth { draggedBarWidth=newWidth; } NSPoint centerSizeInRect(NSSize innerSize, NSRect outerRect) { NSPoint p; p.x=MAX(NSMidX(outerRect)-(innerSize.width/2.),0.); p.y=MAX(NSMidY(outerRect)-(innerSize.height/2.),0.); return p; } NSPoint centerRectInRect(NSRect innerRect, NSRect outerRect) { return centerSizeInRect(innerRect.size,outerRect); } - (void)drawDividerInRect:(NSRect)aRect { NSPoint dimpleOrigin; NSSize dimpleSize; /* focus is already on self */ if(!dimpleImage) return; dimpleSize=[dimpleImage size]; /* composite into the center of the given rect. Since NSImages are always flipped, we adjust for it here */ dimpleOrigin=centerSizeInRect(dimpleSize,aRect); if([self isFlipped]) dimpleOrigin.y+=dimpleSize.height; [dimpleImage compositeToPoint:dimpleOrigin operation:NSCompositeSourceOver]; } - (void)setVertical:(BOOL)flag /* Vertical splitview has a vertical split bar */ { isVertical=flag; } - (BOOL)isVertical { return isVertical; } - (void)setDimpleImage:(NSImage *)anImage resetDividerThickness:(BOOL)flag { if(dimpleImage==anImage) return; [dimpleImage release]; dimpleImage=[anImage retain]; if(flag) { NSSize s={8.,8.}; if(dimpleImage) s=[dimpleImage size]; [self setDividerThickNess: isVertical ? s.height : s.width]; } } - (void)drawRect:(NSRect)r { NSArray *subs=[self subviews]; int i, count=[subs count]; id v; NSRect divRect; if([self isOpaque]) { [[self backgroundColor] set]; [self _fillRect: [self bounds]]; } /* draw the dimples */ { for(i=0;i<(count-1);i++) { v=[subs objectAtIndex:i]; divRect=[v frame]; if([self isVertical]) { divRect.origin.y=NSMaxY(divRect); divRect.size.height=[self dividerThickness]; } else { divRect.origin.x=NSMaxX(divRect); divRect.size.width=[self dividerThickness]; } [self drawDividerInRect:divRect]; } } } - (NSImage *)dimpleImage { return dimpleImage; } /* Overridden Methods */ - (BOOL)isFlipped { return NO; } - (BOOL)isOpaque { return YES; } - initWithFrame:(NSRect)frameRect { if((self=[super initWithFrame:frameRect])!=nil) { dividerWidth=8; draggedBarWidth=8; isVertical=NO; [self seDividerColor:[NSColor darkGrayColor]]; [self setBackgroundColor:[NSColor lightGrayColor]]; [self setDimpleImage: [NSImage imageNamed:@"common_Dimple.tiff"] resetDividerThickness:YES]; } return self; } - (void)resizeWithOldSuperviewSize:(NSSize)oldSize { [super resizeWithOldSuperviewSize:oldSize]; [self adjustSubviews]; [[self window] invalidateCursorRectsForView:self]; } - delegate { return delegate; } - (void)setDelegate:anObject { if(delegate==anObject) return; [delegate release]; delegate=[anObject retain]; } - (NSColor *)dividerColor { return dividerColor; } - (void)seDividerColor:(NSColor *)aColor { if(dividerColor==aColor) return; [dividerColor release]; dividerColor=[aColor retain]; } - (NSColor *)backgroundColor { return backgroundColor; } - (void)setBackgroundColor:(NSColor *)aColor { if(backgroundColor==aColor) return; [backgroundColor release]; backgroundColor=[aColor retain]; } // // NSCoding protocol // - (void)encodeWithCoder:aCoder { // NSDebugLog(@"NSSplitView: start encoding\n"); [super encodeWithCoder:aCoder]; [aCoder encodeObject:delegate]; [aCoder encodeObject:splitCursor]; [aCoder encodeObject:dimpleImage]; [aCoder encodeObject:backgroundColor]; [aCoder encodeObject:dividerColor]; [aCoder encodeValueOfObjCType:@encode(int) at: ÷rWidth]; [aCoder encodeValueOfObjCType:@encode(int) at: &draggedBarWidth]; [aCoder encodeValueOfObjCType:@encode(BOOL) at: &isVertical]; // NSDebugLog(@"NSView: finish encoding\n"); } - initWithCoder:aDecoder { self=[super initWithCoder:aDecoder]; // NSDebugLog(@"NSSplitView: start decoding\n"); delegate=[aDecoder decodeObject]; splitCursor=[aDecoder decodeObject]; dimpleImage=[aDecoder decodeObject]; backgroundColor=[aDecoder decodeObject]; dividerColor=[aDecoder decodeObject]; [aDecoder decodeValueOfObjCType:@encode(int) at: ÷rWidth]; [aDecoder decodeValueOfObjCType:@encode(int) at: &draggedBarWidth]; [aDecoder decodeValueOfObjCType:@encode(BOOL) at: &isVertical]; // NSDebugLog(@"NSView: finish decoding\n"); return self; } - (void)dealloc { [backgroundColor release]; [dividerColor release]; [dimpleImage release]; [super dealloc]; } @end