/* ** Channel.m ** ** Copyright (c) 2002 ** ** Author: Ludovic Marcotte ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #import "Channel.h" #import "Constants.h" #import "NSStringExtensions.h" #import "Plop.h" #import "PlopView.h" #import "PlopWindowController.h" // XML Headers needed under GNUstep #ifndef MACOSX #import // XML Headers needed under Cocoa #else #import #endif @implementation Channel // // // - (id) init { self = [super init]; allElements = [[NSMutableArray alloc] init]; animation_timer = nil; return self; } // // // - (void) dealloc { NSLog(@"Channel: -dealloc"); [plop setChannel: nil]; if ( timer ) { [timer invalidate]; DESTROY(timer); } RELEASE(allElements); RELEASE(icon); RELEASE(plop); RELEASE(inProgress); [super dealloc]; } // // access / mutation methods // - (void) addElement: (Element *) theElement { [allElements addObject: theElement]; } // // // - (void) removeElement: (Element *) theElement { [allElements removeObject: theElement]; } // // // - (void) removeAllElements { [allElements removeAllObjects]; } // // // - (int) count { return [allElements count]; } // // // - (int) itemCount { int i, count; count = 0; for (i = 0; i < [allElements count]; i++) { Element *aElement; aElement = [allElements objectAtIndex: i]; if ( [aElement type] == ITEM ) { count++; } } return count; } // // // - (Element *) elementAtIndex: (int) theIndex { return [allElements objectAtIndex: theIndex]; } // // // - (NSImage *) icon { return icon; } // // // - (void) setIcon: (NSImage *) theIcon { RETAIN(theIcon); RELEASE(icon); icon = theIcon; } // // // - (Plop *) plop { return plop; } // // // - (void) setPlop: (Plop *) thePlop { if ( thePlop ) { NSImage *aImage; RETAIN(thePlop); RELEASE(plop); plop = thePlop; [plop setChannel: self]; // We set our icon aImage = [[NSImage alloc] initWithContentsOfFile: [NSString stringWithFormat: @"%@/%@", PlopFolioUserLibraryPath(), [thePlop iconName]] ]; [self setIcon: aImage]; RELEASE(aImage); #if 0 // We start our timer NSLog(@"Starting timer with %d minutes interval.", [thePlop refresh]); timer = [NSTimer scheduledTimerWithTimeInterval: ([thePlop refresh]*60) target: self selector: @selector(update:) userInfo: nil repeats: YES]; RETAIN(timer); #endif } else { if ( timer ) { [timer invalidate]; DESTROY(timer); } DESTROY(plop); } } // // // - (PlopWindowController *) plopWindowController { return plopWindowController; } // No need to RETAIN here - (void) setPlopWindowController: (PlopWindowController *) theController { plopWindowController = theController; } // // Delegate methods // - (void) URLHandleResourceDidBeginLoading:(NSURLHandle *)sender { PlopView *theView; NSLog(@"Begin loading"); animation_index = 1; // We start our animation animation_timer = [NSTimer scheduledTimerWithTimeInterval: 0.05 target: self selector: @selector(updateAnimation) userInfo: nil repeats: YES]; RETAIN(animation_timer); // We set the stop icon theView = [(id)[[self plopWindowController] window] plopView]; [theView setReloadIcon: [NSImage imageNamed: @"stop.tiff"]]; } // // // - (void) URLHandleResourceDidCancelLoading: (NSURLHandle *) sender { PlopView *theView; NSLog(@"Cancelling update of channel %@... using %@", [plop title], [plop contentSource]); // We stop our animation [animation_timer invalidate]; DESTROY(animation_timer); theView = [(id)[[self plopWindowController] window] plopView]; [theView setAnimationIcon: nil]; [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]]; } // // // - (void) URLHandle: (NSURLHandle *) sender resourceDataDidBecomeAvailable: (NSData *) newBytes { // We do nothing here } // // // - (void) URLHandle: (NSURLHandle *) sender resourceDidFailLoadingWithReason: (NSString *) reason { NSLog(@"failed loading URL, reason: %@:", reason); } // // // - (void) URLHandleResourceDidFinishLoading: (NSURLHandle *) sender { // // GNUstep XML Parsing Code // #ifndef MACOSX GSXMLParser *aParser; NSData *aData; PlopView *theView; NSLog(@"Updating channel %@... using %@", [plop title], [plop contentSource]); // We stop our animation [animation_timer invalidate]; DESTROY(animation_timer); theView = [(id)[[self plopWindowController] window] plopView]; [theView setAnimationIcon: nil]; [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]]; // We get our data aData = [sender resourceData]; if ( !aData ) { NSLog(@"Unable to get the data from the content source. Ignoring Plop."); return; } [self removeAllElements]; aParser = [GSXMLParser parserWithData: aData]; if ( [aParser parse] ) { GSXMLDocument *aDocument; GSXMLNode *aRoot, *aNode; aDocument = [aParser document]; aRoot = [aDocument root]; aNode = [aRoot firstChildElement]; // // We loop inside our channel/image/item nodes // while (aNode != nil) { NSLog(@"aNode name = %@", [aNode name]); if ( [[aNode name] caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame ) { [self _parseNodeInformationUsingElement: self node: aNode]; } else { Element *aElement; aElement = [[Element alloc] init]; [self _parseNodeInformationUsingElement: aElement node: aNode]; [self addElement: aElement]; RELEASE(aElement); } // We get the next node aNode = [aNode nextElement]; } // We finally update our "view" if ( [self plopWindowController] ) { [[self plopWindowController] update]; } } else { NSLog(@"Error while decoding the RDF content."); } // // OS X XML Parsing Code // #else CFXMLTreeRef aTreeRef, xmlTreeRoot; CFXMLNodeRef xmlRoot; NSData *aData; int i; PlopView *theView; // We stop our animation [animation_timer invalidate]; DESTROY(animation_timer); theView = [(id)[[self plopWindowController] window] plopView]; [theView setAnimationIcon: nil]; [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]]; // We get our data aData = [sender resourceData]; aTreeRef = CFXMLTreeCreateFromData(kCFAllocatorDefault, (CFDataRef)aData, NULL, kCFXMLParserSkipWhitespace|kCFXMLParserSkipMetaData, kCFXMLNodeCurrentVersion); if ( !aTreeRef ) { NSRunAlertPanel(_(@"Error!"), _(@"Corrupted RDF. Ignoring %@ Plop."), _(@"OK"), NULL, NULL, [plop title]); return; } [self removeAllElements]; for (i = 0; i < CFTreeGetChildCount(aTreeRef); i++) { xmlTreeRoot = CFTreeGetChildAtIndex(aTreeRef, i); xmlRoot = CFXMLTreeGetNode(xmlTreeRoot); if ( CFXMLNodeGetTypeCode(xmlRoot) == kCFXMLNodeTypeElement ) { CFXMLTreeRef xmlSubTreeRef; CFXMLNodeRef aNode; int j; // We get our RDF info for (j = 0; j < CFTreeGetChildCount(xmlTreeRoot); j++) { xmlSubTreeRef = CFTreeGetChildAtIndex(xmlTreeRoot, j); aNode = CFXMLTreeGetNode(xmlSubTreeRef); if ( CFXMLNodeGetTypeCode(aNode) == kCFXMLNodeTypeElement ) { NSString *aString; aString = [(NSString*)CFXMLNodeGetString(aNode) stringByTrimmingWhiteSpaces]; if ( [aString caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame ) { [self _parseNodeInformationUsingElement: self node: (id)xmlSubTreeRef]; } else { Element *aElement; aElement = [[Element alloc] init]; [self _parseNodeInformationUsingElement: aElement node: (id)xmlSubTreeRef]; [self addElement: aElement]; RELEASE(aElement); } } } // for } } // We finally update our "view" if ( [self plopWindowController] ) { [[self plopWindowController] update]; } #endif } // // Other methods // - (void) update: (id) sender { // If the animation is already running, we just return if ( animation_timer && [animation_timer isValid] ) { // We must stop... if ( [sender isKindOfClass: [PlopView class]] ) { NSLog(@"Cancelling load in background..."); // FIXME: This doesn't work and it's normal. We should handle the stop // differently. [inProgress cancelLoadInBackground]; [inProgress release]; inProgress = nil; } NSLog(@"Update already running."); return; } else { inProgress = [[[self plop] contentSource] URLHandleUsingCache: NO]; [inProgress retain]; [inProgress addClient: self]; [inProgress loadInBackground]; } } // // // - (void) updateAnimation { PlopView *theView; theView = [(id)[[self plopWindowController] window] plopView]; //NSLog(@"Downloading..."); [theView setAnimationIcon: [NSImage imageNamed: [NSString stringWithFormat: @"anim%d.tiff", animation_index]] ]; animation_index += 1; [theView setNeedsDisplay: YES]; if ( animation_index > 16 ) { animation_index = 1; } } @end // // Private methods // @implementation Channel (Private) - (void) _updateElement: (Element *) theElement name: (NSString *) theName content: (NSString *) theContent { if ( [theName caseInsensitiveCompare: @"TITLE"] == NSOrderedSame ) { [theElement setTitle: theContent]; } else if ( [theName caseInsensitiveCompare: @"LINK"] == NSOrderedSame ) { [theElement setLink: [NSURL URLWithString: theContent] ]; } else if ( [theName caseInsensitiveCompare: @"DESCRIPTION"] == NSOrderedSame ) { [theElement setDescription: theContent]; } else if ( [theName caseInsensitiveCompare: @"URL"] == NSOrderedSame ) { [theElement setURL: [NSURL URLWithString: theContent] ]; } } // // // - (void) _parseNodeInformationUsingElement: (Element *) theElement node: (id) theNode { NSString *aNodeName; #ifndef MACOSX static int textNodeType = -1; GSXMLNode *aChild; if (textNodeType < 0) { textNodeType = [GSXMLNode typeFromDescription: @"XML_TEXT_NODE"]; } aNodeName = [(GSXMLNode *)theNode name]; #else CFXMLNodeRef xmlNode, aNode; CFXMLTreeRef xmlTreeRef; int i; xmlNode = CFXMLTreeGetNode((CFXMLTreeRef)theNode); aNodeName = (NSString *)CFXMLNodeGetString(xmlNode); #endif // We decode the right type if ( [aNodeName caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame ) { [theElement setType: CHANNEL]; } else if ( [aNodeName caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame ) { [theElement setType: IMAGE]; } else if ( [aNodeName caseInsensitiveCompare: @"ITEM"] == NSOrderedSame ) { [theElement setType: ITEM]; } else if ( [aNodeName caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame ) { [theElement setType: TEXTINPUT]; } else { [theElement setType: UNKNOWN]; } #ifndef MACOSX // We loop inside our channel/image/item node attributes if ( (aChild = [theNode firstChild]) ) { while ( aChild ) { NSString *aName, *aContent; GSXMLNode *aNode; // // Ok, this is kinda weird. Some klips are defined like that: // // ... // ... // // and some like: // // // ... // // if ( [[aChild name] caseInsensitiveCompare: @"ITEM"] == NSOrderedSame || [[aChild name] caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame || [[aChild name] caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame ) { Element *aElement; aElement = [[Element alloc] init]; [self _parseNodeInformationUsingElement: aElement node: aChild]; [self addElement: aElement]; RELEASE(aElement); aChild = [aChild next]; continue; } aNode = [aChild firstChild]; while ( aNode ) { if ( [aNode type] == textNodeType ) { if ( [[[aNode content] stringByTrimmingWhiteSpaces] length] > 0 ) { break; } } aNode = [aNode next]; } if ( !aNode ) { aChild = [aChild next]; continue; } aName = [aChild name]; aContent = [[aNode content] stringByTrimmingWhiteSpaces]; if ( aName && aContent ) { [self _updateElement: theElement name: aName content: aContent]; } aChild = [aChild next]; } } else { NSLog(@"No children"); } // // OS X XML Parsing Code // #else for (i = 0; i < CFTreeGetChildCount((CFXMLTreeRef)theNode); i++) { NSString *aName, *aContent; CFXMLTreeRef xmlSubTreeRef; CFXMLNodeRef xmlSubNode; int j; xmlTreeRef = CFTreeGetChildAtIndex((CFXMLTreeRef)theNode, i); aNode = CFXMLTreeGetNode(xmlTreeRef); xmlSubTreeRef = NULL; xmlSubNode = NULL; for ( j = 0; j < CFTreeGetChildCount(xmlTreeRef); j++ ) { xmlSubTreeRef = CFTreeGetChildAtIndex(xmlTreeRef, j); xmlSubNode = CFXMLTreeGetNode(xmlSubTreeRef); if ( CFXMLNodeGetTypeCode(xmlNode) == kCFXMLNodeTypeElement ) { break; } else { xmlSubNode = NULL; } } if ( !xmlSubNode ) { continue; } aName = (NSString *)CFXMLNodeGetString(aNode); aContent = [(NSString *)CFXMLNodeGetString(xmlSubNode) stringByTrimmingWhiteSpaces]; // // See description above. // if ( [aName caseInsensitiveCompare: @"ITEM"] == NSOrderedSame || [aName caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame || [aName caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame ) { Element *aElement; aElement = [[Element alloc] init]; [self _parseNodeInformationUsingElement: aElement node: (id)xmlTreeRef]; [(Channel *)theElement addElement: aElement]; RELEASE(aElement); } if ( aName && aContent ) { [self _updateElement: theElement name: aName content: aContent]; } } #endif } @end