forked from ciaran/projectplus
/
ProjectTree.mm
245 lines (202 loc) · 10.4 KB
/
ProjectTree.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#import "JRSwizzle.h"
#import "MHOpenFiles.h"
#import "MHDividerView.h"
#define OakImageAndTextCell NSClassFromString(@"OakImageAndTextCell")
@interface ProjectTree : NSObject
+ (BOOL)preserveTreeState;
@end
@implementation NSWindowController (OakProjectController)
- (void)expandItems:(NSArray*)items inOutlineView:(NSOutlineView*)outlineView toState:(NSDictionary*)treeState
{
unsigned int itemCount = [items count];
for(unsigned int index = 0; index < itemCount; index += 1)
{
NSDictionary *item = [items objectAtIndex:index];
if(not [item objectForKey:@"children"])
continue; // We are only interested in directories
if(NSDictionary *treeItem = [treeState objectForKey:[item objectForKey:@"displayName"]])
{
if([[treeItem objectForKey:@"isExpanded"] boolValue])
[outlineView expandItem:item];
if([[treeItem objectForKey:@"subItems"] count])
[self expandItems:[item objectForKey:@"children"] inOutlineView:outlineView toState:[treeItem objectForKey:@"subItems"]];
}
}
}
- (void)ProjectTree_windowDidLoad
{
[self ProjectTree_windowDidLoad];
if(not [ProjectTree preserveTreeState])
return;
NSArray *rootItems = [self valueForKey:@"rootItems"];
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"OakProjectWindowShowTabBarEnabled"];
[[NSUserDefaults standardUserDefaults] synchronize];
/*NSDictionary *newRoot = [NSDictionary dictionaryWithObjectsAndKeys:
@"Files", @"displayName",
rootItems, @"children",
@"18931726348912", @"lastModified",
@"/", @"sourceDirectory",
@".", @"regexFolderFilter",
nil];
NSMutableArray *newRootItems = [NSMutableArray arrayWithObject:newRoot];
[self setValue:newRootItems forKey:@"rootItems"];*/
[[self valueForKey:@"outlineView"] reloadData];
NSDictionary *treeState = [[NSDictionary dictionaryWithContentsOfFile:[self valueForKey:@"filename"]] objectForKey:@"treeState"];
if(treeState)
{
NSOutlineView *outlineView = [self valueForKey:@"outlineView"];
[self expandItems:rootItems inOutlineView:outlineView toState:treeState];
}
// Update File Browser
//
// First we need to grab the existing file browser outline view and update its styling
// to bring it into this century.
NSOutlineView *fileBrowserOutlineView = [self valueForKey:@"outlineView"];
[fileBrowserOutlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
[fileBrowserOutlineView setRowHeight:14];
[fileBrowserOutlineView setIntercellSpacing:NSMakeSize(3.0, 6.0)];
[fileBrowserOutlineView setAutoresizingMask:NSViewHeightSizable];
// Update the Scroll View
//
// The file browser is inside an acient scroll view with a pre-aqua border, grab the
// scroll view by looking up the view hierarchy so we can axe the border
NSScrollView *fileBrowserScrollView = (NSScrollView *)[[fileBrowserOutlineView superview] superview];
[fileBrowserScrollView setBorderType: NSNoBorder];
// Get the Drawer
//
// A lot of our dimensions and sizing come from the "drawer" that's no longer a drawer
// so localize a variable so we can refer to the drawer a bit more easily
NSView *drawer = [fileBrowserScrollView superview];
// Fill the Space
//
// Update the scrollview to take up the entire drawer by default. As files are opened
// this will change and it will get shorter to allow room for the workspace, but by
// default the file browser takes up all the space and the open files outline has no
// height at all.
[fileBrowserScrollView setFrame:NSMakeRect(0, 0, [drawer frame].size.width, [drawer frame].size.height)];
// Create the Open Files View
//
// No IB for plugins so a lot of code here to programatically create a scroll view to
// contain the open files view
NSRect scrollFrame = NSMakeRect(0.0, [drawer frame].size.height, [drawer frame].size.width, 0.0);
NSScrollView* newScrollView = [[[NSScrollView alloc] initWithFrame:scrollFrame] autorelease];
[newScrollView setVerticalScroller:NO];
[newScrollView setHorizontalScroller:NO];
[newScrollView setBorderType:NSNoBorder];
[newScrollView setAutohidesScrollers:YES];
[newScrollView setAutoresizingMask:NSViewWidthSizable|NSViewMinYMargin];
NSRect clipViewBounds = [[newScrollView contentView] bounds];
NSOutlineView* openFiles = [[[NSOutlineView alloc] initWithFrame:clipViewBounds] autorelease];
[openFiles setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
[openFiles setRowHeight:14];
[openFiles setIntercellSpacing:NSMakeSize(3.0, 6.0)];
[openFiles setHeaderView:nil];
[openFiles setFocusRingType:NSFocusRingTypeNone];
[openFiles setAutoresizingMask:NSViewWidthSizable];
id cell = [[OakImageAndTextCell alloc] init];
[cell setFont:[NSFont fontWithName:@"Lucida Grande" size:11.0]];
NSTableColumn* firstColumn = [[[NSTableColumn alloc] initWithIdentifier:@"firstColumn"] autorelease];
[[firstColumn headerCell] setStringValue:@"First Column"];
[firstColumn setResizingMask:NSTableColumnAutoresizingMask];
[firstColumn setWidth:[drawer frame].size.width];
[firstColumn setDataCell:cell];
[openFiles addTableColumn:firstColumn];
[openFiles setOutlineTableColumn:firstColumn];
MHOpenFiles *openFilesClass = [MHOpenFiles objectForTabs:[self valueForKey:@"tabBarView"]];
[openFiles setDataSource:openFilesClass];
[openFiles setDelegate:openFilesClass];
[openFilesClass setOutlineView:openFiles];
[openFilesClass setFileBrowserView:[self valueForKey:@"outlineView"]];
[newScrollView setDocumentView:openFiles];
[drawer addSubview:newScrollView];
// Add the divider
//
//
NSView *dividerView = [[MHDividerView alloc] initWithFrame:NSMakeRect(-[drawer frame].size.width, 0, [drawer frame].size.width, 2.0)];
[dividerView setAutoresizingMask:NSViewWidthSizable|NSViewMinYMargin];
[drawer addSubview:dividerView];
// Let our class know where the divider is so it can be moved around as the
// open file list changes
[openFilesClass setDividerView:dividerView];
// eventually remove the ugly buttons, for now they just get covered by the scroll view though
// NSArray *subviews = [[scrollView superview] subviews];
}
- (NSDictionary*)outlineView:(NSOutlineView*)outlineView stateForItems:(NSArray*)items
{
NSMutableDictionary *treeState = [NSMutableDictionary dictionaryWithCapacity:3];
unsigned int itemCount = [items count];
for(unsigned int index = 0; index < itemCount; index += 1)
{
NSDictionary *item = [items objectAtIndex:index];
if([outlineView isItemExpanded:item])
{
NSDictionary *subTreeState = [self outlineView:outlineView stateForItems:[item objectForKey:@"children"]];
[treeState setObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],@"isExpanded",
subTreeState,@"subItems",
nil] forKey:[item objectForKey:@"displayName"]];
}
}
return treeState;
}
- (BOOL)ProjectTree_writeToFile:(NSString*)fileName
{
BOOL result = [self ProjectTree_writeToFile:fileName];
if(result && [ProjectTree preserveTreeState])
{
NSMutableDictionary *project = [NSMutableDictionary dictionaryWithContentsOfFile:fileName];
NSDictionary *treeState = [self outlineView:[self valueForKey:@"outlineView"] stateForItems:[self valueForKey:@"rootItems"]];
[project setObject:treeState forKey:@"treeState"];
result = [project writeToFile:fileName atomically:NO];
}
return result;
}
- (void)ProjectTree_outlineView:(id)outlineView willDisplayCell:(id)cell forTableColumn:(id)column item:(NSDictionary *)item
{
NSString *path = [item objectForKey:@"filename"];
if (!path)
{
path = [item objectForKey:@"sourceDirectory"];
}
if ([[item objectForKey:@"displayName"] isEqualTo:@"Files"])
{
[cell setImage:nil];
return;
}
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:path];
[icon setSize:NSMakeSize(16.0, 16.0)];
[cell setImage:icon];
}
- (void)ProjectTree_tabBarView:(id)arg1 didOpenTab:(id)tab
{
MHOpenFiles *openFilesClass = [MHOpenFiles objectForTabs:[self valueForKey:@"tabBarView"]];
[openFilesClass addFile:[tab identifier]];
//NSLog(@"%@", [self valueForKey:@"showTabBarView"]?@"YES":@"NO");
//[self valueForKey:@"tabBarView"];
}
- (void)ProjectTree_tabBarView:(id)arg1 didCloseTab:(id)tab
{
MHOpenFiles *openFilesClass = [MHOpenFiles objectForTabs:[self valueForKey:@"tabBarView"]];
[openFilesClass removeFile:[tab identifier]];
}
// can't override this because important things happen that we don't want to have to recreate
// - (void)ProjectTree_tabBarView:(id)arg1 didSelectTab:(id)arg2 { /* ... */ }
@end
@implementation ProjectTree
+ (void)load
{
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],@"ProjectPlus Preserve Tree",
nil]];
[NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(windowDidLoad) withMethod:@selector(ProjectTree_windowDidLoad) error:NULL];
[NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(writeToFile:) withMethod:@selector(ProjectTree_writeToFile:) error:NULL];
[NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(outlineView:willDisplayCell:forTableColumn:item:) withMethod:@selector(ProjectTree_outlineView:willDisplayCell:forTableColumn:item:) error:NULL];
[NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(tabBarView:didOpenTab:) withMethod:@selector(ProjectTree_tabBarView:didOpenTab:) error:NULL];
[NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(tabBarView:didCloseTab:) withMethod:@selector(ProjectTree_tabBarView:didCloseTab:) error:NULL];
// can't override this because important things happen that we don't want to have to recreate
// [NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(tabBarView:didSelectTab:) withMethod:@selector(ProjectTree_tabBarView:didSelectTab:) error:NULL];
}
+ (BOOL)preserveTreeState;
{
return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlus Preserve Tree"];
}
@end