// Copyright (c) 2002, Peter Bentley // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // The name Peter Bentley may not be used to endorse or promote // products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // $Id: OmdTocCtlr.m,v 1.24 2003/08/23 17:05:58 pete Exp $ #import "OmdTocCtlr.h" #import "OmdToc.h" #import "OmdTrackInfo.h" #import "OmdGroupInfo.h" #import "OmdUnit.h" #import "omd.h" CVSID( "$Id: OmdTocCtlr.m,v 1.24 2003/08/23 17:05:58 pete Exp $" ); // Constants for the node types we allow in the outline view NSString *kOmdTitleNode = @"Title Node"; NSString *kOmdGroupNode = @"Group Node"; NSString *kOmdTrackNode = @"Track Node"; @implementation OmdTocCtlr - (void) refresh { [children release]; children = nil; [toc release]; toc = [[omdUnit toc] retain]; slog( LOG_DEBUG, "Start outline reload" ); [outline reloadData]; slog( LOG_DEBUG, "Finish outline reload" ); } - (void) dealloc { [children release]; [toc release]; [currTitle release]; [super dealloc]; } - (void) awakeFromNib { [self disable]; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: @selector( enableFromTimer: ) userInfo: nil repeats: NO]; [timer retain]; [progress setStyle: NSProgressIndicatorSpinningStyle]; [progress setAnimationDelay: 0.1]; [progress setUsesThreadedAnimation: NO]; [progress setDisplayedWhenStopped: NO]; busyCount = 0; [busyField setStringValue: @""]; [omdUnit setController: self]; } - (void) enable { enabled = YES; } - (void) disable { enabled = NO; } - (BOOL) isEnabled { return enabled; } - (void) enableFromTimer: (NSTimer *) timer { slog( LOG_INFO, "Loading track information from disk" ); [timer invalidate]; [timer release]; [self enable]; [self refresh]; } - (void) updateView { [outline setAutoresizesOutlineColumn: NO]; if( toc ) { slog( LOG_DEBUG, "Updating TOC - %d tracks", [toc numTracks] ); [self saveCurrentTitle: [toc title]]; } else { slog( LOG_INFO, "No TOC" ); } inTitleEdit = NO; } - (void) saveCurrentTitle: (NSString *) newTitle { [currTitle autorelease]; currTitle = [newTitle retain]; if( ( newTitle == nil ) || ( [newTitle length] == 0 )) { titleIsBlank = YES; } else { titleIsBlank = NO; } } // Data Source methods - (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item { slog( LOG_DEBUG, "Get number of children for %p", item ); if( [self isEnabled] ) { return (item == nil) ? [self numberOfChildren] + 1 : [item numberOfChildren]; } else { return 0; } } - (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item { slog( LOG_TRACE, "Get isexpandable for %p", item ); if( ( item == nil ) || ( item == self ) ) { return NO; } return ([item numberOfChildren] != -1); } - (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item { slog( LOG_TRACE, "Get child %d of item %p", index, item ); if( item == nil ) { // First child of root is the disk title if( index == 0 ) { return self; } else { // Then our children come next return [self childAtIndex: index - 1]; } } else { // Otherwise just ask the item for its children return [item childAtIndex: index]; } } - (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item { id retval; // slog( LOG_INFO, "Column %p, item %p", tableColumn, item ); retval = (item == nil) ? @"" : [item valueForKey: [tableColumn identifier]]; return retval; } - (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item { [item takeValue: object forKey: [tableColumn identifier]]; } // Outline view data - (void) findChildren { int g, track; OmdGroupInfo *grp; if( children ) { [children release]; } // Arbitrary initial capacity... children = [[NSMutableArray alloc] initWithCapacity: 8]; track = 0; for( g = 0; g < [toc numGroups]; g++ ) { // Get the group info grp = [toc groupInfo: g]; // Add all tracks before this group while( track < [grp first] ) { [children addObject: [toc trackInfo: track]]; track++; } // Then add the group [children addObject: grp]; // And move the track pointer to the track after track = [grp last] + 1; } // Add any remaining tracks for( ; track < [toc numTracks]; track++ ) { [children addObject: [toc trackInfo: track]]; } } - (int) numberOfChildren { if( children == nil ) { [self findChildren]; } return [children count]; } - (id) childAtIndex: (int) index { return [children objectAtIndex: index]; } - (int) id { return 0; } - (NSString *) idString { return @""; } - (NSString *) groupString { return @""; } - (NSString *) titleString { if( titleIsBlank ) { return @"Untitled Disk"; } else { return currTitle; } } - (void) setTitleString: (NSString *) str { if( titleIsBlank || (! [str isEqualToString: currTitle])) { slog( LOG_INFO, "New disc title: %s", [str lossyCString]); [toc setDiscTitle: str]; [self updateView]; } } - (NSString *) lengthString { return @""; } - (NSString *) codecString { return @""; } - (NSString *) rateString { return @""; } - (NSString *) lockedString { return @""; } - (NSString *) nodeType { return kOmdTitleNode; } - (IBAction) test: (id) sender { // [outline expandItem: self expandChildren: NO]; if( [outline numberOfSelectedRows] ) { int i; NSEnumerator *enumer = [outline selectedRowEnumerator]; NSMutableArray *targets = [[NSMutableArray alloc] initWithCapacity: 5]; NSNumber *row; id t; [targets autorelease]; while( row = [enumer nextObject] ) { [targets addObject: [outline itemAtRow: [row intValue]]]; } enumer = [targets objectEnumerator]; i = 0; while( t = [enumer nextObject] ) { slog( LOG_INFO, "Target %d, type %s, title %s", i++, [[t nodeType] lossyCString], [[t titleString] lossyCString] ); } } else { slog( LOG_INFO, "No rows selected" ); } } - (NSMutableArray *) outlineTargets { NSMutableArray *targets = nil; // [outline expandItem: self expandChildren: NO]; if( [outline numberOfSelectedRows] ) { // int i; NSEnumerator *enumer = [outline selectedRowEnumerator]; NSNumber *row; // id t; targets = [[NSMutableArray alloc] initWithCapacity: 5]; [targets autorelease]; while( row = [enumer nextObject] ) { [targets addObject: [outline itemAtRow: [row intValue]]]; } } return targets; } - (BOOL) canCreateGroupFrom: (NSArray *) targets { NSEnumerator *enumer; id targ; if( ! targets ) { slog( LOG_TRACE, "No targets" ); return NO; } enumer = [targets objectEnumerator]; while( targ = [enumer nextObject] ) { if( [targ nodeType] != kOmdTrackNode ) { slog( LOG_TRACE, "Not a track..." ); return NO; } OmdTrackInfo *track = targ; if( [track group] != -1 ) { slog( LOG_TRACE, "Track already in group..." ); return NO; } } slog( LOG_DEBUG, "Group OK" ); return YES; } - (BOOL) canDeleteTargets: (NSArray *) targets { NSEnumerator *enumer; id targ; if( ! targets ) { slog( LOG_TRACE, "No targets" ); return NO; } enumer = [targets objectEnumerator]; while( targ = [enumer nextObject] ) { if( [targ nodeType] == kOmdTitleNode ) { slog( LOG_TRACE, "Can't delete title node..." ); return NO; } } slog( LOG_TRACE, "Nuke 'em" ); return YES; } - (IBAction) createGroup: (id) sender { NSArray *targets = [self outlineTargets]; NSEnumerator *enumer; OmdTrackInfo *track; int first, last, num; if( targets && [self canCreateGroupFrom: targets] ) { enumer = [targets objectEnumerator]; track = [enumer nextObject]; first = last = [track id]; while( track = [enumer nextObject] ) { num = [track id]; if( num != last + 1 ) { slog( LOG_ERROR, "Non contiguous group!" ); return; } last = num; } slog( LOG_INFO, "Creating new group from %d to %d", first, last ); if( [toc createGroup: @"Untitled Group" from: first - 1 to: last - 1] ) { slog( LOG_INFO, "createGroup: failed" ); } [self refresh]; } } - (IBAction) deleteThings: (id) sender { NSArray *targets = [self outlineTargets]; NSEnumerator *enumer; id node; NSString *type; int trkid, lasttrack = 0, offset = 0; if( targets && [self canDeleteTargets: targets] ) { enumer = [targets objectEnumerator]; while( node = [enumer nextObject] ) { type = [node nodeType]; // XXX Cheesey. Do this with proper polymorphism some time if( type == kOmdTrackNode ) { OmdTrackInfo *track = node; trkid = [track id]; if( lasttrack && (trkid <= lasttrack )) { slog( LOG_ERROR, "Unexpected track order in delete" ); break; } slog( LOG_INFO, "Delete track %d", trkid - offset ); if( [toc deleteTrack: trkid - (offset + 1)] < 0 ) { break; } offset++; lasttrack = trkid; } else if ( type == kOmdGroupNode ) { OmdGroupInfo *group = node; slog( LOG_INFO, "Delete group %d", [group id] ); [toc deleteGroup: [group id] - 1 ]; } else { slog( LOG_ERROR, "Tried to delete something we shouldn't!" ); } } [self refresh]; } } - (BOOL) validateMenuItem: (id ) menuItem { NSString *selectorString = NSStringFromSelector([menuItem action]); slog( LOG_TRACE, "TocController validateMenuItem: %s", [selectorString lossyCString] ); if( [menuItem action] == @selector( createGroup: )) { slog( LOG_TRACE, "Check create group" ); return [self canCreateGroupFrom: [self outlineTargets]]; } else if( [menuItem action] == @selector( deleteThings: )) { slog( LOG_TRACE, "Check Delete stuff" ); return [self canDeleteTargets: [self outlineTargets]]; } // Default return YES; } // Delegate methods //- (BOOL) outlineView: (NSOutlineView *) outlineView // shouldEditTableColumn: (NSTableColumn *) tableColumn // item: (id) item //{ // return NO; //} - (BOOL) outlineView: (NSOutlineView *) outlineView shouldEditTableColumn: (NSTableColumn *) tableColumn item: (id) item { if( item == self ) { // If we call edit with a blank title, clear the cell if( titleIsBlank ) { slog( LOG_INFO, "Do blank" ); titleIsBlank = NO; [outline reloadItem: self]; inTitleEdit = YES; } } return YES; } - (BOOL) outlineView: (NSOutlineView *) outlineView shouldSelectItem: (id) item { if( ( item != self ) & inTitleEdit ) { slog( LOG_INFO, "Poit!" ); inTitleEdit = NO; [outline reloadItem: self]; } return YES; } - (void) busy { int i; //[progress animate: self]; //[progress incrementBy: 5.0]; //[progress display]; [progress display]; if( busyCount == 0 ) { //[progress startAnimation: self]; [busyField setStringValue: @"BUSY"]; [busyField display]; } slog( LOG_DEBUG, "Busy..." ); //[progress incrementBy: 25]; //[progress animate: self]; // for( i = 0; i < 7; i++ ) // { // //[progress animate: self]; // [progress incrementBy: 25]; // [progress display]; // } //[progress incrementBy: 1]; //[progress display]; busyCount++; } - (void) notBusy { [progress display]; busyCount--; if( busyCount >= 0 ) { //[progress stopAnimation: self]; busyCount = 0; slog( LOG_DEBUG, "Idle..." ); [busyField setStringValue: @"IDLE"]; [busyField display]; } } - (BOOL) isBusy { return busyCount; } @end