Moved MacVim project to src/MacVim and removed runtime folder
[MacVim.git] / src / MacVim / PSMTabBarControl / source / PSMTabDragAssistant.m
blobc5b937693cb57171fc0e46191534c526926afc2e
1 //
2 //  PSMTabDragAssistant.m
3 //  PSMTabBarControl
4 //
5 //  Created by John Pannell on 4/10/06.
6 //  Copyright 2006 Positive Spin Media. All rights reserved.
7 //
9 #import "PSMTabDragAssistant.h"
10 #import "PSMTabBarCell.h"
11 #import "PSMTabStyle.h"
14 @implementation PSMTabDragAssistant
16 static PSMTabDragAssistant *sharedDragAssistant = nil;
18 #pragma mark -
19 #pragma mark Creation/Destruction
21 + (PSMTabDragAssistant *)sharedDragAssistant
23     if (!sharedDragAssistant){
24         sharedDragAssistant = [[PSMTabDragAssistant alloc] init];
25     }
26     
27     return sharedDragAssistant;
30 - (id)init
32     if(self = [super init]){
33         _sourceTabBar = nil;
34         _destinationTabBar = nil;
35         _participatingTabBars = [[NSMutableSet alloc] init];
36         _draggedCell = nil;
37         _animationTimer = nil;
38         _sineCurveWidths = [[NSMutableArray alloc] initWithCapacity:kPSMTabDragAnimationSteps];
39         _targetCell = nil;
40         _isDragging = NO;
41     }
42     
43     return self;
46 - (void)dealloc
48     [_sourceTabBar release];
49     [_destinationTabBar release];
50     [_participatingTabBars release];
51     [_draggedCell release];
52     [_animationTimer release];
53     [_sineCurveWidths release];
54     [_targetCell release];
55     [super dealloc];
58 #pragma mark -
59 #pragma mark Accessors
61 - (PSMTabBarControl *)sourceTabBar
63     return _sourceTabBar;
66 - (void)setSourceTabBar:(PSMTabBarControl *)tabBar
68     [tabBar retain];
69     [_sourceTabBar release];
70     _sourceTabBar = tabBar;
73 - (PSMTabBarControl *)destinationTabBar
75     return _destinationTabBar;
78 - (void)setDestinationTabBar:(PSMTabBarControl *)tabBar
80     [tabBar retain];
81     [_destinationTabBar release];
82     _destinationTabBar = tabBar;
85 - (PSMTabBarCell *)draggedCell
87     return _draggedCell;
90 - (void)setDraggedCell:(PSMTabBarCell *)cell
92     [cell retain];
93     [_draggedCell release];
94     _draggedCell = cell;
97 - (int)draggedCellIndex
99     return _draggedCellIndex;
102 - (void)setDraggedCellIndex:(int)value
104     _draggedCellIndex = value;
107 - (BOOL)isDragging
109     return _isDragging;
112 - (void)setIsDragging:(BOOL)value
114     _isDragging = value;
117 - (NSPoint)currentMouseLoc
119     return _currentMouseLoc;
122 - (void)setCurrentMouseLoc:(NSPoint)point
124     _currentMouseLoc = point;
127 - (PSMTabBarCell *)targetCell
129     return _targetCell;
132 - (void)setTargetCell:(PSMTabBarCell *)cell
134     [cell retain];
135     [_targetCell release];
136     _targetCell = cell;
139 #pragma mark -
140 #pragma mark Functionality
142 - (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event
144     [self setIsDragging:YES];
145     [self setSourceTabBar:control];
146     [self setDestinationTabBar:control];
147     [_participatingTabBars addObject:control];
148     [self setDraggedCell:cell];
149     [self setDraggedCellIndex:[[control cells] indexOfObject:cell]];
150     
151     NSRect cellFrame = [cell frame];
152     // list of widths for animation
153     int i;
154     float cellWidth = cellFrame.size.width;
155     for(i = 0; i < kPSMTabDragAnimationSteps; i++){
156         int thisWidth;
157         thisWidth = (int)(cellWidth - ((cellWidth/2.0) + ((sin((PI/2.0) + ((float)i/(float)kPSMTabDragAnimationSteps)*PI) * cellWidth) / 2.0)));
158         [_sineCurveWidths addObject:[NSNumber numberWithInt:thisWidth]];
159     }
160     
161     // hide UI buttons
162     [[control overflowPopUpButton] setHidden:YES];
163     [[control addTabButton] setHidden:YES];
164     
165     [[NSCursor closedHandCursor] set];
166     
167     NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
168     NSImage *dragImage = [cell dragImageForRect:cellFrame];
169     [[cell indicator] removeFromSuperview];
170     [self distributePlaceholdersInTabBar:control withDraggedCell:cell];
172     if([control isFlipped]){
173         cellFrame.origin.y += cellFrame.size.height;
174     }
175     [cell setHighlighted:NO];
176     NSSize offset = NSZeroSize;
177     [pboard declareTypes:[NSArray arrayWithObjects:@"PSMTabBarControlItemPBType", nil] owner: nil];
178     [pboard setString:[[NSNumber numberWithInt:[[control cells] indexOfObject:cell]] stringValue] forType:@"PSMTabBarControlItemPBType"];
179     _animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/30.0) target:self selector:@selector(animateDrag:) userInfo:nil repeats:YES];
180     [control dragImage:dragImage at:cellFrame.origin offset:offset event:event pasteboard:pboard source:control slideBack:YES];
183 - (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc
185     [self setDestinationTabBar:control];
186     [self setCurrentMouseLoc:mouseLoc];
187     // hide UI buttons
188     [[control overflowPopUpButton] setHidden:YES];
189     [[control addTabButton] setHidden:YES];
190     if(![[[control cells] objectAtIndex:0] isPlaceholder])
191         [self distributePlaceholdersInTabBar:control];
192     [_participatingTabBars addObject:control];
195 - (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc
197     if([self destinationTabBar] != control)
198         [self setDestinationTabBar:control];
199     [self setCurrentMouseLoc:mouseLoc];
202 - (void)draggingExitedTabBar:(PSMTabBarControl *)control
204     [self setDestinationTabBar:nil];
205     [self setCurrentMouseLoc:NSMakePoint(-1.0, -1.0)];
208 - (void)performDragOperation
210 #if 1
211     // move cell
212     [[[self destinationTabBar] cells] replaceObjectAtIndex:[[[self destinationTabBar] cells] indexOfObject:[self targetCell]] withObject:[self draggedCell]];
213     [[self draggedCell] setControlView:[self destinationTabBar]];
214     // move actual NSTabViewItem
215     if([self sourceTabBar] != [self destinationTabBar]){
216         [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
217         [[[self destinationTabBar] tabView] addTabViewItem:[[self draggedCell] representedObject]];
218     }
219     [self finishDrag];
220 #else
221     unsigned idx = [[[self destinationTabBar] cells] indexOfObject:[self targetCell]];
222     
223     // move cell
224     [[[self destinationTabBar] cells] replaceObjectAtIndex:idx withObject:[self draggedCell]];
225     [[self draggedCell] setControlView:[self destinationTabBar]];
226     // move actual NSTabViewItem
227     if([self sourceTabBar] != [self destinationTabBar]){
228         [[[self sourceTabBar] tabView] removeTabViewItem:[[self draggedCell] representedObject]];
229         idx = [[[self destinationTabBar] cells] indexOfObject:[self draggedCell]];
230         NSLog(@"Inserting at index %d", idx);
231         [[[self destinationTabBar] tabView] insertTabViewItem:[[self draggedCell] representedObject] atIndex:idx];
232     }
233     [self finishDrag];
234 #endif
237 - (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
239     if([self isDragging]){  // means there was not a successful drop (performDragOperation)
240         // put cell back
241         [[[self sourceTabBar] cells] insertObject:[self draggedCell] atIndex:[self draggedCellIndex]];
242         [self finishDrag];
243     }
246 - (void)finishDrag
248     [self setIsDragging:NO];
249     [self removeAllPlaceholdersFromTabBar:[self sourceTabBar]];
250     [self setSourceTabBar:nil];
251     [self setDestinationTabBar:nil];
252     NSEnumerator *e = [_participatingTabBars objectEnumerator];
253     PSMTabBarControl *tabBar;
254     while(tabBar = [e nextObject]){
255         [self removeAllPlaceholdersFromTabBar:tabBar];
256     }
257     [_participatingTabBars removeAllObjects];
258     [self setDraggedCell:nil];
259     [_animationTimer invalidate];
260     _animationTimer = nil;
261     [_sineCurveWidths removeAllObjects];
262     [self setTargetCell:nil];
265 #pragma mark -
266 #pragma mark Animation
268 - (void)animateDrag:(NSTimer *)timer
270     NSEnumerator *e = [_participatingTabBars objectEnumerator];
271     PSMTabBarControl *tabBar;
272     while(tabBar = [e nextObject]){
273         [self calculateDragAnimationForTabBar:tabBar];
274         [[NSRunLoop currentRunLoop] performSelector:@selector(display) target:tabBar argument:nil order:1 modes:[NSArray arrayWithObjects:@"NSEventTrackingRunLoopMode", @"NSDefaultRunLoopMode", nil]];
275     }
278 - (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control
280     BOOL removeFlag = YES;
281     NSMutableArray *cells = [control cells];
282     int i, cellCount = [cells count];
283     float xPos = [[control style] leftMarginForTabBarControl];
284     
285     // identify target cell
286     // mouse at beginning of tabs
287     NSPoint mouseLoc = [self currentMouseLoc];
288     if([self destinationTabBar] == control){
289         removeFlag = NO;
290         if(mouseLoc.x < [[control style] leftMarginForTabBarControl]){
291             [self setTargetCell:[cells objectAtIndex:0]];
292             goto layout;
293         }
294         
295         NSRect overCellRect;
296         PSMTabBarCell *overCell = [control cellForPoint:mouseLoc cellFrame:&overCellRect];
297         if(overCell){
298             // mouse among cells - placeholder
299             if([overCell isPlaceholder]){
300                 [self setTargetCell:overCell];
301                 goto layout;
302             }
303             
304             // non-placeholders
305             if(mouseLoc.x < (overCellRect.origin.x + (overCellRect.size.width / 2.0))){
306                 // mouse on left side of cell
307                 [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] - 1)]];
308                 goto layout;
309             } else {
310                 // mouse on right side of cell
311                 [self setTargetCell:[cells objectAtIndex:([cells indexOfObject:overCell] + 1)]];
312                 goto layout;
313             }
314         } else {
315             // out at end - must find proper cell (could be more in overflow menu)
316             [self setTargetCell:[control lastVisibleTab]];
317             goto layout;
318         }
319     } else {
320         [self setTargetCell:nil];
321     }
322     
323 layout: 
324     for(i = 0; i < cellCount; i++){
325         PSMTabBarCell *cell = [cells objectAtIndex:i];
326         NSRect newRect = [cell frame];
327         if(![cell isInOverflowMenu]){
328             if([cell isPlaceholder]){
329                 if(cell == [self targetCell]){
330                     [cell setCurrentStep:([cell currentStep] + 1)];
331                 } else {
332                     [cell setCurrentStep:([cell currentStep] - 1)];
333                     if([cell currentStep] > 0){
334                         removeFlag = NO;
335                     }
336                 }
337                 newRect.size.width = [[_sineCurveWidths objectAtIndex:[cell currentStep]] intValue];
338             }
339         } else {
340             break;
341         }
342         newRect.origin.x = xPos;
343         [cell setFrame:newRect];
344         if([cell indicator])
345             [[cell indicator] setFrame:[[control style] indicatorRectForTabCell:cell]];
346         xPos += newRect.size.width;
347     }
348     if(removeFlag){
349         [_participatingTabBars removeObject:control];
350         [self removeAllPlaceholdersFromTabBar:control];
351     }
354 #pragma mark -
355 #pragma mark Placeholders
357 - (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell
359     // called upon first drag - must distribute placeholders
360     [self distributePlaceholdersInTabBar:control];
361     // replace dragged cell with a placeholder, and clean up surrounding cells
362     int cellIndex = [[control cells] indexOfObject:cell];
363     PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:YES inControlView:control] autorelease];
364     [[control cells] replaceObjectAtIndex:cellIndex withObject:pc];
365     [[control cells] removeObjectAtIndex:(cellIndex + 1)];
366     [[control cells] removeObjectAtIndex:(cellIndex - 1)];
367     return;
370 - (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control
372     int i, numVisibleTabs = [control numberOfVisibleTabs];
373     for(i = 0; i < numVisibleTabs; i++){
374         PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control] autorelease]; 
375         [[control cells] insertObject:pc atIndex:(2 * i)];
376     }
377     if(numVisibleTabs > 0){
378         PSMTabBarCell *pc = [[[PSMTabBarCell alloc] initPlaceholderWithFrame:[[self draggedCell] frame] expanded:NO inControlView:control] autorelease];
379         if([[control cells] count] > (2 * numVisibleTabs)){
380             [[control cells] insertObject:pc atIndex:(2 * numVisibleTabs)];
381         } else {
382             [[control cells] addObject:pc];
383         }
384     }
387 - (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control
389     int i, cellCount = [[control cells] count];
390     for(i = (cellCount - 1); i >= 0; i--){
391         PSMTabBarCell *cell = [[control cells] objectAtIndex:i];
392         if([cell isPlaceholder])
393             [[control cells] removeObject:cell];
394     }
395     // redraw
396     [[NSRunLoop currentRunLoop] performSelector:@selector(update) target:control argument:nil order:1 modes:[NSArray arrayWithObjects:@"NSEventTrackingRunLoopMode", @"NSDefaultRunLoopMode", nil]];
397     [[NSRunLoop currentRunLoop] performSelector:@selector(display) target:control argument:nil order:1 modes:[NSArray arrayWithObjects:@"NSEventTrackingRunLoopMode", @"NSDefaultRunLoopMode", nil]];
400 #pragma mark -
401 #pragma mark Archiving
403 - (void)encodeWithCoder:(NSCoder *)aCoder {
404     //[super encodeWithCoder:aCoder];
405     if ([aCoder allowsKeyedCoding]) {
406         [aCoder encodeObject:_sourceTabBar forKey:@"sourceTabBar"];
407         [aCoder encodeObject:_destinationTabBar forKey:@"destinationTabBar"];
408         [aCoder encodeObject:_participatingTabBars forKey:@"participatingTabBars"];
409         [aCoder encodeObject:_draggedCell forKey:@"draggedCell"];
410         [aCoder encodeInt:_draggedCellIndex forKey:@"draggedCellIndex"];
411         [aCoder encodeBool:_isDragging forKey:@"isDragging"];
412         [aCoder encodeObject:_animationTimer forKey:@"animationTimer"];
413         [aCoder encodeObject:_sineCurveWidths forKey:@"sineCurveWidths"];
414         [aCoder encodePoint:_currentMouseLoc forKey:@"currentMouseLoc"];
415         [aCoder encodeObject:_targetCell forKey:@"targetCell"];
416     }
419 - (id)initWithCoder:(NSCoder *)aDecoder {
420     //self = [super initWithCoder:aDecoder];
421     //if (self) {
422         if ([aDecoder allowsKeyedCoding]) {
423             _sourceTabBar = [[aDecoder decodeObjectForKey:@"sourceTabBar"] retain];
424             _destinationTabBar = [[aDecoder decodeObjectForKey:@"destinationTabBar"] retain];
425             _participatingTabBars = [[aDecoder decodeObjectForKey:@"participatingTabBars"] retain];
426             _draggedCell = [[aDecoder decodeObjectForKey:@"draggedCell"] retain];
427             _draggedCellIndex = [aDecoder decodeIntForKey:@"draggedCellIndex"];
428             _isDragging = [aDecoder decodeBoolForKey:@"isDragging"];
429             _animationTimer = [[aDecoder decodeObjectForKey:@"animationTimer"] retain];
430             _sineCurveWidths = [[aDecoder decodeObjectForKey:@"sineCurveWidths"] retain];
431             _currentMouseLoc = [aDecoder decodePointForKey:@"currentMouseLoc"];
432             _targetCell = [[aDecoder decodeObjectForKey:@"targetCell"] retain];
433         }
434     //}
435     return self;
439 @end