Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /XspfManager/XspfMMainWindowController.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 365 - (hide annotations) (download)
Fri May 21 13:38:42 2010 UTC (13 years, 11 months ago) by masakih
File size: 17831 byte(s)
ライセンス表示の行が長過ぎるので適当に行を分けた。
1 masaki 2 //
2     // XspfManager.m
3     // XspfManager
4     //
5     // Created by Hori,Masaki on 09/11/01.
6     //
7    
8 masakih 350 /*
9 masakih 365 This source code is release under the New BSD License.
10 masakih 350 Copyright (c) 2009-2010, masakih
11     All rights reserved.
12    
13 masakih 365 ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満たす場合に
14     限り、再頒布および使用が許可されます。
15    
16     1, ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項を含
17     めること。
18     2, バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作権表
19     示、本条件一覧、および下記免責条項を含めること。
20     3, 書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進に、
21     コントリビューターの名前を使用してはならない。
22     本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供されており、
23     明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性に関する暗黙の保証
24     も含め、またそれに限定されない、いかなる保証もありません。著作権者もコントリビューター
25     も、事由のいかんを問わず、 損害発生の原因いかんを問わず、かつ責任の根拠が契約であるか
26     厳格責任であるか(過失その他の)不法行為であるかを問わず、仮にそのような損害が発生する
27     可能性を知らされていたとしても、本ソフトウェアの使用によって発生した(代替品または代用
28     サービスの調達、使用の喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定
29     されない)直接損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害につい
30     て、一切責任を負わないものとします。
31 masakih 350 -------------------------------------------------------------------
32     Copyright (c) 2009-2010, masakih
33     All rights reserved.
34    
35 masakih 365 Redistribution and use in source and binary forms, with or without
36     modification, are permitted provided that the following conditions
37     are met:
38 masakih 350
39 masakih 365 1, Redistributions of source code must retain the above copyright
40     notice, this list of conditions and the following disclaimer.
41     2, Redistributions in binary form must reproduce the above copyright
42     notice, this list of conditions and the following disclaimer in
43     the documentation and/or other materials provided with the
44     distribution.
45     3, The names of its contributors may be used to endorse or promote
46     products derived from this software without specific prior
47     written permission.
48     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
51     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
52     COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
53     INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
54     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
55     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
56     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
58     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59     POSSIBILITY OF SUCH DAMAGE.
60 masakih 350 */
61    
62 masaki 182 #import "XspfMMainWindowController.h"
63 masaki 2
64 masaki 238 #import "XspfMXspfObject.h"
65 masaki 2
66 masaki 189 #import "XspfMViewController.h"
67 masaki 41 #import "XspfMLibraryViewController.h"
68 masaki 16 #import "XspfMCollectionViewController.h"
69 masaki 25 #import "XspfMListViewController.h"
70 masaki 47 #import "XspfMDetailViewController.h"
71 masaki 281 #import "XspfMCoverFlowViewController.h"
72 masaki 2
73 masaki 281
74 masaki 193 #import "XspfMDragControl.h"
75    
76 masaki 69
77 masaki 182 @interface XspfMMainWindowController(HMPrivate)
78 masaki 43 - (void)setupXspfLists;
79 masaki 47 - (void)setupDetailView;
80 masaki 82 - (void)setupAccessorylView;
81 masaki 25 - (void)changeViewType:(XspfMViewType)newType;
82     - (void)setCurrentListViewType:(XspfMViewType)newType;
83 masaki 189 - (void)recalculateKeyViewLoop;
84 masaki 199
85     - (void)removeSelectedItem;
86 masaki 16 @end
87    
88 masaki 317
89 masaki 182 @implementation XspfMMainWindowController
90 masaki 8 - (id)init
91     {
92 masaki 200 self = [super initWithWindowNibName:@"MainWindow"];
93     if(self) {
94     viewControllers = [[NSMutableDictionary alloc] init];
95     }
96 masaki 50
97 masaki 8 return self;
98 masaki 2 }
99 masaki 8 - (void)awakeFromNib
100     {
101 masaki 47 static BOOL didSetupOnMainMenu = NO;
102    
103     if(appDelegate && !didSetupOnMainMenu) {
104     didSetupOnMainMenu = YES;
105 masaki 98
106 masaki 185 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
107     [nc addObserver:self
108     selector:@selector(managerDidAddObjects:)
109     name:XspfManagerDidAddXspfObjectsNotification
110     object:appDelegate];
111 masaki 304
112 masaki 49 [self window];
113 masaki 99
114 masaki 47 }
115 masaki 8 }
116 masaki 49 - (void)windowDidLoad
117     {
118 masakih 358 [[self window] setContentBorderThickness:27 forEdge:NSMinYEdge];
119 masaki 82
120 masaki 294 [splitView setDelegate:self];
121 masaki 49 [self setupXspfLists];
122     [self setupDetailView];
123 masaki 82 [self setupAccessorylView];
124 masaki 49
125 masaki 316 [self setCurrentListViewType:[[NSUserDefaults standardUserDefaults] integerForKey:@"viewType"]];
126 masaki 315
127 masaki 316
128 masaki 315 [listController bind:NSManagedObjectContextBinding
129     toObject:appDelegate
130     withKeyPath:@"managedObjectContext"
131     options:nil];
132    
133     [allXspfController bind:NSManagedObjectContextBinding
134     toObject:appDelegate
135     withKeyPath:@"managedObjectContext"
136     options:nil];
137 masaki 332
138 masaki 50 [self showWindow:nil];
139 masaki 189 [self recalculateKeyViewLoop];
140 masaki 49 }
141 masaki 25 #pragma mark#### KVC ####
142 masaki 2
143 masaki 25 - (XspfMViewType)currentListViewType
144     {
145     return currentListViewType;
146     }
147     - (void)setCurrentListViewType:(XspfMViewType)newType
148     {
149     if(currentListViewType == newType) return;
150    
151 masaki 316 [[NSUserDefaults standardUserDefaults] setInteger:newType forKey:@"viewType"];
152    
153 masaki 25 [self changeViewType:newType];
154     }
155 masaki 16
156 masaki 13 #pragma mark#### Actions ####
157 masaki 10 - (IBAction)openXspf:(id)sender
158     {
159 masaki 115 BOOL isSelected = [[controller valueForKeyPath:@"selectedObjects.@count"] boolValue];
160     if(!isSelected) return;
161    
162 masaki 238 XspfMXspfObject *rep = [controller valueForKeyPath:@"selection.self"];
163 masaki 277 BOOL didOpen = [[NSWorkspace sharedWorkspace] openFile:rep.filePath withApplication:@"XspfQT"];
164     if(didOpen) {
165     rep.lastPlayDate = [NSDate dateWithTimeIntervalSinceNow:0.0];
166 masaki 101 return;
167     }
168 masaki 277
169     NSInteger result = NSRunCriticalAlertPanel(NSLocalizedString(@"Xspf is not found", @"Xspf is not found"),
170     NSLocalizedString(@"\"%@\" is not found.", @"\"%@\" is not found."),
171     nil, nil/*@"Search Original"*/, nil, rep.title);
172     if(result == NSAlertDefaultReturn) {
173     return;
174     } else if(result == NSAlertAlternateReturn) {
175     //
176 masakih 345 #warning should implement.
177 masaki 277 }
178    
179 masaki 10 }
180 masaki 190 - (IBAction)switchListView:(id)sender
181     {
182     [self setCurrentListViewType:typeTableView];
183     }
184     - (IBAction)switchRegularIconView:(id)sender
185     {
186     [self setCurrentListViewType:typeCollectionView];
187     [(XspfMCollectionViewController *)listViewController collectionViewItemViewRegular:sender];
188     }
189     - (IBAction)switchSmallIconView:(id)sender
190     {
191     [self setCurrentListViewType:typeCollectionView];
192     [(XspfMCollectionViewController *)listViewController collectionViewItemViewSmall:sender];
193     }
194 masaki 281 - (IBAction)switchCoverFlowView:(id)sender
195     {
196     [self setCurrentListViewType:typeCoverFlowView];
197     }
198    
199 masaki 191 - (void)sortByKey:(NSString *)key
200 masaki 190 {
201 masaki 216 NSMutableArray *sortDescs = [[[controller sortDescriptors] mutableCopy] autorelease];
202 masaki 190 NSSortDescriptor *sortDesc = nil;
203    
204     // key is descs first key.
205     if([sortDescs count] > 1) {
206     NSSortDescriptor *firstDesc = [sortDescs objectAtIndex:0];
207     if([key isEqualToString:[firstDesc key]]) {
208     sortDesc = [[[NSSortDescriptor alloc] initWithKey:key ascending:![firstDesc ascending]] autorelease];
209     [sortDescs removeObject:firstDesc];
210     }
211     }
212     // remove same key.
213     if(!sortDesc) {
214 masaki 191 BOOL newAscending = NO;
215 masaki 190 NSSortDescriptor *foundDesc = nil;
216     for(id desc in sortDescs) {
217     if([key isEqualToString:[desc key]]) {
218     foundDesc = desc;
219     break;
220     }
221     }
222     if(foundDesc) {
223 masaki 191 newAscending = [foundDesc ascending];
224 masaki 190 [sortDescs removeObject:foundDesc];
225     }
226    
227 masaki 191 sortDesc = [[[NSSortDescriptor alloc] initWithKey:key ascending:newAscending] autorelease];
228 masaki 190 }
229    
230     [sortDescs insertObject:sortDesc atIndex:0];
231    
232     NSArray *selectedObjects = [controller selectedObjects];
233     [controller setSortDescriptors:sortDescs];
234     [controller setSelectedObjects:selectedObjects];
235     }
236     - (IBAction)sortByTitle:(id)sender
237     {
238 masaki 191 [self sortByKey:@"title"];
239 masaki 190 }
240     - (IBAction)sortByLastPlayDate:(id)sender
241     {
242 masaki 191 [self sortByKey:@"lastPlayDate"];
243 masaki 190 }
244     - (IBAction)sortByModificationDate:(id)sender
245     {
246 masaki 191 [self sortByKey:@"modificationDate"];
247 masaki 190 }
248     - (IBAction)sortByCreationDate:(id)sender
249     {
250 masaki 191 [self sortByKey:@"creationDate"];
251 masaki 190 }
252     - (IBAction)sortByRegisterDate:(id)sender
253     {
254 masaki 191 [self sortByKey:@"registerDate"];
255 masaki 190 }
256     - (IBAction)sortByRate:(id)sender
257     {
258 masaki 191 [self sortByKey:@"rating"];
259 masaki 190 }
260     - (IBAction)sortByMovieNumber:(id)sender
261     {
262 masaki 191 [self sortByKey:@"movieNum"];
263 masaki 190 }
264     - (IBAction)sortByLabel:(id)sender
265     {
266 masaki 224 [self sortByKey:@"label"];
267 masaki 190 }
268    
269 masaki 2 - (IBAction)add:(id)sender
270     {
271     NSOpenPanel *panel = [NSOpenPanel openPanel];
272    
273     [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"xspf", @"com.masakih.xspf", nil]];
274     [panel setAllowsMultipleSelection:YES];
275 masaki 9 [panel setDelegate:self];
276 masaki 2
277     [panel beginSheetForDirectory:nil
278     file:nil
279     types:[NSArray arrayWithObjects:@"xspf", @"com.masakih.xspf", nil]
280 masaki 8 modalForWindow:[self window]
281 masaki 2 modalDelegate:self
282     didEndSelector:@selector(endOpenPanel:::)
283     contextInfo:NULL];
284     }
285 masaki 106 - (void)endOpenPanel:(NSOpenPanel *)panel :(NSInteger)returnCode :(void *)context
286     {
287     [panel orderOut:nil];
288 masaki 2
289 masaki 106 if(returnCode == NSCancelButton) return;
290    
291     NSArray *URLs = [panel URLs];
292     if([URLs count] == 0) return;
293    
294 masaki 184 [appDelegate registerURLs:URLs];
295 masaki 106 }
296 masaki 190 - (IBAction)remove:(id)sender
297     {
298 masaki 199 [self removeSelectedItem];
299 masaki 190 }
300 masaki 182
301 masaki 194 - (IBAction)newPredicate:(id)sender
302     {
303     [libraryViewController newPredicate:sender];
304     }
305    
306 masakih 358 - (IBAction)showHideDetail:(id)sender
307     {
308     BOOL flag = YES;
309    
310     NSPoint origin = [detailView frame].origin;
311     NSSize size = NSZeroSize;
312     CGFloat windowRightEdge = [[detailView window] frame].size.width;
313     flag = (origin.x == windowRightEdge);
314    
315     CGFloat detailWidth = [detailView frame].size.width;
316     if(flag){ // show
317     origin.x -= detailWidth;
318     size = [splitView frame].size;
319     size.width -= detailWidth;
320     } else { // hide
321     origin.x += detailWidth;
322     size = [splitView frame].size;
323     size.width += detailWidth;
324     }
325     [[detailView animator] setFrameOrigin:origin];
326     [[splitView animator] setFrameSize:size];
327     }
328    
329 masaki 190 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
330     {
331     BOOL enabled = YES;
332     SEL action = [menuItem action];
333    
334     if(action == @selector(switchListView:)) {
335     if(currentListViewType == typeTableView) {
336     [menuItem setState:NSOnState];
337     } else {
338     [menuItem setState:NSOffState];
339     }
340     } else if(action == @selector(switchRegularIconView:)) {
341     if(currentListViewType == typeCollectionView
342     && [(XspfMCollectionViewController*)listViewController collectionItemType] == typeXspfMRegularItem) {
343     [menuItem setState:NSOnState];
344     } else {
345     [menuItem setState:NSOffState];
346     }
347     } else if(action == @selector(switchSmallIconView:)) {
348     if(currentListViewType == typeCollectionView
349     && [(XspfMCollectionViewController*)listViewController collectionItemType] == typeXSpfMSmallItem) {
350     [menuItem setState:NSOnState];
351     } else {
352     [menuItem setState:NSOffState];
353     }
354 masaki 281 } else if(action == @selector(switchCoverFlowView:)) {
355     if(currentListViewType == typeCoverFlowView) {
356     [menuItem setState:NSOnState];
357     } else {
358     [menuItem setState:NSOffState];
359     }
360 masaki 190 }
361    
362     return enabled;
363     }
364    
365 masaki 25 #pragma mark#### Other methods ####
366 masaki 199 - (void)removeSelectedItem
367     {
368 masaki 238 XspfMXspfObject *obj = [controller valueForKeyPath:@"selection.self"];
369 masaki 199
370     NSBeginInformationalAlertSheet(nil, nil, @"Cancel", nil, [self window],
371     self, @selector(didEndAskDelete:::), Nil, obj,
372     NSLocalizedString(@"Do you really delete item \"%@\" from list?", @"Do you really delete item \"%@\" from list?"),
373     obj.title);
374     }
375     - (void)didEndAskDelete:(NSWindow *)sheet :(NSInteger)returnCode :(void *)contextInfo
376     {
377     if(returnCode == NSCancelButton) return;
378    
379     [appDelegate removeObject:contextInfo];
380     }
381    
382 masaki 189 - (void)recalculateKeyViewLoop
383     {
384     [searchField setNextKeyView:[libraryViewController firstKeyView]];
385     [libraryViewController setNextKeyView:[listViewController firstKeyView]];
386     [listViewController setNextKeyView:[detailViewController firstKeyView]];
387     [detailViewController setNextKeyView:searchField];
388     }
389 masaki 25 - (void)changeViewType:(XspfMViewType)viewType
390     {
391     if(currentListViewType == viewType) return;
392     currentListViewType = viewType;
393    
394     NSString *className = nil;
395     switch(currentListViewType) {
396     case typeCollectionView:
397     className = @"XspfMCollectionViewController";
398     break;
399     case typeTableView:
400     className = @"XspfMListViewController";
401     break;
402 masaki 281 case typeCoverFlowView:
403     className = @"XspfMCoverFlowViewController";
404     break;
405 masaki 25 }
406     if(!className) return;
407    
408 masaki 189 XspfMViewController *targetContorller = [viewControllers objectForKey:className];
409 masaki 25 if(!targetContorller) {
410     targetContorller = [[[NSClassFromString(className) alloc] init] autorelease];
411     if(!targetContorller) return;
412 masaki 192
413     id selectionIndexes = [controller selectionIndexes];
414 masaki 25 [viewControllers setObject:targetContorller forKey:className];
415 masaki 180 [targetContorller view];
416 masaki 30 [targetContorller setRepresentedObject:controller];
417 masaki 189 [targetContorller recalculateKeyViewLoop];
418 masaki 192 [controller setSelectionIndexes:selectionIndexes];
419 masaki 25 }
420 masaki 109
421 masaki 25 [[listViewController view] removeFromSuperview];
422     listViewController = targetContorller;
423     [listView addSubview:[listViewController view]];
424 masakih 358 NSRect rect = [listView bounds];
425     rect.size.height += 1;
426     rect.origin.y -= 1;
427     [[listViewController view] setFrame:rect];
428 masaki 189 // [[self window] recalculateKeyViewLoop];
429     [self recalculateKeyViewLoop];
430 masaki 25 }
431 masaki 2
432 masaki 5
433 masaki 43 - (void)setupXspfLists
434 masaki 36 {
435 masaki 47 if(libraryViewController) return;
436    
437 masaki 41 libraryViewController = [[XspfMLibraryViewController alloc] init];
438 masaki 37 [libraryViewController setRepresentedObject:listController];
439     [libraryView addSubview:[libraryViewController view]];
440 masaki 193 NSRect rect = [libraryView bounds];
441     rect.size.width += 2;
442     rect.origin.x -= 1;
443 masakih 358 rect.size.height += 1;
444     rect.origin.y -= 1;
445 masaki 193 [[libraryViewController view] setFrame:rect];
446 masaki 189 [libraryViewController recalculateKeyViewLoop];
447 masaki 233 [[libraryViewController dragControl] setDelegate:self];
448 masaki 36 }
449 masaki 47 - (void)setupDetailView
450 masaki 6 {
451 masaki 47 if(detailViewController) return;
452 masaki 6
453 masaki 47 detailViewController = [[XspfMDetailViewController alloc] init];
454     [detailViewController setRepresentedObject:controller];
455     [detailView addSubview:[detailViewController view]];
456     [[detailViewController view] setFrame:[detailView bounds]];
457 masaki 189 [detailViewController recalculateKeyViewLoop];
458 masaki 6 }
459 masaki 82 - (void)setupAccessorylView
460     {
461     if(accessoryViewController) return;
462    
463     accessoryViewController = [[NSViewController alloc] initWithNibName:@"AccessoryView" bundle:nil];
464     [accessoryViewController setRepresentedObject:[appDelegate channel]];
465     [accessoryView addSubview:[accessoryViewController view]];
466     [[accessoryViewController view] setFrame:[accessoryView bounds]];
467 masaki 189 // [accessoryViewController recalculateKeyViewLoop];
468 masaki 82 }
469 masaki 8 #pragma mark#### NSWidnow Delegate ####
470     /**
471     Returns the NSUndoManager for the application. In this case, the manager
472     returned is that of the managed object context for the application.
473     */
474 masaki 6
475 masaki 8 - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
476     return [[appDelegate managedObjectContext] undoManager];
477     }
478    
479 masaki 9 #pragma mark#### NSOpenPanel Delegate ####
480     - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
481     {
482 masaki 184 return ![appDelegate didRegisteredURL:[NSURL fileURLWithPath:filename]];
483 masaki 9 }
484 masaki 294 #pragma mark#### NSSplitView Delegate ####
485     - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
486     {
487     return 130;
488     }
489 masaki 295 - (void)splitView:(NSSplitView *)aSplitView resizeSubviewsWithOldSize:(NSSize)oldSize
490     {
491     NSView *rightView = [[splitView subviews] objectAtIndex:1];
492     NSRect newFrame = [splitView frame];
493     NSRect libFrame = [libraryView frame];
494     NSRect listFrame = [rightView frame];
495     CGFloat dividerThickness = [splitView dividerThickness];
496    
497     libFrame.size.height = newFrame.size.height;
498     listFrame.size.height = newFrame.size.height;
499    
500     listFrame.size.width = newFrame.size.width - libFrame.size.width - dividerThickness;
501    
502     if(listFrame.size.width < 0) listFrame.size.width = 0;
503    
504     [libraryView setFrame:libFrame];
505     [rightView setFrame:listFrame];
506     }
507 masaki 193 #pragma mark#### XspfMDragControl Delegate ####
508     - (void)dragControl:(XspfMDragControl *)control dragDelta:(NSSize)delta
509     {
510     HMLog(HMLogLevelDebug, @"Enter %@", NSStringFromSelector(_cmd));
511     CGFloat libWidth = [libraryView frame].size.width;
512     [splitView setPosition:libWidth + delta.width ofDividerAtIndex:0];
513     }
514    
515 masaki 185 #pragma mark#### XspfManager Notifications ####
516     - (void)managerDidAddObjects:(NSNotification *)notification
517     {
518     id addedObjects = [[notification userInfo] objectForKey:@"XspfManagerAddedXspfObjects"];
519     if(!addedObjects || ![addedObjects isKindOfClass:[NSArray class]] || [addedObjects count] == 0) return;
520    
521     [controller performSelector:@selector(setSelectedObjects:)
522     withObject:addedObjects
523     afterDelay:0.01];
524     }
525    
526 masaki 184 #pragma mark#### Test ####
527     - (IBAction)test01:(id)sender
528     {
529 masakih 358 NSPoint origin = [detailView frame].origin;
530     origin.x = [[detailView window] frame].size.width;
531     [detailView setFrameOrigin:origin];
532 masaki 184 }
533     - (IBAction)test02:(id)sender
534     {
535     NSResponder *responder = [[self window] firstResponder];
536     while(responder) {
537 masaki 188 HMLog(HMLogLevelDebug, @"Responder -> %@", responder);
538 masaki 184 responder = [responder nextResponder];
539     }
540     }
541     - (IBAction)test03:(id)sender
542     {
543 masakih 358 id anime = [detailView animator];
544     NSPoint origin = [detailView frame].origin;
545     origin.x = [[detailView window] frame].size.width;
546     [anime setFrameOrigin:origin];
547 masaki 184
548 masakih 358 anime = [splitView animator];
549     NSSize size = [splitView frame].size;
550     size.width += [detailView frame].size.width;
551     [anime setFrameSize:size];
552 masaki 184 }
553     @end
554 masaki 297

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26