Basic plugin architecture
[MacVim.git] / src / MacVim / PlugInGUI.m
blob8797d235efdfb9886da142c283f121296bf47b93
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
12  * MMPlugInViewHeader
13  *
14  * Essentially just a title bar for a plugin view.  Handles drawing the
15  * drag-and-drop line where a new plugin view will be inserted.
16  *
17  * MMPlugInView
18  *
19  * This contains a single view added by a plugin.
20  *
21  * MMPlugInViewContainer
22  *
23  * This contains multiple MMPlugInViews.  It handles the drag and drop aspects
24  * of the views, as well.
25  *
26  * Author: Matt Tolton
27  */
28 #import "MacVim.h"
30 #ifdef MM_ENABLE_PLUGINS
32 #import "PlugInGUI.h"
33 #import "CTGradient.h"
35 NSString *MMPlugInViewPboardType = @"MMPlugInViewPboardType";
37 @implementation MMPlugInViewHeader
39 - (void)mouseDown:(NSEvent *)theEvent
41     // Make image from view
42     NSView *view = self;
43     [view lockFocus];
44     NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc]
45         initWithFocusedViewRect: [view bounds]] autorelease];
46     [view unlockFocus];
48     NSImage *image = [[[NSImage alloc] initWithSize: [view bounds].size]
49                         autorelease];
50     [image addRepresentation:bitmap];
52     NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
54     [pboard declareTypes:[NSArray arrayWithObject:MMPlugInViewPboardType]
55                    owner:self];
57     NSPoint pt = [view convertPoint:[view bounds].origin
58                              toView:[controller plugInSubview]];
59     [[controller plugInSubview] dragImage:image
60                                        at:pt
61                                    offset:NSMakeSize(0, 0)
62                                     event:theEvent
63                                pasteboard:pboard
64                                    source:controller
65                                 slideBack:YES];
68 - (void)drawRect:(NSRect)rect
70     NSColor *startColor;
71     startColor = [NSColor colorWithCalibratedRed:.600
72                                            green:.600
73                                             blue:.600
74                                            alpha:1.0];
76     NSColor *endColor = [NSColor colorWithCalibratedRed:.800
77                                                   green:.800
78                                                    blue:.800
79                                                   alpha:1.0];
81     CTGradient *grad = [CTGradient gradientWithBeginningColor:startColor
82                                                   endingColor:endColor];
83     [grad fillRect:[self bounds] angle:90];
85     MMPlugInView *dropView = [[controller container] dropView];
87     if (dropView == [controller plugInSubview]) {
88         NSRect insertionRect = NSMakeRect(0,[self bounds].size.height - 2,
89                 [self bounds].size.width, 2);
90         [[NSColor redColor] set];
91         NSRectFill(insertionRect);
92     }
95 - (BOOL)isOpaque
97     return YES;
100 - (NSRect)dragRect
102     return NSMakeRect(0, [self bounds].size.height - 6, [self bounds].size.width, 6);
105 @end
107 @implementation MMPlugInView
109 - (MMPlugInViewController *)controller
111     return controller;
114 @end
116 @implementation MMPlugInViewController
118 - (id)initWithView:(NSView *)view title:(NSString *)title
120     if ((self = [super init]) == nil) return nil;
122     if (![NSBundle loadNibNamed:@"PlugInView" owner:self])
123         NSLog(@"Error loading PlugIn nib");
125     [titleField setStringValue:title];
127     [plugInSubview setMinDimension:50
128                    andMaxDimension:0.0];
130     [view setFrame:[contentView bounds]];
131     [contentView addSubview:view];
133     return self;
136 - (RBSplitSubview *)plugInSubview
138     return plugInSubview;
141 - (void)moveToContainer:(MMPlugInViewContainer *)container
143     if ([plugInSubview splitView]) {
144         [plugInSubview removeFromSuperview];
145     }
146     [container addSubview:plugInSubview];
149 - (void)moveToContainer:(MMPlugInViewContainer *)container before:(MMPlugInView *)lowerView
151     if ([plugInSubview splitView]) {
152         [plugInSubview removeFromSuperview];
153     }
154     [container addSubview:plugInSubview positioned:NSWindowBelow relativeTo:lowerView];
157 - (MMPlugInViewHeader *)headerView
159     return headerView;
162 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
164     if (isLocal)
165         return NSDragOperationPrivate;
166     else
167         return NSDragOperationNone;
170 - (void)dropViewChanged {
171     [headerView setNeedsDisplay:YES];
174 - (MMPlugInViewContainer *)container {
175     return (MMPlugInViewContainer *)[plugInSubview splitView];
177 @end
179 @implementation MMPlugInViewContainer
181 - (id)initWithFrame:(NSRect)frame
183     if ((self = [super initWithFrame:frame]) == nil) return nil;
185     [self registerForDraggedTypes:
186             [NSArray arrayWithObjects:MMPlugInViewPboardType, nil]];
188     [self setVertical:NO];
189     [self setDelegate:self];
191     fillerView = [[RBSplitSubview alloc] initWithFrame:NSMakeRect(0,0,0,0)];
192     [fillerView setHidden:YES];
194     return self;
197 - (void)dealloc
199     [fillerView release]; fillerView = nil;
200     [super dealloc];
203 - (unsigned int)splitView:(RBSplitView*)sender dividerForPoint:(NSPoint)point
204                 inSubview:(RBSplitSubview*)subview
206     MMPlugInViewController *controller = [(MMPlugInView *)subview controller];
207     MMPlugInViewHeader *header = [controller headerView];
209     if ([header mouse:[header convertPoint:point fromView:sender]
210                inRect:[header dragRect]])
211         return [subview position] - 1;
213     return NSNotFound;
216 - (NSRect)splitView:(RBSplitView*)sender cursorRect:(NSRect)rect
217          forDivider:(unsigned int)theDivider
220     if (theDivider != 0) return NSZeroRect;
222     int i;
223     for (i = 1;; i++) {
224         MMPlugInView *view = (MMPlugInView *)[sender subviewAtPosition:i];
225         if (!view) break;
227         MMPlugInViewHeader *header = [[view controller] headerView];
228         NSRect rect = [header dragRect];
229         rect = [sender convertRect:rect fromView:header];
230         [sender addCursorRect:rect
231                        cursor:[RBSplitView cursor:RBSVHorizontalCursor]];
233     }
235     return NSZeroRect;
238 - (void)clearDragInfo
240     if (dropView) {
241         MMPlugInView *save = dropView;
242         dropView = nil;
243         [[save controller] dropViewChanged];
244     }
247 // point should be in the window's coordinate system
248 - (void)updateDragInfo:(id<NSDraggingInfo>)info
251     [self clearDragInfo];
253     if (!([info draggingSourceOperationMask] & NSDragOperationPrivate)) return;
255     if (![[info draggingSource] isKindOfClass:[MMPlugInViewController class]]) return; 
257     // for now has to be THIS container.  in the future, it will be ok for any
258     // container associated with the same vim instance
259     if ([[info draggingSource] container] != self) return;
261     // XXX for now we just use the view that the mouse is currently over, and
262     // always insert "above" that view.  In the future, we might want to try to
263     // find the divider that the mouse is closest to and have the dropView be
264     // the view below that divider.
266     NSPoint point = [info draggingLocation];
268     int i;
269     for (i = 0;; i++) {
270         MMPlugInView *subview = (MMPlugInView *)[self subviewAtPosition:i];
271         if (!subview) break;
273         if ([subview mouse:[subview convertPoint:point fromView:nil]
274                     inRect:[subview bounds]]) {
275             dropView = subview;
276             break;
277         }
278     }
280     if ([[info draggingSource] plugInSubview] == dropView)
281         dropView = nil;
283     if (dropView) [[dropView controller] dropViewChanged];
286 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
288     [self updateDragInfo:sender];
290     if (dropView != nil)
291         return NSDragOperationPrivate;
292     else
293         return NSDragOperationNone;
296 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
298     [self updateDragInfo:sender];
300     if (dropView != nil)
301         return NSDragOperationPrivate;
303     return NSDragOperationNone;
306 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender
308     [self updateDragInfo:sender];
309     return dropView != nil;
312 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
314     MMPlugInViewController *source = [sender draggingSource];
315     [source moveToContainer:self before:dropView];
316     [self clearDragInfo];
317     return YES;
320 - (void)draggingExited:(id<NSDraggingInfo>)sender
322     [self clearDragInfo];
326 - (MMPlugInView *)dropView {
327     return dropView;
330 @end
332 #endif