summaryrefslogtreecommitdiffstats
path: root/Classes
diff options
context:
space:
mode:
authormatt handler <matt.handler@gmail.com>2011-04-20 14:39:20 -0400
committermatt handler <matt.handler@gmail.com>2011-04-20 14:39:20 -0400
commit9d52266f823daf5cb83e3eebe1b9f57594bc9d4a (patch)
tree38402f7ddbb0f3a40ebc7c88d673756701f47510 /Classes
downloadpiccast-app-9d52266f823daf5cb83e3eebe1b9f57594bc9d4a.tar.gz
piccast-app-9d52266f823daf5cb83e3eebe1b9f57594bc9d4a.zip
first commit
Diffstat (limited to 'Classes')
-rw-r--r--Classes/AcidCowFeedburnerParser.h38
-rw-r--r--Classes/AcidCowFeedburnerParser.m207
-rw-r--r--Classes/AsyncImageView.h26
-rw-r--r--Classes/AsyncImageView.m144
-rw-r--r--Classes/Classes-1.moved-aside/TopicTableViewCell.xib279
-rwxr-xr-xClasses/HJCircularBuffer.h44
-rwxr-xr-xClasses/HJCircularBuffer.m149
-rwxr-xr-xClasses/HJMOBigFileCache.h29
-rwxr-xr-xClasses/HJMOBigFileCache.m94
-rwxr-xr-xClasses/HJMOFileCache.h62
-rwxr-xr-xClasses/HJMOFileCache.m280
-rwxr-xr-xClasses/HJMOHandler.h76
-rwxr-xr-xClasses/HJMOHandler.m381
-rwxr-xr-xClasses/HJMOPolicy.h45
-rwxr-xr-xClasses/HJMOPolicy.m45
-rwxr-xr-xClasses/HJMOUser.h68
-rwxr-xr-xClasses/HJMOUserBase.h27
-rwxr-xr-xClasses/HJMOUserBase.m42
-rwxr-xr-xClasses/HJManagedImageV.h74
-rwxr-xr-xClasses/HJManagedImageV.m254
-rwxr-xr-xClasses/HJManagedImageVDelegate.h14
-rwxr-xr-xClasses/HJObjManager.h106
-rwxr-xr-xClasses/HJObjManager.m150
-rwxr-xr-xClasses/HJWeakMutableArray.h41
-rwxr-xr-xClasses/HJWeakMutableArray.m144
-rw-r--r--Classes/PhotoSource.h44
-rw-r--r--Classes/PhotoSource.m192
-rw-r--r--Classes/PhotoViewController.h24
-rw-r--r--Classes/PhotoViewController.m150
-rw-r--r--Classes/PicCast.pngbin0 -> 29647 bytes
-rw-r--r--Classes/PicCastAppDelegate.h21
-rw-r--r--Classes/PicCastAppDelegate.m111
-rw-r--r--Classes/PicDumpViewController.h22
-rw-r--r--Classes/PicDumpViewController.m273
-rw-r--r--Classes/PicDumpViewController.xib561
-rw-r--r--Classes/TabViewController.h23
-rw-r--r--Classes/TabViewController.m64
-rw-r--r--Classes/Topic.h34
-rw-r--r--Classes/Topic.m28
-rw-r--r--Classes/TopicTableViewCell.h32
-rw-r--r--Classes/TopicTableViewCell.m80
-rw-r--r--Classes/TopicTableViewCell.xib573
-rw-r--r--Classes/TopicsViewController.h33
-rw-r--r--Classes/TopicsViewController.m380
-rw-r--r--Classes/XMLParser.h59
-rw-r--r--Classes/XMLParser.m81
46 files changed, 5604 insertions, 0 deletions
diff --git a/Classes/AcidCowFeedburnerParser.h b/Classes/AcidCowFeedburnerParser.h
new file mode 100644
index 0000000..7b5c80b
--- /dev/null
+++ b/Classes/AcidCowFeedburnerParser.h
@@ -0,0 +1,38 @@
+//
+// AcidCowFeedburnerParser.h
+// PicCast
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "XMLParser.h"
+
+@class Topic;
+
+@interface AcidCowFeedburnerParser : XMLParser <NSXMLParserDelegate> {
+ NSMutableString *currentString;
+ Topic *currentTopic;
+ BOOL storingCharacters;
+ NSDateFormatter *parseFormatter;
+ NSMutableData *xmlData;
+ BOOL done;
+ NSURLConnection *rssConnection;
+ // The number of parsed songs is tracked so that the autorelease pool for the parsing thread can be periodically
+ // emptied to keep the memory footprint under control.
+ NSUInteger countOfParsedTopics;
+ NSAutoreleasePool *downloadAndParsePool;
+}
+
+@property (nonatomic, retain) NSMutableString *currentString;
+@property (nonatomic, retain) Topic *currentTopic;
+@property (nonatomic, retain) NSDateFormatter *parseFormatter;
+@property (nonatomic, retain) NSMutableData *xmlData;
+@property (nonatomic, retain) NSURLConnection *rssConnection;
+// The autorelease pool property is assign because autorelease pools cannot be retained.
+@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;
+
+- (void)downloadAndParse:(NSURL *)url;
+
+@end
diff --git a/Classes/AcidCowFeedburnerParser.m b/Classes/AcidCowFeedburnerParser.m
new file mode 100644
index 0000000..46d7ec5
--- /dev/null
+++ b/Classes/AcidCowFeedburnerParser.m
@@ -0,0 +1,207 @@
+//
+// AcidCowFeedburnerParser.m
+// PicCast
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "AcidCowFeedburnerParser.h"
+#import "Topic.h"
+
+@implementation AcidCowFeedburnerParser
+
+@synthesize currentString, currentTopic, parseFormatter, xmlData, rssConnection, downloadAndParsePool;
+
+
+- (void)downloadAndParse:(NSURL *)url {
+ NSLog(@"downloadAndParse");
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ done = NO;
+ self.parseFormatter = [[[NSDateFormatter alloc] init] autorelease];
+ [parseFormatter setDateStyle:NSDateFormatterLongStyle];
+ [parseFormatter setTimeStyle:NSDateFormatterNoStyle];
+ // necessary because iTunes RSS feed is not localized, so if the device region has been set to other than US
+ // the date formatter must be set to US locale in order to parse the dates
+ [parseFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"US"] autorelease]];
+ self.xmlData = [NSMutableData data];
+ //[[NSURLCache sharedURLCache] removeAllCachedResponses];
+ NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
+ // create the connection with the request and start loading the data
+ rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
+ [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO];
+ if (rssConnection != nil) {
+ do {
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
+ } while (!done);
+ }
+ self.rssConnection = nil;
+ self.parseFormatter = nil;
+ self.currentTopic = nil;
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = nil;
+}
+
+#pragma mark NSURLConnection Delegate methods
+
+/*
+ Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application.
+ */
+//- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
+// return nil;
+//}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ done = YES;
+ [self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [xmlData appendData:data];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ NSLog(@"finishedLoading");
+ [self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO];
+ [self convertXMLData];
+
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
+ parser.delegate = self;
+ self.currentString = [NSMutableString string];
+ //NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
+ [parser parse];
+ //NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;
+ //[self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO];
+ [parser release];
+ self.currentString = nil;
+ self.xmlData = nil;
+ // Set the condition which ends the run loop.
+ done = YES;
+}
+
+// needed to convert feedburners xml into utf-8 because the iphone parser breaks on windows-1251
+- (void)convertXMLData {
+ NSString *string = [[NSString alloc] initWithData:xmlData encoding:NSWindowsCP1251StringEncoding];
+ string = [string stringByReplacingOccurrencesOfString:@"encoding=\"windows-1251\"" withString:@""];
+
+ // [xmlData release];
+ // xmlData = nil;
+ xmlData = [[NSMutableData dataWithData:[string dataUsingEncoding:NSUTF8StringEncoding]] retain];
+}
+
+#pragma mark Parsing support methods
+
+static const NSUInteger kAutoreleasePoolPurgeFrequency = 20;
+
+- (void)finishedCurrentTopic {
+ [self performSelectorOnMainThread:@selector(parsedTopic:) withObject:currentTopic waitUntilDone:NO];
+ // performSelectorOnMainThread: will retain the object until the selector has been performed
+ // setting the local reference to nil ensures that the local reference will be released
+ self.currentTopic = nil;
+ countOfParsedTopics++;
+ // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the
+ // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but
+ // taking this action too frequently would be wasteful and reduce performance.
+ if (countOfParsedTopics == kAutoreleasePoolPurgeFrequency) {
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ countOfParsedTopics = 0;
+ }
+}
+
+#pragma mark NSXMLParser Parsing Callbacks
+
+// Constants for the XML element names that will be considered during the parse.
+// Declaring these as static constants reduces the number of objects created during the run
+// and is less prone to programmer error.
+static NSString *kName_Item = @"item";
+static NSString *kName_Title = @"title";
+static NSString *kName_Category = @"category";
+static NSString *kName_Creator = @"dc:creator";
+static NSString *kName_Description = @"description";
+static NSString *kName_ReleaseDate = @"pubDate";
+static NSString *kName_Guid = @"guid";
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict {
+ //NSLog(@"%@", elementName);
+ if ([elementName isEqualToString:kName_Item]) {
+ self.currentTopic = [[[Topic alloc] init] autorelease];
+ } else if ([elementName isEqualToString:kName_Title]
+ || [elementName isEqualToString:kName_Category]
+ || [elementName isEqualToString:kName_Guid]
+ || [elementName isEqualToString:kName_Creator]
+ || [elementName isEqualToString:kName_Description]
+ || [elementName isEqualToString:kName_ReleaseDate]) {
+ [currentString setString:@""];
+ storingCharacters = YES;
+ }
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
+ if ([elementName isEqualToString:kName_Item]) {
+ [self finishedCurrentTopic];
+ } else if ([elementName isEqualToString:kName_Title]) {
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"[^(]+"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+ if ([results count] > 0) {
+ NSString *title = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+ currentTopic.title = title;
+ }
+
+ NSRegularExpression* regex2 = [NSRegularExpression regularExpressionWithPattern:@"(?<=\\()\\d+"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results2 = [regex2 matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+ if ([results2 count] > 0) {
+ NSString *count = [currentString substringWithRange:[[results2 objectAtIndex:0] range]];
+ currentTopic.picCount = [NSNumber numberWithInt:[count intValue]];
+ //NSLog(@"%@", count);
+ }
+ else
+ currentTopic.picCount = [NSNumber numberWithInt:1];
+
+ //currentTopic.title = currentString;
+ } else if ([elementName isEqualToString:kName_Category]) {
+ currentTopic.category = currentString;
+ } else if ([elementName isEqualToString:kName_Guid]) {
+ currentTopic.guid = currentString;
+ } else if ([elementName isEqualToString:kName_Creator]) {
+ currentTopic.creator = currentString;
+ } else if ([elementName isEqualToString:kName_Description]) {
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"http\\S+(jpg|png|gif|jpeg)"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+// for (NSTextCheckingResult *res in results) {
+// NSLog(@"regexed: %@", [currentString substringWithRange:res.range]);
+// }
+ if ([results count] > 0) {
+ NSString *url = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+ //NSLog(@"%@", url);
+ currentTopic.iconUrl = url;
+ }
+ currentTopic.description = currentString;
+ } else if ([elementName isEqualToString:kName_ReleaseDate]) {
+ currentTopic.releaseDate = [parseFormatter dateFromString:currentString];
+ }
+ storingCharacters = NO;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
+ //NSLog(@"foundCharacters: %@", string); // storing string is when you want the inner text
+ if (storingCharacters) [currentString appendString:string];
+}
+
+/*
+ A production application should include robust error handling as part of its parsing implementation.
+ The specifics of how errors are handled depends on the application.
+ */
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
+ NSLog(@"parseError: %@", parseError);
+ // Handle errors as appropriate for your application.
+}
+
+@end
diff --git a/Classes/AsyncImageView.h b/Classes/AsyncImageView.h
new file mode 100644
index 0000000..049ba68
--- /dev/null
+++ b/Classes/AsyncImageView.h
@@ -0,0 +1,26 @@
+//
+// AsyncImageView.h
+// Postcard
+//
+// Created by markj on 2/18/09.
+// Copyright 2009 Mark Johnson. You have permission to copy parts of this code into your own projects for any use.
+// www.markj.net
+//
+
+#import <UIKit/UIKit.h>
+
+
+@interface AsyncImageView : UIView {
+ //could instead be a subclass of UIImageView instead of UIView, depending on what other features you want to
+ // to build into this class?
+
+ NSURLConnection* connection; //keep a reference to the connection so we can cancel download in dealloc
+ NSMutableData* data; //keep reference to the data so we can collect it as it downloads
+ //but where is the UIImage reference? We keep it in self.subviews - no need to re-code what we have in the parent class
+
+}
+
+- (void)loadImageFromURL:(NSURL*)url;
+- (UIImage*) image;
+
+@end
diff --git a/Classes/AsyncImageView.m b/Classes/AsyncImageView.m
new file mode 100644
index 0000000..d978e82
--- /dev/null
+++ b/Classes/AsyncImageView.m
@@ -0,0 +1,144 @@
+//
+// AsyncImageView.m
+// Postcard
+//
+// Created by markj on 2/18/09.
+// Copyright 2009 Mark Johnson. You have permission to copy parts of this code into your own projects for any use.
+// www.markj.net
+//
+
+#import "AsyncImageView.h"
+
+@interface UIImage (Extras)
+
+- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize;
+
+@end
+
+@implementation UIImage (Extras)
+
+#pragma mark -
+#pragma mark Scale and crop image
+
+- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
+{
+ UIImage *sourceImage = self;
+ UIImage *newImage = nil;
+ CGSize imageSize = sourceImage.size;
+ CGFloat width = imageSize.width;
+ CGFloat height = imageSize.height;
+ CGFloat targetWidth = targetSize.width;
+ CGFloat targetHeight = targetSize.height;
+ CGFloat scaleFactor = 0.0;
+ CGFloat scaledWidth = targetWidth;
+ CGFloat scaledHeight = targetHeight;
+ CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
+
+ if (CGSizeEqualToSize(imageSize, targetSize) == NO)
+ {
+ CGFloat widthFactor = targetWidth / width;
+ CGFloat heightFactor = targetHeight / height;
+
+ if (widthFactor > heightFactor)
+ scaleFactor = widthFactor; // scale to fit height
+ else
+ scaleFactor = heightFactor; // scale to fit width
+ scaledWidth = width * scaleFactor;
+ scaledHeight = height * scaleFactor;
+
+ // center the image
+ if (widthFactor > heightFactor)
+ {
+ thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
+ }
+ else
+ if (widthFactor < heightFactor)
+ {
+ thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
+ }
+ }
+
+ UIGraphicsBeginImageContext(targetSize); // this will crop
+
+ CGRect thumbnailRect = CGRectZero;
+ thumbnailRect.origin = thumbnailPoint;
+ thumbnailRect.size.width = scaledWidth;
+ thumbnailRect.size.height = scaledHeight;
+
+ [sourceImage drawInRect:thumbnailRect];
+
+ newImage = UIGraphicsGetImageFromCurrentImageContext();
+ if(newImage == nil)
+ NSLog(@"could not scale image");
+
+ //pop the context to get back to the default
+ UIGraphicsEndImageContext();
+ return newImage;
+}
+
+@end
+
+
+// This class demonstrates how the URL loading system can be used to make a UIView subclass
+// that can download and display an image asynchronously so that the app doesn't block or freeze
+// while the image is downloading. It works fine in a UITableView or other cases where there
+// are multiple images being downloaded and displayed all at the same time.
+
+@implementation AsyncImageView
+
+- (void)dealloc {
+ [connection cancel]; //in case the URL is still downloading
+ [connection release];
+ [data release];
+ [super dealloc];
+}
+
+
+- (void)loadImageFromURL:(NSURL*)url {
+ NSLog(@"%@",url);
+ if (connection!=nil) { [connection release]; } //in case we are downloading a 2nd image
+ if (data!=nil) { [data release]; }
+
+ NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
+ connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; //notice how delegate set to self object
+ //TODO error handling, what if connection is nil?
+}
+
+
+//the URL connection calls this repeatedly as data arrives
+- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
+ if (data==nil) { data = [[NSMutableData alloc] initWithCapacity:2048]; }
+ [data appendData:incrementalData];
+}
+
+//the URL connection calls this once all the data has downloaded
+- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection {
+ //so self data now has the complete image
+ [connection release];
+ connection=nil;
+ if ([[self subviews] count]>0) {
+ //then this must be another image, the old one is still in subviews
+ [[[self subviews] objectAtIndex:0] removeFromSuperview]; //so remove it (releases it also)
+ }
+
+ //make an image view for the image
+ UIImageView* imageView = [[[UIImageView alloc] initWithImage:[[UIImage imageWithData:data] imageByScalingAndCroppingForSize:CGSizeMake(100,100)]] autorelease];
+ //make sizing choices based on your needs, experiment with these. maybe not all the calls below are needed.
+ imageView.contentMode = UIViewContentModeScaleAspectFill;
+ imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight );
+ [self addSubview:imageView];
+ imageView.frame = self.bounds;
+ [imageView setNeedsLayout];
+ [self setNeedsLayout];
+
+ [data release]; //don't need this any more, its in the UIImageView now
+ data=nil;
+}
+
+//just in case you want to get the image directly, here it is in subviews
+- (UIImage*) image {
+ UIImageView* iv = [[self subviews] objectAtIndex:0];
+ return [iv image];
+}
+
+@end
diff --git a/Classes/Classes-1.moved-aside/TopicTableViewCell.xib b/Classes/Classes-1.moved-aside/TopicTableViewCell.xib
new file mode 100644
index 0000000..5762042
--- /dev/null
+++ b/Classes/Classes-1.moved-aside/TopicTableViewCell.xib
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1056</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">823</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="NS.object.0">132</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="1"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBProxyObject" id="372490531">
+ <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBProxyObject" id="975951072">
+ <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUIView" id="191373211">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">310</int>
+ <string key="NSFrameSize">{320, 460}</string>
+ <reference key="NSSuperview"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ <object class="NSColorSpace" key="NSCustomColorSpace">
+ <int key="NSID">2</int>
+ </object>
+ </object>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="191373211"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="372490531"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="975951072"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-2.CustomClassName</string>
+ <string>1.CustomClassName</string>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>UIResponder</string>
+ <string>TopicTableViewCell</string>
+ <string>{{354, 376}, {320, 480}}</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">2</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">TopicTableViewCell</string>
+ <string key="superclassName">UITableViewCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Classes/TopicTableViewCell.h</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="148314833">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIResponder</string>
+ <string key="superclassName">NSObject</string>
+ <reference key="sourceIdentifier" ref="148314833"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UITableViewCell</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITableViewCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <string key="superclassName">UIResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+ <integer value="1056" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+ <integer value="3100" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../PicCast.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <string key="IBCocoaTouchPluginVersion">132</string>
+ </data>
+</archive>
diff --git a/Classes/HJCircularBuffer.h b/Classes/HJCircularBuffer.h
new file mode 100755
index 0000000..f2b6452
--- /dev/null
+++ b/Classes/HJCircularBuffer.h
@@ -0,0 +1,44 @@
+//
+// HJCircularBuffer.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+
+#import <Foundation/Foundation.h>
+
+
+/*
+ Fixed size buffer (array) that you can add objects to. When it gets full to the end of the array,
+ it starts again from the front of the array replacing old objects with new ones.
+ Is implemented with an array full of [NSNull null] objects, so putting nulls in is like deleting the object at
+ the next available slot. The NSNulls never come out of this data structure, nil is returned instead of NSNull.
+*/
+@interface HJCircularBuffer : NSObject {
+ NSMutableArray* buffer;
+ int nextIndex;
+}
+
+
++(HJCircularBuffer*)bufferWithCapacity:(int)capacity;
+-(HJCircularBuffer*)initWithCapacity:(int)capacity;
+-(int)length;
+
+/**
+ * Add object to 'end' of buffer.
+ * @return an autoreleased object that had to be removed to make room for the object added.
+ */
+-(id)addObject:(id)obj;
+-(id)objectAtIndex:(int)i;
+-(void)removeObject:(id)key;
+-(void)removeObjectAtIndex:(int)i;
+-(id)swapObject:(NSObject*)obj atIndex:(int)i;
+-(id)findObject:(id)key;
+-(int)findIndexOfObject:(id)key;
+
+-(NSArray*)allObjects;
+
+@end
diff --git a/Classes/HJCircularBuffer.m b/Classes/HJCircularBuffer.m
new file mode 100755
index 0000000..ebb682d
--- /dev/null
+++ b/Classes/HJCircularBuffer.m
@@ -0,0 +1,149 @@
+//
+// HJCircularBuffer.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJCircularBuffer.h"
+
+
+@implementation HJCircularBuffer
+
++(HJCircularBuffer*)bufferWithCapacity:(int)capacity {
+ return [[[HJCircularBuffer alloc] initWithCapacity:capacity] autorelease];
+}
+
+-(HJCircularBuffer*)initWithCapacity:(int)capacity {
+ [super init];
+ buffer = [[NSMutableArray arrayWithCapacity:capacity] retain];
+ nextIndex=0;
+ NSNull* nullObj = [NSNull null];
+ for (int i=0; i<capacity; i++) {
+ [buffer addObject:nullObj];
+ }
+ return self;
+}
+
+-(void)dealloc {
+ [buffer release];
+ [super dealloc];
+}
+
+-(int)length {
+ return [buffer count];
+}
+
+/**
+ * Add object to 'end' of buffer.
+ * @return an autoreleased object that had to be removed to make room for the object added.
+ */
+-(id)addObject:(id)obj {
+ id oldObj = [self swapObject:obj atIndex:nextIndex];
+ nextIndex++;
+ if (nextIndex>=[buffer count]) {
+ nextIndex=0;
+ }
+ return oldObj;
+}
+
+-(id)swapObject:(id)obj atIndex:(int)i {
+ if ([buffer count]==0) {
+ return nil;
+ }
+ id oldObj = [buffer objectAtIndex:i];
+ if (oldObj!=[NSNull null]) {
+ //because when old objObj replaced in the buffer, it will be released and might dealloc:
+ [oldObj retain];
+ [oldObj autorelease];
+ }
+ [buffer replaceObjectAtIndex:nextIndex withObject:obj];
+ if (oldObj!=[NSNull null]) {
+ return oldObj;
+ }
+ return nil;
+}
+
+
+-(int)findIndexOfObject:(id)key {
+ NSNull* nullObj = [NSNull null];
+ id obj;
+ int i = nextIndex-1; //start looking at the last object
+ if (i<0) { i=0; }
+ for (int n = [buffer count]-1; n>=0; n--) {
+ if (i<0) {
+ i=[buffer count]-1; //i has to wrap around to the end of the buffer array
+ }
+ obj = [buffer objectAtIndex:i];
+ if (obj!=nullObj) {
+ BOOL result = [obj isEqual:key];
+ if (result) {
+ return i;
+ }
+ }
+ i--;
+ }
+ return -1;
+}
+
+-(id)findObject:(id)key {
+ int i = [self findIndexOfObject:key];
+ if (i<0) {
+ return nil;
+ }
+ return [self objectAtIndex:i];
+}
+
+-(id)objectAtIndex:(int)i {
+ id obj = [buffer objectAtIndex:i];
+ if (obj==[NSNull null]) {
+ return nil;
+ }
+ return obj;
+}
+
+-(void)removeObjectAtIndex:(int)i {
+ [buffer replaceObjectAtIndex:i withObject:[NSNull null]];
+}
+
+-(void)removeObject:(id)key {
+ int i = [self findIndexOfObject:key];
+ if (i<0) {
+ return;
+ }
+ //NSLog(@"remove at index %i",i);
+ [self removeObjectAtIndex:i];
+}
+
+-(NSArray*)allObjects {
+ NSMutableArray* all = [NSMutableArray arrayWithCapacity:[buffer count]];
+ for (id obj in buffer) {
+ if (![obj isKindOfClass:[NSNull class]]) {
+ [all addObject:obj];
+ }
+ }
+ return all;
+}
+
+
++(void)test {
+ /*
+ HJCircularBuffer* b = [HJCircularBuffer bufferWithCapacity:4];
+
+ id o;
+ o = [b addObject:@"zero"];
+ o = [b addObject:@"one"];
+ o = [b addObject:@"two"];
+ o = [b addObject:@"three"];
+ o = [b addObject:@"andBack"];
+
+ int i = [b findIndexOfObjectEqualTo:@"two"];
+ o = [b findObjectEqualTo:@"two"];
+ NSLog(@"%@",o);
+ */
+}
+
+
+@end
diff --git a/Classes/HJMOBigFileCache.h b/Classes/HJMOBigFileCache.h
new file mode 100755
index 0000000..0d4f7f1
--- /dev/null
+++ b/Classes/HJMOBigFileCache.h
@@ -0,0 +1,29 @@
+//
+// HJMOBigFileCache.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+#import "HJMOFileCache.h"
+
+/*
+ HJBigFileCache is more scalable for very large caches. When the cache gets very
+ large, then the trim time gets big because trimming the size of the cache requires
+ scanning through all of its files and checking their age and size. HJBigFileCache
+ divides that probem into 10 by trimming 1/10th of the cache each day in a 10 day cycle.
+ */
+
+@interface HJMOBigFileCache : HJMOFileCache {
+
+ NSNumber* lastTrimDirNum;
+ NSDate* lastTrimDate;
+}
+
+@property (retain) NSNumber* lastTrimDirNum;
+@property (retain) NSDate* lastTrimDate;
+
+@end
diff --git a/Classes/HJMOBigFileCache.m b/Classes/HJMOBigFileCache.m
new file mode 100755
index 0000000..f1a8725
--- /dev/null
+++ b/Classes/HJMOBigFileCache.m
@@ -0,0 +1,94 @@
+//
+// HJMOBigFileCache.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+
+#import "HJMOBigFileCache.h"
+
+
+NSString* HJMOBigFileCache_trimDirKey = @"HJMOBigFileCacheTrimDir";
+NSString* HJMOBigFileCache_trimDateKey = @"HJMOBigFileCacheTrimDate";
+
+
+@implementation HJMOBigFileCache
+
+@synthesize lastTrimDate;
+@synthesize lastTrimDirNum;
+
+- (void) dealloc {
+ [lastTrimDirNum release];
+ [lastTrimDate release];
+ [super dealloc];
+}
+
+-(void)createCacheDirsIfNeeded {
+ [super createCacheDirsIfNeeded];
+ for (int i=0; i<=9; i++) {
+ NSString* path = [NSString stringWithFormat:@"%@%i",readyPath,i];
+ [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+}
+
+-(NSString*)subdirForFilename:(NSString*)filename {
+ unichar c1 = [filename characterAtIndex:[filename length]-1];
+ unichar c2 = [filename characterAtIndex:[filename length]-2];
+ int hashpath = (c1+c2)%10;
+ return [NSString stringWithFormat:@"%i",hashpath];
+}
+
+-(NSString*)readyFilePathForOid:(id)oid {
+ NSString* filename = [self filenameForOid:oid];
+ NSString* subdir = [self subdirForFilename:filename];
+ NSString* path;
+ if (subdir==nil) {
+ path = [readyPath stringByAppendingString:filename];
+ } else {
+ path = [NSString stringWithFormat:@"%@%@/%@",readyPath,subdir,filename];
+ }
+ return path;
+}
+
+
+-(void)updateLastTrimState {
+ [[NSUserDefaults standardUserDefaults] setObject:self.lastTrimDirNum forKey:HJMOBigFileCache_trimDirKey];
+ [[NSUserDefaults standardUserDefaults] setObject:self.lastTrimDate forKey:HJMOBigFileCache_trimDateKey];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+
+/*
+ This only trims 1/10th of the cache at a time, and does this no more than once every 1hrs.
+ */
+-(void)trimCache {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ self.lastTrimDirNum = (NSNumber*) [[NSUserDefaults standardUserDefaults] objectForKey:HJMOBigFileCache_trimDirKey];
+ self.lastTrimDate = (NSDate*) [[NSUserDefaults standardUserDefaults] objectForKey:HJMOBigFileCache_trimDateKey];
+ NSDate* now = [[[NSDate alloc] init] autorelease];
+
+ if (lastTrimDate==nil || [now timeIntervalSinceDate:lastTrimDate]>60*60*1) {
+ if (lastTrimDirNum==nil) {
+ self.lastTrimDirNum = [NSNumber numberWithInt:-1];
+ }
+ int num = [lastTrimDirNum intValue];
+ num++;
+ if (num>=10) { num=0; }
+ NSString* nextTrimPath = [NSString stringWithFormat:@"%@%i/",readyPath,num];
+ [self trimCacheDir:nextTrimPath];
+
+ self.lastTrimDate = now;
+ self.lastTrimDirNum = [NSNumber numberWithInt:num];
+ [self updateLastTrimState];
+ }
+ [self performSelectorOnMainThread:@selector(updateLastTrimState) withObject:nil waitUntilDone:YES];
+
+ [pool release];
+}
+
+
+@end
diff --git a/Classes/HJMOFileCache.h b/Classes/HJMOFileCache.h
new file mode 100755
index 0000000..6cbbb24
--- /dev/null
+++ b/Classes/HJMOFileCache.h
@@ -0,0 +1,62 @@
+//
+// HJFiler.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+
+/*
+ Provides file caching for the object manager. Its able to trim its size so it
+ doesn't grow forever. See also HJBigFileCache.
+ */
+
+@interface HJMOFileCache : NSObject {
+
+ NSString* rootPath;
+ NSString* loadingPath;
+ NSString* readyPath;
+ NSString* countsPath;
+
+ BOOL isCounting;
+ BOOL isLRU;
+ long fileCount;
+ long long byteCount;
+
+ long fileCountLimit;
+ NSTimeInterval fileAgeLimit;
+ NSThread* maintenanceThread;
+}
+
+@property long fileCount;
+@property long long byteCount;
+@property long fileCountLimit;
+@property NSTimeInterval fileAgeLimit;
+@property (nonatomic, retain) NSThread* maintenanceThread;
+
+-(HJMOFileCache*)initWithRootPath:(NSString*)root;
+-(HJMOFileCache*)initWithRootPath:(NSString*)root
+ isCounting:(BOOL)isCounting
+ fileCountLimit:(long)countLimit
+ fileAgeLimit:(NSTimeInterval)ageLimit;
+
+-(void)createCacheDirsIfNeeded;
+
+-(NSString*)filenameForOid:(id)oid;
+-(NSString*)readyFilePathForOid:(id)oid;
+-(NSString*)loadingFilePathForOid:(id)oid;
+-(NSString*)loadingFinishedForOid:(id)oid;
+-(void)removeOid:(id)oid;
+
+-(void) deleteAllFilesInDir:(NSString*)path;
+-(void) emptyCache;
+-(void) saveCounts;
+-(void) loadCounts;
+-(void)trimCache;
+-(void)trimCacheUsingBackgroundThread;
+-(void)trimCacheDir:(NSString*)cachePath;
+
+@end
diff --git a/Classes/HJMOFileCache.m b/Classes/HJMOFileCache.m
new file mode 100755
index 0000000..7ba7b23
--- /dev/null
+++ b/Classes/HJMOFileCache.m
@@ -0,0 +1,280 @@
+//
+// HJFiler.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJMOFileCache.h"
+
+
+@implementation HJMOFileCache
+
+@synthesize fileCount;
+@synthesize byteCount;
+@synthesize fileAgeLimit;
+@synthesize fileCountLimit;
+@synthesize maintenanceThread;
+
+
+-(HJMOFileCache*)initWithRootPath:(NSString*)root {
+ [super init];
+ isCounting = NO;
+ fileCount = 0;
+ byteCount = 0;
+ rootPath = root;
+ [rootPath retain];
+
+ fileCountLimit = 0;
+ fileAgeLimit = 0;
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath:rootPath]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+
+ loadingPath = [[NSString stringWithFormat:@"%@/loading/",rootPath] retain];
+ readyPath = [[NSString stringWithFormat:@"%@/ready/",rootPath] retain];
+ [self createCacheDirsIfNeeded];
+ countsPath = [[NSString stringWithFormat:@"%@/counts.plist",rootPath] retain];
+
+ //delete any half loaded files
+ [self deleteAllFilesInDir:loadingPath];
+
+ return self;
+}
+
+-(HJMOFileCache*)initWithRootPath:(NSString*)root
+ isCounting:(BOOL)isCounting_
+ fileCountLimit:(long)countLimit
+ fileAgeLimit:(NSTimeInterval)ageLimit {
+
+ [self initWithRootPath:root];
+ isCounting = isCounting_;
+ fileCountLimit = countLimit;
+ fileAgeLimit = ageLimit;
+
+ if (isCounting) {
+ [self loadCounts];
+ }
+ return self;
+}
+
+
+-(void)dealloc {
+ [rootPath release];
+ [loadingPath release];
+ [readyPath release];
+ [countsPath release];
+ [maintenanceThread release];
+ [super dealloc];
+}
+
+
+-(void)createCacheDirsIfNeeded {
+ if (![[NSFileManager defaultManager] fileExistsAtPath:loadingPath]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:loadingPath withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+ if (![[NSFileManager defaultManager] fileExistsAtPath:readyPath]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:readyPath withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+}
+
+
+-(void) deleteAllFilesInDir:(NSString*)path {
+ NSError* err=nil;
+ NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&err];
+ if (err!=nil) {
+ return;
+ }
+ for (NSString* file in files) {
+ NSString* filepath = [NSString stringWithFormat:@"%@%@",path,file];
+ [[NSFileManager defaultManager] removeItemAtPath:filepath error:&err];
+ }
+}
+
+-(void) emptyCache {
+ [self deleteAllFilesInDir:readyPath];
+ [self deleteAllFilesInDir:loadingPath];
+ self.fileCount=0;
+ self.byteCount=0;
+ if (isCounting) {
+ [self saveCounts];
+ }
+}
+
+-(NSString*)filenameForOid:(id)oid {
+ NSString* oidStr = [NSString stringWithFormat:@"%@",oid];
+ oidStr = [oidStr stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
+ return oidStr;
+}
+
+-(NSString*)readyFilePathForOid:(id)oid {
+ NSString* filename = [self filenameForOid:oid];
+ NSString* path = [readyPath stringByAppendingString:filename];
+ return path;
+}
+
+-(NSString*)loadingFilePathForOid:(id)oid {
+ NSString* filename = [self filenameForOid:oid];
+ NSString* path = [loadingPath stringByAppendingString:filename];
+ return path;}
+
+-(NSString*)loadingFinishedForOid:(id)oid {
+ NSString* loadingFilename = [self loadingFilePathForOid:oid];
+ NSString* readyFilename = [self readyFilePathForOid:oid];
+ NSError* e=nil;
+ [[NSFileManager defaultManager] moveItemAtPath:loadingFilename toPath:readyFilename error:&e];
+ if (e) {
+ NSLog(@"HJMOFileCache failed to move loading file to ready file %@",readyFilename);
+ return nil;
+ } else {
+ if (isCounting) {
+ NSError* e;
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:readyFilename error:&e];
+ NSNumber* size = [dict objectForKey:NSFileSize];
+ fileCount++;
+ byteCount = byteCount + size.unsignedIntegerValue;
+ [self saveCounts];
+ }
+ return readyFilename;
+ }
+}
+
+-(void)removeOid:(id)oid {
+ NSError* err = nil;
+ NSString* path = [self readyFilePathForOid:oid];
+ NSError* e;
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&e]; NSNumber* size = [dict objectForKey:NSFileSize];
+ [[NSFileManager defaultManager] removeItemAtPath:path error:&err];
+ if (err==nil) {
+ fileCount--;
+ byteCount = byteCount - size.unsignedIntegerValue;
+ [self saveCounts];
+ } else {
+ //NSLog(@"HJMOFileCache error deleting %@",path);
+ }
+}
+
+
+-(void) saveCounts {
+ NSMutableDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithLongLong:byteCount],@"bytes",
+ [NSNumber numberWithLong:fileCount],@"files",nil];
+ [dict writeToFile:countsPath atomically:YES];
+}
+
+-(void) loadCounts {
+ NSMutableDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:countsPath];
+ NSNumber* files = [dict objectForKey:@"files"];
+ NSNumber* bytes = [dict objectForKey:@"bytes"];
+ if (files!=nil) {
+ fileCount = [files longValue];
+ } else {
+ files = 0;
+ }
+ if (bytes!=nil) {
+ byteCount = [bytes longLongValue];
+ } else {
+ byteCount = 0;
+ }
+}
+
+int fileAgeCompareFunction(id obj1, id obj2, void *context) {
+ NSNumber* age1 = [(NSArray*)obj1 objectAtIndex:0];
+ NSNumber* age2 = [(NSArray*)obj2 objectAtIndex:0];
+ return [age1 compare:age2];
+}
+
+
+-(void)trimCacheDir:(NSString*)cachePath {
+ //limit number of files by deleting the oldest ones.
+ //creation date used to see age of file
+ //modification date used to see staleness of file - how long since last used.
+
+ NSLog(@"triming cache %@",cachePath);
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSString *file;
+ NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:cachePath];
+
+ NSMutableArray* fileAges = [NSMutableArray arrayWithCapacity:fileCountLimit]; //used to sort files by age
+ int thisDirFileCount=0;
+ int deletedCount=0;
+ long deletedBytes=0;
+ // this loop is the slow part, which is why this whole method is run on a separate thread.
+ while (file = [dirEnum nextObject]) {
+ NSString* filename = [NSString stringWithFormat:@"%@%@",cachePath,file];
+ NSError* e;
+ NSDictionary* fsAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filename error:&e];
+ double ageSeconds = -1* [[fsAttributes fileModificationDate] timeIntervalSinceNow];
+ long filesize = [fsAttributes fileSize];
+ if (ageSeconds>fileAgeLimit && fileAgeLimit>0) {
+ //old files get deleted right away
+ NSError* err=nil;
+ [fileManager removeItemAtPath:filename error:&err];
+ if (isCounting && err==nil) {
+ deletedCount++;
+ deletedBytes += filesize;
+ }
+ } else {
+ //files that are not too old are going to be counted and sorted by age
+ thisDirFileCount++;
+ NSArray* fileAge; //a holder of filename, age, and size, for sorting names by file age
+ if (isCounting) {
+ fileAge = [NSArray arrayWithObjects:[NSNumber numberWithDouble:ageSeconds],filename,[NSNumber numberWithLong:filesize],nil];
+ } else {
+ fileAge = [NSArray arrayWithObjects:[NSNumber numberWithDouble:ageSeconds],filename,nil];
+ }
+ [fileAges addObject:fileAge];
+ }
+ }
+
+ if (thisDirFileCount >= fileCountLimit && fileCountLimit>0) {
+ //thisDirFileCount is still over the limit (even if some old files were deleted)
+ //so now have to delete the oldest files. Behavoir of cache will be FIFO or LRU depending on cache policy readsUpdateFileDate
+ [fileAges sortUsingFunction:fileAgeCompareFunction context:nil]; //sorted from oldest to youngest
+ //for (NSArray* a in fileAges) {
+ // NSLog(@"%@ %@",[a objectAtIndex:0],[a objectAtIndex:1]);
+ //}
+ int index = [fileAges count]-1;
+ //delete files until 20% under file count.
+ while ((thisDirFileCount)>(fileCountLimit*0.8) && index>0) {
+ NSError* err=nil;
+ NSArray* fileAgeObj = [fileAges objectAtIndex:index];
+ NSString* filename = [fileAgeObj objectAtIndex:1];
+ //NSLog(@"deleting %@",filename);
+ [fileManager removeItemAtPath:filename error:&err];
+ if (err==nil) {
+ thisDirFileCount--;
+ if (isCounting) {
+ NSNumber* filesize = [fileAgeObj objectAtIndex:2];
+ deletedCount++;
+ deletedBytes += [filesize longValue];
+ }
+ }
+ index--;
+ }
+ }
+ NSLog(@"cache file trimed %i files",deletedCount);
+ if (isCounting) {
+ fileCount -= deletedCount;
+ byteCount -= deletedBytes;
+ [self performSelectorOnMainThread:@selector(saveCounts) withObject:nil waitUntilDone:YES];
+ }
+}
+
+-(void)trimCache {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ [self trimCacheDir:readyPath];
+ [pool release];
+}
+
+-(void)trimCacheUsingBackgroundThread {
+ self.maintenanceThread = [[[NSThread alloc] initWithTarget:self selector:@selector(trimCache) object:nil] autorelease];
+ [maintenanceThread start];
+}
+
+
+@end
diff --git a/Classes/HJMOHandler.h b/Classes/HJMOHandler.h
new file mode 100755
index 0000000..5e6715d
--- /dev/null
+++ b/Classes/HJMOHandler.h
@@ -0,0 +1,76 @@
+//
+// HJManagedState.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+#import "HJWeakMutableArray.h"
+#import "HJMOUser.h"
+@class HJObjManager;
+@class HJMOPolicy;
+
+/*
+ HJMOHandler is an internal class, and should not be used directly
+ for most users of HJCache. The handler is responsible for sharing the managed object between
+ different HJMOUsers, and loading it from url. If two HJMOUsers have the same oid (or url)
+ then they share the same handler instance.
+ */
+
+@interface HJMOHandler : NSObject {
+ enum HJMOState { stateNew, stateLoading, stateLoaded, stateReady, stateFailed } state;
+ id oid;
+ BOOL isDataReady;
+ NSURL* url;
+ NSURLConnection* urlConn;
+ NSMutableData* moData;
+ NSString* moReadyDataFilename;
+ NSFileHandle* moLoadingDataFile;
+ id managedObj;
+ HJWeakMutableArray* users;
+ HJObjManager* objManager;
+ HJMOPolicy* ownPolicy;
+}
+
+@property (readonly) enum HJMOState state;
+@property (nonatomic, retain) id oid;
+@property (nonatomic, retain) NSURL* url;
+@property (nonatomic, retain) NSURLConnection* urlConn;
+@property (nonatomic, retain) NSData* moData;
+@property (nonatomic, retain) NSFileHandle* moLoadingDataFile;
+@property (nonatomic, retain) NSString* moReadyDataFilename;
+@property (nonatomic, retain) id managedObj;
+@property (nonatomic, assign) HJObjManager* objManager;
+@property (nonatomic, retain) HJMOPolicy* ownPolicy;
+
+
+-(HJMOHandler*)initWithOid:(id)oid_ url:(NSURL*)url_ objManager:objManager_;
+
+
+//in use means that it has one or more users
+-(BOOL)isInUse;
+
+//ready means that the managedObj is not nil and can be used by users. If not, then its unready
+-(BOOL)isReady;
+
+//loading means that its not ready yet, but it will become ready by itself because the url is or will start loading.
+-(BOOL)isLoading;
+
+
+-(void) addUser:(id<HJMOUser>)user;
+-(void) removeUser:(id<HJMOUser>)mo;
+-(void) activateHandlerForUser:(id<HJMOUser>)user ;
+-(void) startDownloadingFromURL;
+-(void) callbackReadyToUsers;
+-(void) callbackFailedToUsers;
+-(void) goFromLoadedToReady;
+-(void) cancelLoading;
+-(void) clearLoadingState;
+
+@end
+
+
+
diff --git a/Classes/HJMOHandler.m b/Classes/HJMOHandler.m
new file mode 100755
index 0000000..fb8643e
--- /dev/null
+++ b/Classes/HJMOHandler.m
@@ -0,0 +1,381 @@
+//
+// HJManagedState.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJMOHandler.h"
+#import "HJMOUser.h"
+#import "HJObjManager.h"
+#import "HJMOFileCache.h"
+
+
+@implementation HJMOHandler
+
+@synthesize state;
+@synthesize oid;
+@synthesize url;
+@synthesize urlConn;
+@synthesize moData;
+@synthesize moReadyDataFilename;
+@synthesize moLoadingDataFile;
+@synthesize managedObj;
+@synthesize objManager;
+@synthesize ownPolicy;
+
+
+-(HJMOHandler*)initWithOid:(id)oid_ url:(NSURL*)url_ objManager:objManager_{
+ [super init];
+ state = stateNew;
+ self.oid = oid_;
+ self.url = url_ ;
+ self.objManager = objManager_;
+ if (oid==nil) {
+ self.oid = url_;
+ }
+
+ users = [[HJWeakMutableArray alloc] initWithCapacity:1]; //it can expand automatically.
+ return self;
+}
+
+-(void)dealloc {
+ //NSLog(@"dealloc %@",self);
+ [urlConn cancel];
+ [self clearLoadingState];
+ [users release];
+ [url release];
+ [moReadyDataFilename release];
+ //NSLog(@"managed Obj retain count before handler dealloc %i",[managedObj retainCount]);
+ [managedObj release];
+ [ownPolicy release];
+ [oid release];
+ [super dealloc];
+}
+
+
+-(BOOL) isEqual:(id)object {
+ if (![object isKindOfClass:[HJMOHandler class]]) {
+ return NO;
+ }
+ return [oid isEqual:[(HJMOHandler*)object oid]];
+}
+
+-(HJMOPolicy*)policy {
+ if (ownPolicy) {
+ return ownPolicy;
+ } else {
+ return [objManager policy];
+ }
+}
+
+-(void)addUser:(id<HJMOUser>)user {
+ //check if already managing for this user (should not be if being used right)
+ if (nil==[users findObject:user]) {
+ [users addObject:user]; //did not already have user, so remember it (with a weak reference)
+ } else {
+ //can happen if users reused, and recycling code is lazy clearing old state, eg with UITableCellView
+ //NSLog(@"HJMOHandler was already managing for user");
+ }
+
+ if (user.moHandler==nil) {
+ //this is the normal case, so set the state
+ user.moHandler=self;
+ } else {
+ if (user.moHandler==self) {
+ //this is not what we expect, addUser has been called twice, but thats OK - do nothing
+ } else {
+ //user was pointing to another handler, this can happen when user is reused.
+ //have to make sure that the old handler knows it should no longer manage for this user
+ //otherwise it might send it callbacks, and it won't be able to become inactive (have no users)
+ [user.moHandler removeUser:user];
+ //now can assign the current state, which will also release the old one.
+ user.moHandler=self;
+ }
+ }
+}
+
+-(void) deleteFileIfExistsAtPath:(NSString*)path {
+ if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
+ NSError* e = nil;
+ [[NSFileManager defaultManager] removeItemAtPath:path error:&e];
+ if (e) {
+ NSLog(@"HJMOHandler error deleting file %@",path);
+ }
+ }
+}
+
+-(void)clearLoadingState {
+ self.urlConn=nil;
+ [moLoadingDataFile closeFile];
+ self.moLoadingDataFile = nil;
+ self.moData = nil;
+}
+
+-(void)cancelLoading {
+ if (state==stateLoading) {
+ [urlConn cancel];
+ [self clearLoadingState];
+ state=stateNew;
+ }
+}
+
+-(void)becameNotInUse {
+ //TODO is there more policy decisions here?
+ //[self cancelLoading]; //don't cancel loading, do that in dealloc. because object manager might be holding on to
+ //this handler in loadingHandlers to keep loading going
+}
+
+-(void)removeUser:(id<HJMOUser>)user {
+ [users removeObject:user];
+ [self retain]; //because the next line could dealloc self.
+ user.moHandler = nil;
+ if (![self isInUse]) {
+ [self becameNotInUse];
+ }
+ [self autorelease];
+}
+
+
+
+-(BOOL)isInUse {
+ return [users count]>0;
+}
+
+-(BOOL)isLoading {
+ return urlConn!=nil;
+}
+
+-(BOOL)isReady {
+ return managedObj!=nil;
+}
+
+-(void)touchFile:(NSString*)path {
+ HJMOFileCache* fileCache = objManager.fileCache;
+ if (fileCache==nil) { return; }
+ NSTimeInterval ageLimit = fileCache.fileAgeLimit;
+ if (ageLimit<=0) { return; }
+
+ NSFileManager* fileMan = [NSFileManager defaultManager];
+
+ NSError* e;
+ NSDictionary* fsAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&e];
+ double ageSeconds = -1* [[fsAttributes fileModificationDate] timeIntervalSinceNow];
+
+ if (ageSeconds>(ageLimit/4)) {
+ //to save writes, file age modification date isn't changed on every access, only if 1/4 of age limit old.
+ NSString *keyArray[1] = {NSFileModificationDate};
+ id objectArray[1] = {[NSDate dateWithTimeIntervalSinceNow:0]};
+ NSDictionary* attributes = [NSDictionary dictionaryWithObjects:objectArray forKeys:keyArray count:1];
+ NSError* err;
+ [fileMan setAttributes:attributes ofItemAtPath:path error:&err];
+ }
+}
+
+-(void)activateNewHandlerForUser:(id<HJMOUser>)user {
+ HJMOFileCache* fileCache = objManager.fileCache;
+ if (fileCache) {
+ //File caching is in use
+
+ NSString* readyFile = [fileCache readyFilePathForOid:oid];
+
+ if ([[NSFileManager defaultManager] fileExistsAtPath:readyFile]) {
+ //NSLog(@"HJCache loading from fileCache");
+ //mo is loaded as a file in file cache
+ self.moReadyDataFilename = readyFile;
+ if (self.policy.readsUpdateFileDate) {
+ [self touchFile:readyFile];
+ }
+ state = stateLoaded;
+ [self goFromLoadedToReady];
+ if (state == stateReady || state==stateLoaded) {
+ [objManager addHandlerToMemCache:self];
+ }
+ return;
+
+ } else {
+ //not loaded yet, so load to file because file cache in use
+ //NSLog(@"HJCache loading from url");
+ NSString* loadingFile = [fileCache loadingFilePathForOid:oid];
+
+ BOOL ok = [[NSFileManager defaultManager] createFileAtPath:loadingFile contents:nil attributes:nil];
+ if (!ok) {
+ state = stateFailed;
+ NSLog(@"HJMOHandler error creating loading file %@",loadingFile);
+ loadingFile = nil;
+ [self clearLoadingState];
+ [self callbackFailedToUsers];
+ return;
+ } else {
+ self.moLoadingDataFile = [NSFileHandle fileHandleForWritingAtPath:loadingFile];
+ }
+ }
+ }
+
+ //if file cache is in use temporary file name is prepared, either way now load from url
+ [self startDownloadingFromURL];
+}
+
+
+
+-(void)activateHandlerForUser:(id<HJMOUser>)user {
+ //stateNew, stateLoading, stateLoaded, stateReady, stateFailed
+
+ switch (state) {
+
+ case stateNew:
+ [self activateNewHandlerForUser:user];
+ return;
+
+ case stateLoading:
+ //handler is still loading, have to wait for it to load, so nop.
+ return;
+
+ case stateLoaded:
+ //for some reason it didn't go to ready when it was loaded, so try again now.
+ [self goFromLoadedToReady];
+ return;
+
+ case stateReady:
+ [user managedObjReady];
+ return;
+
+ case stateFailed:
+ [user managedObjFailed];
+ return;
+
+ default:
+ //not supposed to get here
+ NSLog(@"HJMOHandler activateHandlerForUser error, no recognized state");
+ break;
+ }
+}
+
+
+-(void)startDownloadingFromURL {
+ //NSLog(@"HJMOHandler starting download for %@",self);
+ HJMOPolicy* policy = [self policy];
+ NSURLRequest* request = [NSURLRequest requestWithURL:url
+ cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+ timeoutInterval:policy.urlTimeoutTime];
+ self.urlConn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+ [urlConn release];
+ if (urlConn==nil) {
+ NSLog(@"HJMOHandler nil URLConnection for %@",url);
+ state=stateFailed;
+ } else {
+ state=stateLoading;
+ //TODO if app is showing a network activity monitor in the status bar, here is where a call needs to be
+ // made to increment the number of active URLs
+ }
+}
+
+-(void) goFromLoadedToReady {
+ if ([users count]==0) {
+ //can't go to stateReady because there's no user to do it. stay in stateLoaded.
+ //this is not a bug, it can happen if the object has already been deleted before its content was
+ //loaded over the net, eg because scrolled off the top of a table.
+ //NSLog(@"HJMOHandler no user object to make it ready");
+ return;
+ }
+
+ self.managedObj=nil; //just to be sure there's not some old one around
+ //pick _one_ and only one user to take mo from loaded to ready
+ id<HJMOUser> user = [users objectAtIndex:0];
+ @try {
+ [user changeManagedObjStateFromLoadedToReady];
+ if (managedObj!=nil) {
+ state = stateReady; //because it worked
+ [self callbackReadyToUsers];
+ }
+ }
+ @catch (id exception) {
+ NSLog(@"%@",exception);
+ self.managedObj=nil;
+ }
+ @finally {
+ if (managedObj==nil) {
+ //managedObj was still nil, ie going from loaded to ready failed. go to stateFailed and clean up from caches
+ state = stateFailed;
+ self.moReadyDataFilename = nil;
+ self.moData=nil;
+ [objManager removeFromHandlerFromCaches:self];
+ [self callbackFailedToUsers];
+ }
+ }
+}
+
+-(void) callbackReadyToUsers {
+ for (id<HJMOUser> user in [users objectEnumerator]) {
+ [user managedObjReady];
+ }
+}
+
+-(void) callbackFailedToUsers {
+ for (id<HJMOUser> user in [users objectEnumerator]) {
+ [user managedObjFailed];
+ }
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ if (state!=stateLoading) { return; }
+ if (!moLoadingDataFile) {
+ //loading direct to memory
+ if (moData==nil) {
+ self.moData = [NSMutableData dataWithCapacity:1024*10];
+ }
+ [moData appendData:data];
+ } else {
+ [moLoadingDataFile writeData:data];
+ }
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ [self retain]; //ensure that self isn't released in this method when the connection is finished with it.
+ //NSLog(@"finishedLoading %@",self);
+ state = stateLoaded;
+ if (moLoadingDataFile) {
+ //was downloading to file
+ [moLoadingDataFile closeFile];
+ self.moLoadingDataFile = nil;
+ self.urlConn = nil;
+ NSString* readyFilename = [self.objManager.fileCache loadingFinishedForOid:oid];
+ if (readyFilename==nil) {
+ state = stateFailed;
+ [self callbackFailedToUsers];
+ return;
+ } else {
+ self.moReadyDataFilename = readyFilename;
+ }
+ }
+ //TODO if app is showing a network activity monitor in the status bar, here is where a call needs to be
+ // made to decrement the count of active URLs
+ [objManager handlerFinishedDownloading:self];
+ [self goFromLoadedToReady];
+ if (state==stateReady || state==stateLoaded) {
+ [objManager addHandlerToMemCache:self];
+ }
+ [self release];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ state = stateFailed;
+ NSLog(@"HJMOHandler URLConnection failed %@",error);
+ //TODO if app is showing a network activity monitor in the status bar, here is where a call needs to be
+ // made to decrement the count of active URLs
+ [self clearLoadingState];
+ self.moReadyDataFilename = nil;
+ self.moData=nil;
+ [objManager removeFromHandlerFromCaches:self];
+ [self callbackFailedToUsers];
+}
+
+
+-(NSString*)description {
+ return [NSString stringWithFormat:@"HJMOHandler %@ users:%i retains:%i",oid,[users count],[self retainCount]];
+}
+
+
+@end
diff --git a/Classes/HJMOPolicy.h b/Classes/HJMOPolicy.h
new file mode 100755
index 0000000..11fdbab
--- /dev/null
+++ b/Classes/HJMOPolicy.h
@@ -0,0 +1,45 @@
+//
+// HJManagedObjPolicy.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+
+/*
+ The policy has some settings for customizing how the object manger works.
+ Its optional, as the object manager has a default policy
+ */
+
+@interface HJMOPolicy : NSObject {
+
+ NSTimeInterval urlTimeoutTime;
+
+ //if YES, then when accessing a managed object that has a cahce file, the date on that
+ //cache file is updated, so that the file cache can trim its size by deleting only
+ //objects that have not been accessed recently
+ BOOL readsUpdateFileDate;
+
+ // TODO: make object manager use these parts of the policy
+ //BOOL loadToFile;
+ //BOOL cacheInMemory;
+ //BOOL asyncStartLoad;
+ //BOOL asyncIsReadyIfCached;
+}
+
+@property NSTimeInterval urlTimeoutTime;
+@property BOOL readsUpdateFileDate;
+
+
+//@property BOOL loadToFile;
+//@property BOOL cacheInMemory;
+//@property BOOL asyncStartLoad;
+//@property BOOL asyncIsReadyIfCached;
+
++(HJMOPolicy*) smallImgFastScrollLRUCachePolicy;
+
+
+@end
diff --git a/Classes/HJMOPolicy.m b/Classes/HJMOPolicy.m
new file mode 100755
index 0000000..25f0eba
--- /dev/null
+++ b/Classes/HJMOPolicy.m
@@ -0,0 +1,45 @@
+//
+// HJManagedObjPolicy.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJMOPolicy.h"
+
+
+@implementation HJMOPolicy
+
+@synthesize urlTimeoutTime;
+@synthesize readsUpdateFileDate;
+
+//@synthesize loadToFile;
+//@synthesize cacheInMemory;
+//@synthesize asyncStartLoad;
+//@synthesize asyncIsReadyIfCached;
+
+
++(HJMOPolicy*) smallImgFastScrollLRUCachePolicy {
+ //this is the default policy settings.
+ HJMOPolicy* policy = [[[HJMOPolicy alloc] init] autorelease];
+ return policy;
+}
+
+/** default policy is good for small images, fast scrolling, async updates, LRU file cache */
+-(HJMOPolicy*)init {
+ [super init];
+ self.urlTimeoutTime = 30;
+ self.readsUpdateFileDate = YES;
+
+ //self.loadToFile = YES;
+ //self.cacheInMemory = YES;
+ //self.asyncStartLoad = YES;
+ //self.asyncIsReadyIfCached = YES;
+
+ return self;
+}
+
+
+@end
diff --git a/Classes/HJMOUser.h b/Classes/HJMOUser.h
new file mode 100755
index 0000000..4d16707
--- /dev/null
+++ b/Classes/HJMOUser.h
@@ -0,0 +1,68 @@
+//
+// HJManagedObj.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+
+@class HJMOHandler;
+
+/*
+ HJMOUser is a protocol that is used to make a class works with a 'managed object' that can
+ then be managed by the HJCache object manager. The HJMOUser contains a managed object and supplies
+ information about it, and the managed object itself gets loaded, cached, shared, by the object
+ manager.
+ See the implementation of HJManagedImageV as a referance for writting your own HJMOUser classes.
+ */
+
+@protocol HJMOUser
+
+/*
+ The object id for the managed object this HJMOUser is using. You can leave the oid nil,
+ and the url will be used as the oid. Some servers will tell you different urls for loading the
+ same image as part of their server load balancing design. Eg if you get photos from facebook,
+ then facebook will send you different urls for the same photo id. By using oid you can 'de-dupe'
+ those urls and prevent multiple network loads for images that are already cached.
+ */
+@property (nonatomic, retain) id oid;
+
+/*
+ The url from which to load the managed object data
+ */
+@property (nonatomic, retain) NSURL* url;
+
+/*
+ The handler is an internal object to HJCache and its what ties the managed object user together
+ with the managed object and the object manager. moHandler must be released in the dealloc
+ method of any HJMOUser class. The handler has a weak reference to the HJMOUser, so
+ dealloc will be called acording to normal Cocoa memory management conventions whether or not
+ the managed object is being cached, etc.
+ */
+@property (nonatomic, retain) HJMOHandler* moHandler;
+
+/*
+ This method is called by the object manager after data has been loaded from url or cache,
+ to create the managed object itself. Its this method that allows the HJCache to manage any
+ kind of object, eg UIImage, NSXMLDocument, etc, instead of just one specific class.
+ Note that this method is not called on every HJMOUser object, because if two different users
+ use the same managed object (same oid / url) then that managed object is only instanciated
+ once and its then shared between the users. See the implementation of this method in
+ HJManagedImageV as a referance for writting your own HJMOUser classes.
+ */
+-(void) changeManagedObjStateFromLoadedToReady;
+
+/*
+ called when the object manager has made the managed object ready
+ */
+-(void) managedObjReady;
+
+/*
+ called when the object manager has failed to make the managed object ready
+ */
+-(void) managedObjFailed;
+
+@end
diff --git a/Classes/HJMOUserBase.h b/Classes/HJMOUserBase.h
new file mode 100755
index 0000000..781d1d4
--- /dev/null
+++ b/Classes/HJMOUserBase.h
@@ -0,0 +1,27 @@
+//
+// HJManagedObjBase.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+#import "HJMOUser.h"
+#import "HJMOHandler.h"
+
+/*
+ Just a simple base class for your own HJMOUser classes. For convienience only,
+ an HJMOUser doesn't have to extend HJMOUserBase
+ */
+
+@interface HJMOUserBase : NSObject <HJMOUser> {
+
+ HJMOHandler* moHandler;
+ id oid;
+ NSURL* url;
+
+}
+
+@end
diff --git a/Classes/HJMOUserBase.m b/Classes/HJMOUserBase.m
new file mode 100755
index 0000000..3cfbf6c
--- /dev/null
+++ b/Classes/HJMOUserBase.m
@@ -0,0 +1,42 @@
+//
+// HJManagedObjBase.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJMOUserBase.h"
+
+
+@implementation HJMOUserBase
+
+@synthesize moHandler;
+@synthesize oid;
+@synthesize url;
+
+
+-(void) dealloc {
+ [moHandler removeUser:self];
+ [moHandler release];
+ [oid release];
+ [url release];
+ [super dealloc];
+}
+
+-(void) changeManagedObjStateFromLoadedToReady {
+
+}
+
+-(void) managedObjReady {
+
+}
+
+-(void) managedObjFailed {
+
+}
+
+
+
+@end
diff --git a/Classes/HJManagedImageV.h b/Classes/HJManagedImageV.h
new file mode 100755
index 0000000..c817f99
--- /dev/null
+++ b/Classes/HJManagedImageV.h
@@ -0,0 +1,74 @@
+//
+// HJManagedImageV.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+#import "HJMOUser.h"
+#import "HJMOHandler.h"
+
+
+/*
+ The managed image view is a UIView subclass that can be used just like any other view,
+ and it contains a UIImage object thats a managed object, and hence
+ can be cached, shared, and asynchronously loaded by the object manager.
+ So you can think of it like a UIImageView
+ that has built in image object sharing, asynchronous loading, and caching.
+ If you want to use HJCache for handling images (which is what its primarily designed for)
+ you'll probably want to use this class or your own version of it.
+ NB HJManagedImageV is an example of how to use HJCache, its not fully functional for all cases,
+ so don't be afraid to code your own version of it to suit your needs.
+ */
+
+@class HJManagedImageV;
+
+@protocol HJManagedImageVDelegate
+ -(void) managedImageSet:(HJManagedImageV*)mi;
+ -(void) managedImageCancelled:(HJManagedImageV*)mi;
+@end
+
+
+
+@interface HJManagedImageV : UIView <HJMOUser> {
+
+ id oid;
+ NSURL* url;
+ HJMOHandler* moHandler;
+ BOOL squareCropped;
+ UIImage* image;
+ UIImageView* imageView;
+ id callbackOnSetImage;
+ id callbackOnCancel;
+ BOOL isCancelled;
+ UIActivityIndicatorView* loadingWheel;
+ NSInvocation* onImageTap;
+ int index; // optional; may be used to assign an ordering to a image.
+ int modification;
+}
+
+@property int modification;
+@property int index;
+@property BOOL squareCropped;
+@property (nonatomic, retain) UIImage* image;
+@property (nonatomic, retain) UIImageView* imageView;
+@property (nonatomic, retain) UIActivityIndicatorView* loadingWheel;
+@property (nonatomic, retain) id <HJManagedImageVDelegate> callbackOnSetImage;
+@property (nonatomic, retain) id <HJManagedImageVDelegate> callbackOnCancel;
+
+-(void) clear;
+-(void) markCancelled;
+-(UIImage*) modifyImage:(UIImage*)theImage modification:(int)mod;
+-(void) setImage:(UIImage*)theImage modification:(int)mod;
+-(void) showLoadingWheel;
+-(void) setCallbackOnImageTap:(id)obj method:(SEL)m;
+
+@end
+
+
+
+
+
diff --git a/Classes/HJManagedImageV.m b/Classes/HJManagedImageV.m
new file mode 100755
index 0000000..eb83f7d
--- /dev/null
+++ b/Classes/HJManagedImageV.m
@@ -0,0 +1,254 @@
+//
+// HJManagedImageV.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJManagedImageV.h"
+
+@interface UIImage (Extras)
+
+- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize;
+
+@end
+
+@implementation UIImage (Extras)
+
+#pragma mark -
+#pragma mark Scale and crop image
+
+- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
+{
+ UIImage *sourceImage = self;
+ UIImage *newImage = nil;
+ CGSize imageSize = sourceImage.size;
+ CGFloat width = imageSize.width;
+ CGFloat height = imageSize.height;
+ CGFloat targetWidth = targetSize.width;
+ CGFloat targetHeight = targetSize.height;
+ CGFloat scaleFactor = 0.0;
+ CGFloat scaledWidth = targetWidth;
+ CGFloat scaledHeight = targetHeight;
+ CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
+
+ if (CGSizeEqualToSize(imageSize, targetSize) == NO)
+ {
+ CGFloat widthFactor = targetWidth / width;
+ CGFloat heightFactor = targetHeight / height;
+
+ if (widthFactor > heightFactor)
+ scaleFactor = widthFactor; // scale to fit height
+ else
+ scaleFactor = heightFactor; // scale to fit width
+ scaledWidth = width * scaleFactor;
+ scaledHeight = height * scaleFactor;
+
+ // center the image
+ if (widthFactor > heightFactor)
+ {
+ thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
+ }
+ else
+ if (widthFactor < heightFactor)
+ {
+ thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
+ }
+ }
+
+ UIGraphicsBeginImageContext(targetSize); // this will crop
+
+ CGRect thumbnailRect = CGRectZero;
+ thumbnailRect.origin = thumbnailPoint;
+ thumbnailRect.size.width = scaledWidth;
+ thumbnailRect.size.height = scaledHeight;
+
+ [sourceImage drawInRect:thumbnailRect];
+
+ newImage = UIGraphicsGetImageFromCurrentImageContext();
+ if(newImage == nil)
+ NSLog(@"could not scale image");
+
+ //pop the context to get back to the default
+ UIGraphicsEndImageContext();
+ return newImage;
+}
+
+@end
+
+@implementation HJManagedImageV
+
+
+@synthesize oid;
+@synthesize url;
+@synthesize moHandler;
+@synthesize squareCropped;
+@synthesize callbackOnSetImage;
+@synthesize callbackOnCancel;
+@synthesize imageView;
+@synthesize modification;
+@synthesize loadingWheel;
+@synthesize index;
+
+
+- (id)initWithFrame:(CGRect)frame {
+ if (self = [super initWithFrame:frame]) {
+ isCancelled=NO;
+ modification=0;
+ url=nil;
+ onImageTap = nil;
+ squareCropped = false;
+ index = -1;
+ self.userInteractionEnabled = NO; //because want to treat it like a UIImageView. Just turn this back on if you want to catch taps.
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self clear];
+ self.callbackOnCancel=nil;
+ self.callbackOnSetImage=nil;
+ self.loadingWheel=nil;
+ [super dealloc];
+ //NSLog(@"ManagedImage dealloc");
+}
+
+
+-(void) clear {
+ [self.moHandler removeUser:self];
+ self.moHandler=nil;
+ [imageView removeFromSuperview];
+ self.image = nil;
+ self.imageView.image=nil;
+ self.imageView=nil;
+ self.oid=nil;
+ self.url=nil;
+}
+
+/*
+-(void) clear {
+ self.url = nil;
+ self.callbackOnSetImage = nil;
+ //int rc1 = [image retainCount];
+ [self.imageView removeFromSuperview];
+ self.imageView = nil;
+ //int rc2 = [image retainCount];
+ [image release]; image=nil; //do this instead of self.image=nil because setImage has more code
+ self.loadingWheel = nil;
+}
+*/
+
+
+-(void) changeManagedObjStateFromLoadedToReady {
+ //NSLog(@"managedStateReady %@",managedState);
+ if (moHandler.moData) {
+ moHandler.managedObj=[UIImage imageWithData:moHandler.moData];
+ } else if (moHandler.moReadyDataFilename) {
+ moHandler.managedObj=[UIImage imageWithContentsOfFile:moHandler.moReadyDataFilename];
+ } else {
+ //error?
+ NSLog(@"HJManagedImageV error in changeManagedObjStateFromLoadedToReady ?");
+ }
+}
+
+-(void) managedObjFailed {
+ NSLog(@"moHandlerFailed %@",moHandler);
+ [image release];
+ image = nil;
+}
+
+-(void) managedObjReady {
+ //NSLog(@"moHandlerReady %@",moHandler);
+ [self setImage:moHandler.managedObj];
+}
+
+
+-(UIImage*) image {
+ return image;
+}
+
+-(void) markCancelled {
+ isCancelled = YES;
+ [callbackOnCancel managedImageCancelled:self];
+}
+
+-(UIImage*) modifyImage:(UIImage*)theImage modification:(int)mod {
+ return theImage;
+}
+
+
+-(void) setImage:(UIImage*)theImage modification:(int)mod {
+ if (mod==modification) {
+ [self setImage:theImage];
+ } else {
+ UIImage* modified = [self modifyImage:theImage modification:(int)mod];
+ [self setImage:modified];
+ }
+}
+
+
+-(void) setImage:(UIImage*)theImage {
+ if (theImage==image) {
+ //when the same image is on the screen multiple times, an image that is alredy set might be set again with the same image.
+ return;
+ }
+ [theImage retain];
+ [image release];
+ image = theImage;
+
+ [imageView removeFromSuperview];
+
+ if (squareCropped)
+ self.imageView = [[[UIImageView alloc] initWithImage:[theImage imageByScalingAndCroppingForSize:CGSizeMake(100,100)]] autorelease];
+ else
+ self.imageView = [[[UIImageView alloc] initWithImage:theImage] autorelease];
+
+ imageView.contentMode = UIViewContentModeScaleAspectFit;
+ imageView.autoresizingMask = ( UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight );
+ [self addSubview:imageView];
+ imageView.frame = self.bounds;
+ [imageView setNeedsLayout];
+ [self setNeedsLayout];
+ //NSLog(@"setImageCallback from %@ to %@",self,callbackOnSetImage);
+ [loadingWheel stopAnimating];
+ [loadingWheel removeFromSuperview];
+ self.loadingWheel = nil;
+ self.hidden=NO;
+ if (image!=nil) {
+ [callbackOnSetImage managedImageSet:self];
+ }
+}
+
+-(void) showLoadingWheel {
+ [loadingWheel removeFromSuperview];
+ self.loadingWheel = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
+ loadingWheel.center = self.center;
+ loadingWheel.hidesWhenStopped=YES;
+ [self addSubview:loadingWheel];
+ [loadingWheel startAnimating];
+}
+
+-(void) setCallbackOnImageTap:(id)obj method:(SEL)m {
+ NSInvocation* invo = [NSInvocation invocationWithMethodSignature:[obj methodSignatureForSelector:m]];
+ [invo setTarget:obj];
+ [invo setSelector:m];
+ [invo setArgument:&self atIndex:2];
+ [invo retain];
+ [onImageTap release];
+ onImageTap = invo;
+ self.userInteractionEnabled=YES; //because it's NO in the initializer, but if we want to get a callback on tap,
+ //then need to get touch events.
+}
+
+-(void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
+ if (onImageTap) {
+ [onImageTap invoke];
+ }
+ else {
+ [super touchesEnded:touches withEvent:event];
+ }
+}
+
+@end
diff --git a/Classes/HJManagedImageVDelegate.h b/Classes/HJManagedImageVDelegate.h
new file mode 100755
index 0000000..63d3b4e
--- /dev/null
+++ b/Classes/HJManagedImageVDelegate.h
@@ -0,0 +1,14 @@
+//
+// HJManagedImageVDelegate.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+
+@class HJManagedImageV;
+
+
diff --git a/Classes/HJObjManager.h b/Classes/HJObjManager.h
new file mode 100755
index 0000000..d45f1da
--- /dev/null
+++ b/Classes/HJObjManager.h
@@ -0,0 +1,106 @@
+//
+// HJObjManager.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+#import "HJCircularBuffer.h"
+#import "HJMOHandler.h"
+#import "HJMOUser.h"
+#import "HJMOPolicy.h"
+#import "HJMOFileCache.h"
+
+
+/*
+ HJObjManager is the object manager at the heart of HJCache. Normally you instanciate
+ one of these, assign it an HJMOFileCache, and then share it across the whole app. You can
+ however have as multiple HJMObjManager instances if you need more than one cache because
+ you are dealing with different types of cached data.
+ Note, HJObjManager should only be called from the main thread.
+ */
+
+@interface HJObjManager : NSObject {
+
+ HJMOPolicy* policy;
+ HJCircularBuffer* loadingHandlers;
+ HJCircularBuffer* memCache;
+ HJMOHandler* flyweightManagedState;
+ HJMOFileCache* fileCache;
+}
+
+/*
+ An options file cache for this object manager. If you want a file cache, just assign
+ this right after instanciation.
+ */
+@property (nonatomic, retain) HJMOFileCache* fileCache;
+
+/*
+ Used to tweak the behavoir of this object manager. Its usually fine to just
+ use the default one the object manager creates for itself.
+ */
+@property (nonatomic, retain) HJMOPolicy* policy;
+
+/*
+ Internal state. These are the HJMOHandlers that are currently actively loading.
+ */
+@property (nonatomic, retain) HJCircularBuffer* loadingHandlers;
+
+/*
+ Internal state. This is the memory cache of managed objects
+ */
+@property (nonatomic, retain) HJCircularBuffer* memCache;
+
+
+/*
+ initilize the object manager with custom sizes for its internal buffer
+ @param loadingBufferSize how many user objects can be loading at the same time, a FIFO buffer.
+ If you are using managed images in a scrolling table view, loadingBufferSize should be at least
+ as big as the number of images on the screen at the same time.
+ @param memCacheSize how many user objects are cached in memory
+ */
+-(HJObjManager*) initWithLoadingBufferSize:(int)loadingBufferSize memCacheSize:(int)memCacheSize;
+
+-(HJObjManager*) init;
+
+/*
+ If HJObjManager needs to be used to load a lot of managed objects at once, it's loading buffer needs to be big enough
+ to hold them all. This method can be used to change loading buffer size to accomodate the application going into
+ a different mode that needs to do this. It will cancel the loading of any objects that are loading at the time.
+ */
+-(void) resetLoadingBufferToSize:(int)loadingBufferSize;
+
+/*
+ tells object manager to manage this user object, which will cause the object to
+ be loaded from in-memory cache, file cache, or its url.
+ This method should only be called from the main thread of the app. If you need to call
+ it from a different thread, use this:
+ [self.objectManager performSelectorOnMainThread:@selector(manage:) withObject:managedImage waitUntilDone:YES];
+ */
+-(BOOL) manage:(id<HJMOUser>)user;
+
+/*
+ called by HJMOHandler, which is internal to the object manager and HJMOUser
+ */
+-(void) addHandlerToMemCache:(HJMOHandler*)handler;
+
+/*
+ called by HJMOHandler, which is internal to the object manager and HJMOUser
+ */
+-(void) handlerFinishedDownloading:(HJMOHandler*)handler;
+
+/*
+ cancels any objects that are currently loading
+ */
+-(void) cancelLoadingObjects;
+
+/*
+ removed a single handler and its cached managed object
+ */
+-(void) removeFromHandlerFromCaches:(HJMOHandler*)handler;
+
+
+@end
diff --git a/Classes/HJObjManager.m b/Classes/HJObjManager.m
new file mode 100755
index 0000000..e1ef862
--- /dev/null
+++ b/Classes/HJObjManager.m
@@ -0,0 +1,150 @@
+//
+// HJObjManager.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJObjManager.h"
+
+
+@implementation HJObjManager
+
+@synthesize policy;
+@synthesize loadingHandlers;
+@synthesize memCache, fileCache;
+
+
+
+-(HJObjManager*) init {
+ return [self initWithLoadingBufferSize:12 memCacheSize:20];
+}
+
+-(HJObjManager*) initWithLoadingBufferSize:(int)loadingBufferSize memCacheSize:(int)memCacheSize {
+ [super init];
+ self.policy = [HJMOPolicy smallImgFastScrollLRUCachePolicy];
+ self.loadingHandlers = [HJCircularBuffer bufferWithCapacity:loadingBufferSize];
+ self.memCache = [HJCircularBuffer bufferWithCapacity:memCacheSize];
+ flyweightManagedState = [[HJMOHandler alloc] init];
+ return self;
+}
+
+-(void) dealloc {
+ //if the HJObjManager is stored in a view that gets dealloced, and that view is also using the object managers
+ // managed objects, then its possible for the object manager to dealloc before the HJMOHandlers.
+ // For this reason, have to cancel any loading handlers here, so that they don't finish loading when the
+ // object manager itself is dealloced.
+ [self cancelLoadingObjects];
+
+ self.loadingHandlers=nil;
+ self.memCache=nil;
+ self.policy=nil;
+ [flyweightManagedState release];
+ self.fileCache=nil;
+ [super dealloc];
+}
+
+/*
+ If HJObjManager needs to be used to load a lot of managed objects at once, it's loading buffer needs to be big enough
+ to hold them all. This method can be used to change loading buffer size to accomodate the application going into
+ a different mode that needs to do this. It will cancel the loading of any objects that are loading at the time.
+ */
+-(void) resetLoadingBufferToSize:(int)loadingBufferSize {
+ [self cancelLoadingObjects];
+ self.loadingHandlers = [HJCircularBuffer bufferWithCapacity:loadingBufferSize];
+}
+
+/*
+ tells object manager to manage this user object, which will cause the object to
+ be loaded from in-memory cache, file cache, or its url.
+ This method should only be called from the main thread of the app. If you need to call
+ it from a different thread, use this:
+ [self.objectManager performSelectorOnMainThread:@selector(manage:) withObject:managedImage waitUntilDone:YES];
+ */
+-(BOOL) manage:(id<HJMOUser>)user {
+ id oid;
+ if (! (oid=[user oid])) {
+ //oid is nil, try to use the url as the oid
+ if (! (oid=[user url])) {
+ //oid and url are nil, so can't manage this object
+ return NO;
+ }
+
+ }
+
+ //find handler for this oid in caches, or make a new handler.
+ HJMOHandler* handler;
+ BOOL handlerWasAllocedInThisCall=NO;
+ BOOL handlerWasFromLoadingBuffer=NO;
+
+ //look in loadingHandlers first.
+ flyweightManagedState.oid = oid;
+ handler = [loadingHandlers findObject:flyweightManagedState];
+ if (handler!=nil) {
+ //if handler from loadingHandlers, its probably in stateLoading, remember this so we don't add it to loadingHandlers again
+ handlerWasFromLoadingBuffer = YES;
+ //NSLog(@"HJCache loading from loadingBuffer");
+
+ } else {
+ //look in memCache for handler
+ handler = [memCache findObject:flyweightManagedState];
+
+ if (handler==nil) {
+ //was not in loadingHandlers or memCache. have to make a new handler to load image
+ handler = [[HJMOHandler alloc] initWithOid:user.oid url:user.url objManager:self];
+ handlerWasAllocedInThisCall=YES;
+ } else {
+ //NSLog(@"HJCache loading from memCache");
+ }
+ }
+
+ //now use the handler can be used, whatever state its in.
+ [handler addUser:user];
+ [handler activateHandlerForUser:user]; //this can 'get things going' whatever state the handler is in
+
+ //check if handler is loading and needs to be added to loadingHandlers buffer
+ if (!handlerWasFromLoadingBuffer && handler.state == stateLoading) {
+ //put in loadingHandlers, which is a cirular buffer so we might bump a handler out
+ HJMOHandler* bumpedHandler = (HJMOHandler*) [loadingHandlers addObject:handler];
+
+ //the whole point of the loadingHandlers is to limit how many handlers are loading at the same time,
+ // so if something gets bumped out of loadingHandlers, loading has to be cancelled and it won't go in to memCache.
+ // This is why the loadingHandlers should always be at least the number of managed objects on the screen at the same time,
+ // otherwise on-screen managed objects will get loading canceled.
+ // Can't just wait and cancel in dealloc because cell reuse typically means managed object users don't get
+ // dealloced until a cell gets reused.
+ [bumpedHandler cancelLoading]; //usually nil, but could be non nil when scrolling fast
+ }
+
+ if (handlerWasAllocedInThisCall) {
+ [handler release];
+ }
+
+ return YES; //yes this object is now being managed. only NO if misused.
+}
+
+-(void) addHandlerToMemCache:(HJMOHandler*)handler {
+ [memCache addObject:handler]; //we can ignore any handler bumped from mem cache
+}
+
+-(void) handlerFinishedDownloading:(HJMOHandler*)handler {
+ [loadingHandlers removeObject:handler];
+}
+
+-(void) cancelLoadingObjects {
+ for (HJMOHandler* handler in [loadingHandlers allObjects]) {
+ [handler cancelLoading];
+ }
+}
+
+-(void) removeFromHandlerFromCaches:(HJMOHandler*)handler {
+ [loadingHandlers removeObject:handler];
+ [memCache removeObject:handler];
+ if (fileCache) {
+ [fileCache removeOid:handler.oid];
+ }
+}
+
+@end
diff --git a/Classes/HJWeakMutableArray.h b/Classes/HJWeakMutableArray.h
new file mode 100755
index 0000000..7637c91
--- /dev/null
+++ b/Classes/HJWeakMutableArray.h
@@ -0,0 +1,41 @@
+//
+// HJWeakMutableArray.h
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import <Foundation/Foundation.h>
+
+
+/*
+ A mutable array of weak references. Used so that we can hold 'back references' to objects
+ without preventing those objects from getting deallocated by normal memory management.
+ */
+@interface HJWeakMutableArray : NSObject {
+ NSMutableArray* array;
+}
+
++(HJWeakMutableArray*) arrayWithCapacity:(int)capacity;
+-(HJWeakMutableArray*) initWithCapacity:(int)capacity;
+
+- (void) addObject:(id)anObject;
+- (id) objectAtIndex:(int)i;
+- (id) findObject:(id)obj;
+- (void) removeObject:(id)obj;
+- (int) count;
+- (NSEnumerator*) objectEnumerator;
+
+@end
+
+
+@interface HJWeakMutableArrayEnumerator : NSEnumerator {
+ NSEnumerator* arrayEnum;
+}
+
+-(HJWeakMutableArrayEnumerator*)initWithInternalArray:(NSArray*)array;
+
+@end
+
diff --git a/Classes/HJWeakMutableArray.m b/Classes/HJWeakMutableArray.m
new file mode 100755
index 0000000..ce5988f
--- /dev/null
+++ b/Classes/HJWeakMutableArray.m
@@ -0,0 +1,144 @@
+//
+// HJWeakMutableArray.m
+// hjlib
+//
+// Copyright Hunter and Johnson 2009, 2010, 2011
+// HJCache may be used freely in any iOS or Mac application free or commercial.
+// May be redistributed as source code only if all the original files are included.
+// See http://www.markj.net/hjcache-iphone-image-cache/
+
+#import "HJWeakMutableArray.h"
+
+
+@implementation HJWeakMutableArray
+
++(HJWeakMutableArray*) arrayWithCapacity:(int)capacity {
+ return [[[HJWeakMutableArray alloc] initWithCapacity:capacity] autorelease];
+}
+
+-(HJWeakMutableArray*) initWithCapacity:(int)capacity {
+ [super init];
+ array = [[NSMutableArray arrayWithCapacity:capacity] retain];
+ return self;
+}
+
+-(void) dealloc {
+ [array release];
+ [super dealloc];
+}
+
+- (void)addObject:(id)anObject {
+ [array addObject:[NSValue valueWithNonretainedObject:anObject]];
+}
+
+-(id)objectAtIndex:(int)i {
+ return [(NSValue*)[array objectAtIndex:i] nonretainedObjectValue];
+}
+
+- (void) removeObject:(id)objToRemove {
+ for (int i=0; i<[array count]; i++) {
+ NSValue* val = [array objectAtIndex:i];
+ id obj = [val nonretainedObjectValue];
+ if ([obj isEqual:objToRemove]) {
+ [array removeObjectAtIndex:i];
+ return;
+ }
+ }
+ return;
+}
+
+- (id) findObject:(id)obj {
+ for (int i=0; i<[array count]; i++) {
+ NSValue* val = [array objectAtIndex:i];
+ id objI = [val nonretainedObjectValue];
+ if ([objI isEqual:obj]) {
+ return objI;
+ }
+ }
+ return nil;
+}
+
+-(int)count {
+ return [array count];
+}
+
+- (NSEnumerator*)objectEnumerator {
+ return [[[HJWeakMutableArrayEnumerator alloc] initWithInternalArray:array] autorelease];
+}
+
+- (NSString*)description {
+ NSMutableString* s = [NSMutableString stringWithCapacity:100];
+ [s appendString:@"Weak array {\n"];
+ for (NSValue* v in array) {
+ [s appendString:[[v nonretainedObjectValue] description]];
+ [s appendString:@" "];
+ [s appendFormat:@"%u",[[v nonretainedObjectValue] retainCount]];
+ [s appendString:@"\n"];
+ }
+ [s appendString:@"}"];
+ return s;
+}
+
++(void)test {
+ /*
+ HJWeakMutableArray* array = [HJWeakMutableArray arrayWithCapacity:4];
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ NSString* s = [[NSString stringWithFormat:@"zero"] retain];
+ NSUInteger i1 = [s retainCount];
+ [array addObject:s];
+ NSUInteger i2 = [s retainCount];
+ [array addObject:[[NSString stringWithFormat:@"one"] retain]];
+ [array addObject:[[NSString stringWithFormat:@"two"] retain]];
+ [array addObject:[NSString stringWithFormat:@"three"]];
+
+ for (id obj in [array objectEnumerator]) {
+ NSLog(@"obj %@ retain count %u",obj,[obj retainCount]);
+ }
+
+ NSEnumerator* e = [array objectEnumerator];
+ id obj = [e nextObject];
+ id all = [e allObjects];
+
+ [pool drain];
+ */
+}
+
+
+@end
+
+
+@implementation HJWeakMutableArrayEnumerator
+
+-(HJWeakMutableArrayEnumerator*)initWithInternalArray:(NSArray*)array {
+ arrayEnum = [array objectEnumerator];
+ [arrayEnum retain];
+ return self;
+}
+
+-(void)dealloc {
+ [arrayEnum release];
+ [super dealloc];
+}
+
+-(id)nextObject {
+ NSValue* val = (NSValue*)[arrayEnum nextObject];
+ return [val nonretainedObjectValue];
+}
+
+-(NSArray*)allObjects {
+ NSMutableArray* all2 = [NSMutableArray arrayWithCapacity:10];
+ for (NSValue* val in [arrayEnum allObjects]) {
+ //the value _does_ get retained here
+ [all2 addObject:[val nonretainedObjectValue]];
+ }
+ return all2;
+}
+
+
+
+@end
+
+
+
diff --git a/Classes/PhotoSource.h b/Classes/PhotoSource.h
new file mode 100644
index 0000000..9aadb93
--- /dev/null
+++ b/Classes/PhotoSource.h
@@ -0,0 +1,44 @@
+#import <Three20/Three20.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+typedef enum {
+ PhotoSourceNormal = 0,
+ PhotoSourceDelayed = 1,
+ PhotoSourceVariableCount = 2,
+ PhotoSourceLoadError = 4,
+} PhotoSourceType;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface PhotoSource : TTURLRequestModel <TTPhotoSource> {
+ PhotoSourceType _type;
+ NSString* _title;
+ NSMutableArray* _photos;
+ NSArray* _tempPhotos;
+ NSTimer* _fakeLoadTimer;
+}
+
+- (id)initWithType:(PhotoSourceType)type title:(NSString*)title
+ photos:(NSArray*)photos photos2:(NSArray*)photos2;
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface Photo : NSObject <TTPhoto> {
+ id<TTPhotoSource> _photoSource;
+ NSString* _thumbURL;
+ NSString* _smallURL;
+ NSString* _URL;
+ CGSize _size;
+ NSInteger _index;
+ NSString* _caption;
+}
+
+- (id)initWithURL:(NSString*)URL smallURL:(NSString*)smallURL size:(CGSize)size;
+
+- (id)initWithURL:(NSString*)URL smallURL:(NSString*)smallURL size:(CGSize)size
+ caption:(NSString*)caption;
+
+@end
diff --git a/Classes/PhotoSource.m b/Classes/PhotoSource.m
new file mode 100644
index 0000000..8601018
--- /dev/null
+++ b/Classes/PhotoSource.m
@@ -0,0 +1,192 @@
+#import "PhotoSource.h"
+
+@implementation PhotoSource
+
+@synthesize title = _title;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// private
+
+- (void)fakeLoadReady {
+ _fakeLoadTimer = nil;
+
+ if (_type & PhotoSourceLoadError) {
+ [_delegates perform: @selector(model:didFailLoadWithError:)
+ withObject: self
+ withObject: nil];
+ } else {
+ NSMutableArray* newPhotos = [NSMutableArray array];
+
+ for (int i = 0; i < _photos.count; ++i) {
+ id<TTPhoto> photo = [_photos objectAtIndex:i];
+ if ((NSNull*)photo != [NSNull null]) {
+ [newPhotos addObject:photo];
+ }
+ }
+
+ [newPhotos addObjectsFromArray:_tempPhotos];
+ TT_RELEASE_SAFELY(_tempPhotos);
+
+ [_photos release];
+ _photos = [newPhotos retain];
+
+ for (int i = 0; i < _photos.count; ++i) {
+ id<TTPhoto> photo = [_photos objectAtIndex:i];
+ if ((NSNull*)photo != [NSNull null]) {
+ photo.photoSource = self;
+ photo.index = i;
+ }
+ }
+
+ [_delegates perform:@selector(modelDidFinishLoad:) withObject:self];
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// NSObject
+
+- (id)initWithType:(PhotoSourceType)type title:(NSString*)title photos:(NSArray*)photos
+ photos2:(NSArray*)photos2 {
+ if (self = [super init]) {
+ _type = type;
+ _title = [title copy];
+ _photos = photos2 ? [photos mutableCopy] : [[NSMutableArray alloc] init];
+ _tempPhotos = photos2 ? [photos2 retain] : [photos retain];
+ _fakeLoadTimer = nil;
+
+ for (int i = 0; i < _photos.count; ++i) {
+ id<TTPhoto> photo = [_photos objectAtIndex:i];
+ if ((NSNull*)photo != [NSNull null]) {
+ photo.photoSource = self;
+ photo.index = i;
+ }
+ }
+
+ if (!(_type & PhotoSourceDelayed || photos2)) {
+ [self performSelector:@selector(fakeLoadReady)];
+ }
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithType:PhotoSourceNormal title:nil photos:nil photos2:nil];
+}
+
+- (void)dealloc {
+ [_fakeLoadTimer invalidate];
+ TT_RELEASE_SAFELY(_photos);
+ TT_RELEASE_SAFELY(_tempPhotos);
+ TT_RELEASE_SAFELY(_title);
+ [super dealloc];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TTModel
+
+- (BOOL)isLoading {
+ return !!_fakeLoadTimer;
+}
+
+- (BOOL)isLoaded {
+ return !!_photos;
+}
+
+- (void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more {
+ if (cachePolicy & TTURLRequestCachePolicyNetwork) {
+ [_delegates perform:@selector(modelDidStartLoad:) withObject:self];
+
+ TT_RELEASE_SAFELY(_photos);
+ _fakeLoadTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self
+ selector:@selector(fakeLoadReady) userInfo:nil repeats:NO];
+ }
+}
+
+- (void)cancel {
+ [_fakeLoadTimer invalidate];
+ _fakeLoadTimer = nil;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TTPhotoSource
+
+- (NSInteger)numberOfPhotos {
+ if (_tempPhotos) {
+ return _photos.count + (_type & PhotoSourceVariableCount ? 0 : _tempPhotos.count);
+ } else {
+ return _photos.count;
+ }
+}
+
+- (NSInteger)maxPhotoIndex {
+ return _photos.count-1;
+}
+
+- (id<TTPhoto>)photoAtIndex:(NSInteger)photoIndex {
+ if (photoIndex < _photos.count) {
+ id photo = [_photos objectAtIndex:photoIndex];
+ if (photo == [NSNull null]) {
+ return nil;
+ } else {
+ return photo;
+ }
+ } else {
+ return nil;
+ }
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation Photo
+
+@synthesize photoSource = _photoSource, size = _size, index = _index, caption = _caption;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// NSObject
+
+- (id)initWithURL:(NSString*)URL smallURL:(NSString*)smallURL size:(CGSize)size {
+ return [self initWithURL:URL smallURL:smallURL size:size caption:nil];
+}
+
+- (id)initWithURL:(NSString*)URL smallURL:(NSString*)smallURL size:(CGSize)size
+ caption:(NSString*)caption {
+ if (self = [super init]) {
+ _photoSource = nil;
+ _URL = [URL copy];
+ _smallURL = [smallURL copy];
+ _thumbURL = [smallURL copy];
+ _size = size;
+ _caption = [caption copy];
+ _index = NSIntegerMax;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ TT_RELEASE_SAFELY(_URL);
+ TT_RELEASE_SAFELY(_smallURL);
+ TT_RELEASE_SAFELY(_thumbURL);
+ TT_RELEASE_SAFELY(_caption);
+ [super dealloc];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TTPhoto
+
+- (NSString*)URLForVersion:(TTPhotoVersion)version {
+ if (version == TTPhotoVersionLarge) {
+ return _URL;
+ } else if (version == TTPhotoVersionMedium) {
+ return _URL;
+ } else if (version == TTPhotoVersionSmall) {
+ return _smallURL;
+ } else if (version == TTPhotoVersionThumbnail) {
+ return _thumbURL;
+ } else {
+ return nil;
+ }
+}
+
+@end
diff --git a/Classes/PhotoViewController.h b/Classes/PhotoViewController.h
new file mode 100644
index 0000000..e1c71ae
--- /dev/null
+++ b/Classes/PhotoViewController.h
@@ -0,0 +1,24 @@
+//
+// PhotoViewController.h
+// acidcow
+//
+// Created by Matthew Handler on 4/18/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import <Three20/Three20.h>
+#import "Topic.h"
+
+@interface PhotoViewController : TTThumbsViewController <NSURLProtocolClient> {
+ NSArray *photoList;
+ NSMutableData *_acidcowPage;
+ Topic *topic;
+}
+
+@property (nonatomic, retain) NSArray *photoList;
+@property (retain) NSMutableData *_acidcowPage;
+@property (retain) Topic *topic;
+
+-(void)convertArray;
+@end
diff --git a/Classes/PhotoViewController.m b/Classes/PhotoViewController.m
new file mode 100644
index 0000000..5999626
--- /dev/null
+++ b/Classes/PhotoViewController.m
@@ -0,0 +1,150 @@
+//
+// PhotoViewController.m
+// acidcow
+//
+// Created by Matthew Handler on 4/18/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "PhotoViewController.h"
+#import "PhotoSource.h"
+
+@implementation PhotoViewController
+
+@synthesize photoList, topic;
+
+- (void)viewDidLoad {
+// [self convertArray];
+ [super viewDidLoad];
+
+ _acidcowPage = [[NSMutableData data] retain];
+
+ [self loadImages];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
+}
+
+- (void)setPhotoList:(NSArray *)list {
+ photoList = list;
+ [self convertArray];
+}
+
+-(void)setTopic:(Topic *)t {
+ topic = t;
+ self.navigationItem.title = topic.title;
+ [self loadImages];
+ //NSLog(@"%@", topic.guid);
+}
+
+- (void)convertArray {
+ NSMutableArray *converted = [[NSMutableArray alloc] initWithCapacity:[photoList count]];
+ for (NSString *url in photoList) {
+ [converted addObject:[[[Photo alloc] initWithURL:url
+ smallURL:url
+ size:CGSizeMake(0, 0)] autorelease]];
+ }
+
+
+ self.photoSource = [[PhotoSource alloc]
+ initWithType:PhotoSourceNormal
+ title:topic.title
+ photos:converted
+ photos2:nil];
+}
+
+#pragma mark -
+#pragma mark URL Connection Stuff
+- (void)loadImages {
+
+ NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:topic.guid]
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:3600.0];
+
+ if ([NSURLConnection connectionWithRequest:theRequest delegate:self]) {
+ [_acidcowPage setLength:0];
+ } else {
+ //[MBProgressHUD hideHUDForView:self.view animated:YES];
+ //[WerdMergeAppDelegate prompt:@"Error" withMessage:@"No internet connection" andButtonTitle:@"shucks" withDelegate:self];
+ }
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+ [_acidcowPage setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [_acidcowPage appendData:data];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+
+ //[_receivedData release];
+
+ //[MBProgressHUD hideHUDForView:self.view animated:YES];
+ //[WerdMergeAppDelegate prompt:@"Error" withMessage:[error localizedDescription] andButtonTitle:@"Aww man..." withDelegate:self];
+ //[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
+
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ NSString *html = [[[NSString alloc] initWithData:_acidcowPage encoding:NSUTF8StringEncoding] autorelease];
+
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"http://acidcow\\.com/pics[^ \"]+\\.(jpg|png|jpeg|gif)"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:html options:0 range:NSMakeRange(0, [html length])];
+ NSMutableArray *strings = [[NSMutableArray alloc] initWithCapacity:[results count]];
+ for (NSTextCheckingResult *obj in results) {
+ [strings addObject:[html substringWithRange:[obj range]]];
+
+ //NSLog(@"found: %@", [html substringWithRange:[obj range]]);
+ }
+
+ //PhotoViewController *photoView = [[[PhotoViewController alloc] init] autorelease];
+ self.photoList = strings;
+ [self convertArray];
+ //[self.navigationController pushViewController:photoView animated:YES];
+ // [self.view addSubview:(UIView *)photoView];
+
+ // if ([results count] > 0) {
+ // NSString *title = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+ // currentTopic.title = title;
+ // }
+
+ //[_tableData removeAllObjects];
+ //[_tableData addObjectsFromArray:[[json JSONValue] objectForKey:@"results"]];
+ //[json release];
+
+ // if ([_tableData count] > 0) {
+ // [tableView reloadData];
+ // [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
+ // self.tableView.allowsSelection = YES;
+ // self.tableView.scrollEnabled = YES;
+ // }
+ // else {
+ // [WerdMergeAppDelegate prompt:@"No Results" withMessage:@"Sorry, that word isn't in our dictionary" andButtonTitle:@"Alright" withDelegate:self];
+ // }
+ //
+ // [MBProgressHUD hideHUDForView:self.view animated:YES];
+
+ //[_receivedData release];
+}
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+ // Drawing code.
+}
+*/
+
+- (void)dealloc {
+ [photoList release];
+ [super dealloc];
+}
+
+
+@end
diff --git a/Classes/PicCast.png b/Classes/PicCast.png
new file mode 100644
index 0000000..4c6ba76
--- /dev/null
+++ b/Classes/PicCast.png
Binary files differ
diff --git a/Classes/PicCastAppDelegate.h b/Classes/PicCastAppDelegate.h
new file mode 100644
index 0000000..a0efe4d
--- /dev/null
+++ b/Classes/PicCastAppDelegate.h
@@ -0,0 +1,21 @@
+//
+// PicCastAppDelegate.h
+// PicCast
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface PicCastAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
+ UIWindow *window;
+ UITabBarController *tabBarController;
+ UINavigationController *navigationController;
+}
+
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
+@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
+
+@end
diff --git a/Classes/PicCastAppDelegate.m b/Classes/PicCastAppDelegate.m
new file mode 100644
index 0000000..0bd0798
--- /dev/null
+++ b/Classes/PicCastAppDelegate.m
@@ -0,0 +1,111 @@
+//
+// PicCastAppDelegate.m
+// PicCast
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "PicCastAppDelegate.h"
+#import <Three20Network/TTURLRequestQueue.h>
+#import <iAd/iAD.h>
+
+
+@implementation PicCastAppDelegate
+
+@synthesize window;
+@synthesize tabBarController;
+@synthesize navigationController;
+
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+ // Override point for customization after application launch.
+
+ // Add the tab bar controller's view to the window and display.
+ [self.window addSubview:tabBarController.view];
+ [self.window makeKeyAndVisible];
+ //navigationController = [[UINavigationController alloc] initWithRootViewController:(UIViewController *)tabBarController];
+ // this was put in because i errored out on some gif files that i guess were really large?
+ [[TTURLRequestQueue mainQueue] setMaxContentLength:0];
+
+ return YES;
+}
+
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ /*
+ Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+ */
+}
+
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ /*
+ Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
+ */
+}
+
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ /*
+ Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
+ */
+}
+
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ /*
+ Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+ */
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ /*
+ Called when the application is about to terminate.
+ See also applicationDidEnterBackground:.
+ */
+}
+
+
+#pragma mark -
+#pragma mark UITabBarControllerDelegate methods
+
+/*
+// Optional UITabBarControllerDelegate method.
+- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
+}
+*/
+
+/*
+// Optional UITabBarControllerDelegate method.
+- (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray *)viewControllers changed:(BOOL)changed {
+}
+*/
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ /*
+ Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
+ */
+}
+
+
+- (void)dealloc {
+ [tabBarController release];
+ [navigationController release];
+ [window release];
+ [super dealloc];
+}
+
+@end
+
diff --git a/Classes/PicDumpViewController.h b/Classes/PicDumpViewController.h
new file mode 100644
index 0000000..4716538
--- /dev/null
+++ b/Classes/PicDumpViewController.h
@@ -0,0 +1,22 @@
+//
+// PicDumpViewController.h
+// acidcow
+//
+// Created by Matthew Handler on 4/16/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "Topic.h"
+//@class Topic;
+
+@interface PicDumpViewController : UIViewController <NSURLProtocolClient> {
+ NSMutableData *_acidcowPage;
+ NSDateFormatter *dateFormatter;
+ Topic *topic;
+}
+
+@property (nonatomic, retain) Topic *topic;
+@property (nonatomic, readonly, retain) NSDateFormatter *dateFormatter;
+
+@end
diff --git a/Classes/PicDumpViewController.m b/Classes/PicDumpViewController.m
new file mode 100644
index 0000000..436534e
--- /dev/null
+++ b/Classes/PicDumpViewController.m
@@ -0,0 +1,273 @@
+//
+// PicDumpViewController.m
+// acidcow
+//
+// Created by Matthew Handler on 4/16/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "PicDumpViewController.h"
+#import "PhotoViewController.h"
+
+@implementation PicDumpViewController
+
+@synthesize topic, dateFormatter;
+
+- (NSDateFormatter *)dateFormatter {
+ if (dateFormatter == nil) {
+ dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
+ }
+ return dateFormatter;
+}
+
+//- (Topic *)topic {
+// NSLog(@"%@", topic.guid);
+// return topic;
+//}
+
+-(void)setTopic:(Topic *)t {
+ topic = t;
+ [self loadImages];
+ NSLog(@"%@", topic.guid);
+}
+
+#pragma mark -
+#pragma mark View lifecycle
+
+- (void)viewDidLoad {
+// UIBarButtonItem *doneItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissModalViewControllerAnimated:)] autorelease];
+// self.navigationItem.rightBarButtonItem = doneItem;
+ [super viewDidLoad];
+
+ _acidcowPage = [[NSMutableData data] retain];
+
+ [self loadImages];
+ // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
+ // self.navigationItem.rightBarButtonItem = self.editButtonItem;
+}
+
+
+- (void)viewWillAppear:(BOOL)animated {
+ self.title = topic.title;
+ self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
+ //[self.tableView reloadData];
+
+ [super viewWillAppear:animated];
+}
+
+
+- (void)viewDidAppear:(BOOL)animated {
+ //[self becomeFirstResponder];
+ [super viewDidAppear:animated];
+}
+
+/*
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+}
+*/
+/*
+- (void)viewDidDisappear:(BOOL)animated {
+ [super viewDidDisappear:animated];
+}
+*/
+/*
+// Override to allow orientations other than the default portrait orientation.
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ // Return YES for supported orientations.
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+*/
+
+#pragma mark -
+#pragma mark URL Connection Stuff
+- (void)loadImages {
+
+ NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:topic.guid]
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:3600.0];
+
+ if ([NSURLConnection connectionWithRequest:theRequest delegate:self]) {
+ [_acidcowPage setLength:0];
+ } else {
+ //[MBProgressHUD hideHUDForView:self.view animated:YES];
+ //[WerdMergeAppDelegate prompt:@"Error" withMessage:@"No internet connection" andButtonTitle:@"shucks" withDelegate:self];
+ }
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+ [_acidcowPage setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [_acidcowPage appendData:data];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+
+ //[_receivedData release];
+
+ //[MBProgressHUD hideHUDForView:self.view animated:YES];
+ //[WerdMergeAppDelegate prompt:@"Error" withMessage:[error localizedDescription] andButtonTitle:@"Aww man..." withDelegate:self];
+ //[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
+
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ NSString *html = [[[NSString alloc] initWithData:_acidcowPage encoding:NSUTF8StringEncoding] autorelease];
+
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"http://acidcow\\.com/pics[^ \"]+\\.(jpg|png|jpeg|gif)"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:html options:0 range:NSMakeRange(0, [html length])];
+ NSMutableArray *strings = [[NSMutableArray alloc] initWithCapacity:[results count]];
+ for (NSTextCheckingResult *obj in results) {
+ [strings addObject:[html substringWithRange:[obj range]]];
+
+ //NSLog(@"found: %@", [html substringWithRange:[obj range]]);
+ }
+
+ PhotoViewController *photoView = [[[PhotoViewController alloc] init] autorelease];
+ photoView.photoList = strings;
+ [self.navigationController pushViewController:photoView animated:YES];
+// [self.view addSubview:(UIView *)photoView];
+
+// if ([results count] > 0) {
+// NSString *title = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+// currentTopic.title = title;
+// }
+
+ //[_tableData removeAllObjects];
+ //[_tableData addObjectsFromArray:[[json JSONValue] objectForKey:@"results"]];
+ //[json release];
+
+// if ([_tableData count] > 0) {
+// [tableView reloadData];
+// [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
+// self.tableView.allowsSelection = YES;
+// self.tableView.scrollEnabled = YES;
+// }
+// else {
+// [WerdMergeAppDelegate prompt:@"No Results" withMessage:@"Sorry, that word isn't in our dictionary" andButtonTitle:@"Alright" withDelegate:self];
+// }
+//
+// [MBProgressHUD hideHUDForView:self.view animated:YES];
+
+ //[_receivedData release];
+}
+
+
+#pragma mark -
+#pragma mark Table view data source
+
+//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+// // Return the number of sections.
+// return <#number of sections#>;
+//}
+//
+//
+//- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+// // Return the number of rows in the section.
+// return <#number of rows in section#>;
+//}
+
+
+// Customize the appearance of table view cells.
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ static NSString *CellIdentifier = @"Cell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ }
+
+ // Configure the cell...
+
+ return cell;
+}
+
+
+/*
+// Override to support conditional editing of the table view.
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the specified item to be editable.
+ return YES;
+}
+*/
+
+
+/*
+// Override to support editing the table view.
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ if (editingStyle == UITableViewCellEditingStyleDelete) {
+ // Delete the row from the data source.
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
+ }
+ else if (editingStyle == UITableViewCellEditingStyleInsert) {
+ // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
+ }
+}
+*/
+
+
+/*
+// Override to support rearranging the table view.
+- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
+}
+*/
+
+
+/*
+// Override to support conditional rearranging of the table view.
+- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the item to be re-orderable.
+ return YES;
+}
+*/
+
+
+#pragma mark -
+#pragma mark Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Navigation logic may go here. Create and push another view controller.
+ /*
+ <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
+ // ...
+ // Pass the selected object to the new view controller.
+ [self.navigationController pushViewController:detailViewController animated:YES];
+ [detailViewController release];
+ */
+}
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Relinquish ownership any cached data, images, etc. that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
+ // For example: self.myOutlet = nil;
+}
+
+
+- (void)dealloc {
+ [_acidcowPage release];
+ [topic release];
+ [dateFormatter release];
+ [super dealloc];
+}
+
+
+@end
+
diff --git a/Classes/PicDumpViewController.xib b/Classes/PicDumpViewController.xib
new file mode 100644
index 0000000..1e45644
--- /dev/null
+++ b/Classes/PicDumpViewController.xib
@@ -0,0 +1,561 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1056</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">823</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="NS.object.0">132</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="10"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBProxyObject" id="372490531">
+ <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBProxyObject" id="975951072">
+ <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUIViewController" id="1061758839">
+ <object class="IBUIScrollView" key="IBUIView" id="245580672">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUILabel" id="582704778">
+ <reference key="NSNextResponder" ref="245580672"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{20, 20}, {280, 53}}</string>
+ <reference key="NSSuperview" ref="245580672"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">7</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <string key="IBUIText">Label</string>
+ <object class="NSColor" key="IBUITextColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MSAxIDEAA</bytes>
+ <object class="NSColorSpace" key="NSCustomColorSpace">
+ <int key="NSID">1</int>
+ </object>
+ </object>
+ <object class="NSColor" key="IBUIHighlightedColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ <int key="IBUIBaselineAdjustment">1</int>
+ <float key="IBUIMinimumFontSize">10</float>
+ </object>
+ <object class="IBUIToolbar" id="441641112">
+ <reference key="NSNextResponder" ref="245580672"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{0, 323}, {320, 44}}</string>
+ <reference key="NSSuperview" ref="245580672"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIBarStyle">2</int>
+ <object class="NSMutableArray" key="IBUIItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUIBarButtonItem" id="63963298">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIStyle">1</int>
+ <reference key="IBUIToolbar" ref="441641112"/>
+ <int key="IBUISystemItemIdentifier">19</int>
+ </object>
+ <object class="IBUIBarButtonItem" id="934585534">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIStyle">1</int>
+ <reference key="IBUIToolbar" ref="441641112"/>
+ <int key="IBUISystemItemIdentifier">8</int>
+ </object>
+ <object class="IBUIBarButtonItem" id="416580111">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIStyle">1</int>
+ <reference key="IBUIToolbar" ref="441641112"/>
+ <int key="IBUISystemItemIdentifier">18</int>
+ </object>
+ <object class="IBUIBarButtonItem" id="756844583">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIStyle">1</int>
+ <reference key="IBUIToolbar" ref="441641112"/>
+ <int key="IBUISystemItemIdentifier">17</int>
+ </object>
+ <object class="IBUIBarButtonItem" id="884405032">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIStyle">1</int>
+ <reference key="IBUIToolbar" ref="441641112"/>
+ <int key="IBUISystemItemIdentifier">20</int>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{320, 367}</string>
+ <reference key="NSSuperview"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">10</int>
+ <object class="NSImage" key="NSImage">
+ <int key="NSImageFlags">549453824</int>
+ <string key="NSSize">{84, 1}</string>
+ <object class="NSMutableArray" key="NSReps">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="0"/>
+ <object class="NSBitmapImageRep">
+ <object class="NSData" key="NSTIFFRepresentation">
+ <bytes key="NS.bytes">TU0AKgAAAVjFzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/
+y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/
+xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/
+xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/
+xczU/8XM1P/FzNL/y9LY/8vS2P/FzNT/xczU/8XM1P/FzNT/xczS/8vS2P/L0tj/xczU/8XM1P/FzNT/
+xczU/8XM0v/L0tj/y9LY/8XM1P/FzNT/xczU/8XM1P/FzNL/y9LY/8vS2P8ADQEAAAMAAAABAFQAAAEB
+AAMAAAABAAEAAAECAAMAAAAEAAAB+gEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES
+AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABAAEAAAEXAAQAAAABAAABUAEcAAMAAAABAAEAAAFS
+AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwAA</bytes>
+ </object>
+ </object>
+ <string key="IBUIColorCocoaTouchKeyPath">groupTableViewBackgroundColor</string>
+ </object>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <bool key="IBUIMultipleTouchEnabled">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUISimulatedTabBarMetrics" key="IBUISimulatedBottomBarMetrics"/>
+ <object class="IBUISimulatedNavigationBarMetrics" key="IBUISimulatedTopBarMetrics">
+ <bool key="IBUIPrompted">NO</bool>
+ </object>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="372490531"/>
+ <reference key="destination" ref="245580672"/>
+ </object>
+ <int key="connectionID">24</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="372490531"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="975951072"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="1061758839"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="245580672"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="245580672"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="441641112"/>
+ <reference ref="582704778"/>
+ </object>
+ <reference key="parent" ref="1061758839"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="441641112"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="416580111"/>
+ <reference ref="884405032"/>
+ <reference ref="756844583"/>
+ <reference ref="934585534"/>
+ <reference ref="63963298"/>
+ </object>
+ <reference key="parent" ref="245580672"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">21</int>
+ <reference key="object" ref="416580111"/>
+ <reference key="parent" ref="441641112"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">20</int>
+ <reference key="object" ref="884405032"/>
+ <reference key="parent" ref="441641112"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="756844583"/>
+ <reference key="parent" ref="441641112"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="934585534"/>
+ <reference key="parent" ref="441641112"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">17</int>
+ <reference key="object" ref="63963298"/>
+ <reference key="parent" ref="441641112"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="582704778"/>
+ <reference key="parent" ref="245580672"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.CustomClassName</string>
+ <string>-2.CustomClassName</string>
+ <string>10.IBEditorWindowLastContentRect</string>
+ <string>10.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>13.IBViewBoundsToFrameTransform</string>
+ <string>14.IBPluginDependency</string>
+ <string>14.IBViewBoundsToFrameTransform</string>
+ <string>16.IBPluginDependency</string>
+ <string>17.IBPluginDependency</string>
+ <string>18.IBPluginDependency</string>
+ <string>19.IBPluginDependency</string>
+ <string>20.IBPluginDependency</string>
+ <string>21.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>PicDumpViewController</string>
+ <string>UIResponder</string>
+ <string>{{0, 365}, {320, 480}}</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAw7aAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABBoAAAwoAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">24</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">PicDumpViewController</string>
+ <string key="superclassName">UIViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Classes/PicDumpViewController.h</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="529766302">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIBarButtonItem</string>
+ <string key="superclassName">UIBarItem</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIBarButtonItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIBarItem</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIBarItem.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UILabel</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UILabel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIResponder</string>
+ <string key="superclassName">NSObject</string>
+ <reference key="sourceIdentifier" ref="529766302"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIScrollView</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIScrollView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UISearchBar</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UISearchDisplayController</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIToolbar</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIToolbar.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <string key="superclassName">UIResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIViewController</string>
+ <string key="superclassName">UIResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+ <integer value="1056" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../PicCast.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <string key="IBCocoaTouchPluginVersion">132</string>
+ </data>
+</archive>
diff --git a/Classes/TabViewController.h b/Classes/TabViewController.h
new file mode 100644
index 0000000..f09abd7
--- /dev/null
+++ b/Classes/TabViewController.h
@@ -0,0 +1,23 @@
+//
+// FirstViewController.h
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "TopicsViewController.h"
+
+
+@interface TabViewController : UIViewController {
+ TopicsViewController *topicsViewController;
+ //NSIndexPath *parserSelection;
+ //UIButton *startButton;
+}
+
+//@property (nonatomic, retain) IBOutlet UIButton *startButton;
+@property (nonatomic, retain, readonly) TopicsViewController *topicsViewController;
+//@property (nonatomic, retain) NSIndexPath *parserSelection;
+
+@end
diff --git a/Classes/TabViewController.m b/Classes/TabViewController.m
new file mode 100644
index 0000000..c4ada83
--- /dev/null
+++ b/Classes/TabViewController.m
@@ -0,0 +1,64 @@
+//
+// FirstViewController.m
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "TabViewController.h"
+
+
+@implementation TabViewController
+
+
+/*
+// The designated initializer. Override to perform setup that is required before the view is loaded.
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+ if (self) {
+ // Custom initialization
+ }
+ return self;
+}
+*/
+
+/*
+// Implement loadView to create a view hierarchy programmatically, without using a nib.
+- (void)loadView {
+}
+*/
+
+/*
+// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
+- (void)viewDidLoad {
+ [super viewDidLoad];
+}
+*/
+
+/*
+// Override to allow orientations other than the default portrait orientation.
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ // Return YES for supported orientations
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+*/
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Release any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+}
+
+
+- (void)dealloc {
+ [super dealloc];
+}
+
+@end
diff --git a/Classes/Topic.h b/Classes/Topic.h
new file mode 100644
index 0000000..2ae6520
--- /dev/null
+++ b/Classes/Topic.h
@@ -0,0 +1,34 @@
+//
+// Topic.h
+// acidcow
+//
+// Created by Matthew Handler on 4/16/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+
+@interface Topic : NSObject {
+
+ NSString *title;
+ NSString *creator;
+ NSString *description;
+ NSDate *releaseDate;
+ NSString *category;
+ NSString *iconUrl;
+ NSNumber *picCount;
+ NSString *guid;
+
+}
+
+@property (nonatomic, copy) NSNumber *picCount;
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *guid;
+@property (nonatomic, copy) NSString *creator;
+@property (nonatomic, copy) NSString *description;
+@property (nonatomic, copy) NSDate *releaseDate;
+@property (nonatomic, copy) NSString *category;
+@property (nonatomic, copy) NSString *iconUrl;
+
+@end
diff --git a/Classes/Topic.m b/Classes/Topic.m
new file mode 100644
index 0000000..712519b
--- /dev/null
+++ b/Classes/Topic.m
@@ -0,0 +1,28 @@
+//
+// Topic.m
+// acidcow
+//
+// Created by Matthew Handler on 4/16/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "Topic.h"
+
+
+@implementation Topic
+
+@synthesize title, guid, creator, iconUrl, description, releaseDate, category, picCount;
+
+- (void)dealloc {
+ [picCount release];
+ [guid release];
+ [title release];
+ [creator release];
+ [description release];
+ [releaseDate release];
+ [category release];
+ [iconUrl release];
+ [super dealloc];
+}
+
+@end
diff --git a/Classes/TopicTableViewCell.h b/Classes/TopicTableViewCell.h
new file mode 100644
index 0000000..583d56c
--- /dev/null
+++ b/Classes/TopicTableViewCell.h
@@ -0,0 +1,32 @@
+//
+// TopicTableViewCell.h
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+
+@interface TopicTableViewCell : UITableViewCell {
+ NSString *title;
+ NSString *iconUrl;
+ NSString *picCount;
+ BOOL useDarkBackground;
+
+ IBOutlet UILabel *titleLabel;
+ IBOutlet UILabel *picCountLabel;
+ IBOutlet UIImageView *icon;
+ IBOutlet UIImageView *gradient;
+}
+@property BOOL useDarkBackground;
+@property (nonatomic, retain) UIImageView *gradient;
+@property (nonatomic, retain) UILabel *titleLabel;
+@property (nonatomic, retain) UIImageView *icon;
+@property (nonatomic, retain) UILabel *picCountLabel;
+@property (retain) NSString *picCount;
+@property (retain) NSString *iconUrl;
+@property (retain) NSString *title;
+
+@end
diff --git a/Classes/TopicTableViewCell.m b/Classes/TopicTableViewCell.m
new file mode 100644
index 0000000..a933c7d
--- /dev/null
+++ b/Classes/TopicTableViewCell.m
@@ -0,0 +1,80 @@
+//
+// TopicTableViewCell.m
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "TopicTableViewCell.h"
+
+
+@implementation TopicTableViewCell
+
+@synthesize useDarkBackground, title, iconUrl, picCount, gradient; // titleLabel, icon
+
+- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+
+ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+ if (self) {
+ // Initialization code.
+ }
+// titleLabel.lineBreakMode = UILineBreakModeWordWrap;
+// titleLabel.numberOfLines = 0;
+ return self;
+}
+
+
+#define DARK_BACKGROUND [UIColor colorWithRed:200.0/255.0 green:240.0/255.0 blue:240.0/255.0 alpha:1.0]
+#define LIGHT_BACKGROUND [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0]
+
+- (void)setUseDarkBackground:(BOOL)flag
+{
+ if (flag != useDarkBackground || !self.backgroundView)
+ {
+ useDarkBackground = flag;
+
+ //NSString *backgroundImagePath = [[NSBundle mainBundle] pathForResource:useDarkBackground ? @"DarkBackground" : @"LightBackground" ofType:@"png"];
+ //UIImage *backgroundImage = [[UIImage imageWithContentsOfFile:backgroundImagePath] stretchableImageWithLeftCapWidth:0.0 topCapHeight:1.0];
+ //self.backgroundView = [[[UIImageView alloc] initWithImage:backgroundImage] autorelease];
+ //self.backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+
+ //self.backgroundColor = LIGHT_BACKGROUND;
+ //[self setBackgroundColor:LIGHT_BACKGROUND];
+ //[self.contentView setBackgroundColor:LIGHT_BACKGROUND];
+ }
+ else {
+ //[self.contentView setBackgroundColor:DARK_BACKGROUND];
+ //[self setBackgroundColor:DARK_BACKGROUND];
+ //self.backgroundColor = DARK_BACKGROUND;
+ }
+ self.backgroundView.frame = self.bounds;
+}
+
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
+ [gradient removeFromSuperview];
+ [super setSelected:selected animated:animated];
+
+ // Configure the view for the selected state.
+}
+
+- (void)setIconUrl:(NSString *)url {
+// NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
+// [icon setImage:[[UIImage alloc] initWithData:imageData]];
+}
+
+- (void)setTitle:(NSString *)string {
+ titleLabel.text = string;
+}
+
+- (void)setPicCount:(NSString *)string {
+ picCountLabel.text = string;
+}
+
+- (void)dealloc {
+ //NSLog(@"powering down");
+ [super dealloc];
+}
+
+
+@end
diff --git a/Classes/TopicTableViewCell.xib b/Classes/TopicTableViewCell.xib
new file mode 100644
index 0000000..a66bd79
--- /dev/null
+++ b/Classes/TopicTableViewCell.xib
@@ -0,0 +1,573 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1056</int>
+ <string key="IBDocument.SystemVersion">10J567</string>
+ <string key="IBDocument.InterfaceBuilderVersion">823</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
+ <string key="IBDocument.HIToolboxVersion">462.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="NS.object.0">132</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBProxyObject" id="841351856">
+ <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBProxyObject" id="371349661">
+ <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUITableViewCell" id="896521461">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">292</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUIView" id="885389068">
+ <reference key="NSNextResponder" ref="896521461"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUIImageView" id="647701623">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrameSize">{320, 100}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">gradient.png</string>
+ </object>
+ </object>
+ <object class="IBUILabel" id="840200400">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{108, 10}, {179, 60}}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">9</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <string key="IBUIText">Title</string>
+ <object class="NSFont" key="IBUIFont">
+ <string key="NSName">Helvetica-Bold</string>
+ <double key="NSSize">22</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <object class="NSColor" key="IBUITextColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MCAwIDAAA</bytes>
+ </object>
+ <object class="NSColor" key="IBUIHighlightedColor" id="350503524">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ <int key="IBUIBaselineAdjustment">1</int>
+ <bool key="IBUIAdjustsFontSizeToFit">NO</bool>
+ <float key="IBUIMinimumFontSize">10</float>
+ <int key="IBUINumberOfLines">0</int>
+ <int key="IBUILineBreakMode">0</int>
+ </object>
+ <object class="IBUIImageView" id="83087555">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrameSize">{100, 100}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <int key="IBUIContentMode">1</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUILabel" id="559717521">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{219, 69}, {51, 21}}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">7</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <string key="IBUIText">n1</string>
+ <object class="NSFont" key="IBUIFont">
+ <string key="NSName">Helvetica-LightOblique</string>
+ <double key="NSSize">14</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <object class="NSColor" key="IBUITextColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MC45MDU4OTk0NjUxIDAuMzM3MTgyMTA0NiAwLjExMzQ2MTkxOTEAA</bytes>
+ </object>
+ <reference key="IBUIHighlightedColor" ref="350503524"/>
+ <int key="IBUIBaselineAdjustment">1</int>
+ <float key="IBUIMinimumFontSize">10</float>
+ <int key="IBUITextAlignment">2</int>
+ </object>
+ <object class="IBUILabel" id="94212658">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{274, 67}, {40, 27}}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">7</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <string key="IBUIText">pics</string>
+ <object class="NSFont" key="IBUIFont">
+ <string key="NSName">Helvetica-LightOblique</string>
+ <double key="NSSize">10</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <object class="NSColor" key="IBUITextColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ <reference key="IBUIHighlightedColor" ref="350503524"/>
+ <int key="IBUIBaselineAdjustment">1</int>
+ <float key="IBUIMinimumFontSize">10</float>
+ </object>
+ <object class="IBUIActivityIndicatorView" id="439279812">
+ <reference key="NSNextResponder" ref="885389068"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{40, 40}, {20, 20}}</string>
+ <reference key="NSSuperview" ref="885389068"/>
+ <bool key="IBUIOpaque">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHidesWhenStopped">NO</bool>
+ <bool key="IBUIAnimating">YES</bool>
+ <int key="IBUIStyle">2</int>
+ </object>
+ </object>
+ <string key="NSFrameSize">{300, 100}</string>
+ <reference key="NSSuperview" ref="896521461"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwAA</bytes>
+ </object>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">4</int>
+ <bool key="IBUIMultipleTouchEnabled">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ </object>
+ <string key="NSFrameSize">{320, 100}</string>
+ <reference key="NSSuperview"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MSAxIDEAA</bytes>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUISeparatorStyle">1</int>
+ <int key="IBUIAccessoryType">1</int>
+ <reference key="IBUIContentView" ref="885389068"/>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">titleLabel</string>
+ <reference key="source" ref="896521461"/>
+ <reference key="destination" ref="840200400"/>
+ </object>
+ <int key="connectionID">8</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">picCountLabel</string>
+ <reference key="source" ref="896521461"/>
+ <reference key="destination" ref="559717521"/>
+ </object>
+ <int key="connectionID">17</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">gradient</string>
+ <reference key="source" ref="896521461"/>
+ <reference key="destination" ref="647701623"/>
+ </object>
+ <int key="connectionID">19</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <reference key="object" ref="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="841351856"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="371349661"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="896521461"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="83087555"/>
+ <reference ref="840200400"/>
+ <reference ref="439279812"/>
+ <reference ref="559717521"/>
+ <reference ref="94212658"/>
+ <reference ref="647701623"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="840200400"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="83087555"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="559717521"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="94212658"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="439279812"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="647701623"/>
+ <reference key="parent" ref="896521461"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-2.CustomClassName</string>
+ <string>11.IBPluginDependency</string>
+ <string>11.IBViewBoundsToFrameTransform</string>
+ <string>13.IBPluginDependency</string>
+ <string>13.IBViewBoundsToFrameTransform</string>
+ <string>16.IBPluginDependency</string>
+ <string>18.IBPluginDependency</string>
+ <string>18.IBViewBoundsToFrameTransform</string>
+ <string>2.CustomClassName</string>
+ <string>2.IBEditorWindowLastContentRect</string>
+ <string>2.IBPluginDependency</string>
+ <string>3.IBPluginDependency</string>
+ <string>3.IBViewBoundsToFrameTransform</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>UIResponder</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABC2AAAwqwAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDFAAAwrYAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform"/>
+ <string>TopicTableViewCell</string>
+ <string>{{460, 755}, {320, 100}}</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABDJAAAwrAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">19</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">TopicTableViewCell</string>
+ <string key="superclassName">UITableViewCell</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>gradient</string>
+ <string>icon</string>
+ <string>picCountLabel</string>
+ <string>titleLabel</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>UIImageView</string>
+ <string>UIImageView</string>
+ <string>UILabel</string>
+ <string>UILabel</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>gradient</string>
+ <string>icon</string>
+ <string>picCountLabel</string>
+ <string>titleLabel</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">gradient</string>
+ <string key="candidateClassName">UIImageView</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">icon</string>
+ <string key="candidateClassName">UIImageView</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">picCountLabel</string>
+ <string key="candidateClassName">UILabel</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">titleLabel</string>
+ <string key="candidateClassName">UILabel</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Classes/TopicTableViewCell.h</string>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="828180742">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIActivityIndicatorView</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIActivityIndicatorView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIImageView</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIImageView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UILabel</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UILabel.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIResponder</string>
+ <string key="superclassName">NSObject</string>
+ <reference key="sourceIdentifier" ref="828180742"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UITableViewCell</string>
+ <string key="superclassName">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITableViewCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">UIView</string>
+ <string key="superclassName">UIResponder</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBFrameworkSource</string>
+ <string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+ <integer value="1056" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+ <integer value="3100" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../PicCast.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <string key="NS.key.0">gradient.png</string>
+ <string key="NS.object.0">{320, 100}</string>
+ </object>
+ <string key="IBCocoaTouchPluginVersion">132</string>
+ </data>
+</archive>
diff --git a/Classes/TopicsViewController.h b/Classes/TopicsViewController.h
new file mode 100644
index 0000000..58acc43
--- /dev/null
+++ b/Classes/TopicsViewController.h
@@ -0,0 +1,33 @@
+//
+// TopicsViewController.h
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "XMLParser.h"
+#import "HJObjManager.h"
+#import "PicDumpViewController.h"
+#import "PhotoViewController.h"
+
+@interface TopicsViewController : UITableViewController <XMLParserDelegate>{
+ UINavigationController *topicsNavigationController;
+ NSMutableArray *topics;
+ PicDumpViewController *picDumpViewController;
+ PhotoViewController *photoViewController;
+ XMLParser *parser;
+ HJObjManager* objMan;
+}
+
+@property (nonatomic, retain, readonly) UINavigationController *topicsNavigationController;
+@property (nonatomic, retain) PhotoViewController *photoViewController;
+@property (nonatomic, retain) NSMutableArray *topics;
+@property (nonatomic, retain, readonly) PicDumpViewController *picDumpViewController;
+@property (nonatomic, retain) XMLParser *parser;
+
+// Called by the ParserChoiceViewController based on the selected parser type.
+- (void)startParsing;
+
+@end
diff --git a/Classes/TopicsViewController.m b/Classes/TopicsViewController.m
new file mode 100644
index 0000000..ab07416
--- /dev/null
+++ b/Classes/TopicsViewController.m
@@ -0,0 +1,380 @@
+//
+// TopicsViewController.m
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "TopicsViewController.h"
+#import "XMLParser.h"
+#import "Topic.h"
+#import "TopicTableViewCell.h"
+#import "AcidCowFeedburnerParser.h"
+#import "PhotoViewController.h"
+//#import "AsyncImageView.h"
+#import "HJObjManager.h"
+#import "HJManagedImageV.h"
+
+
+@implementation TopicsViewController
+
+@synthesize topics, parser;
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ NSLog(@"viewDidLoad");
+ self.tableView.rowHeight = 100;
+ //UIBarButtonItem *doneItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(returnToParserChoices)] autorelease];
+ //self.navigationItem.rightBarButtonItem = doneItem;
+
+ // Create the object manager
+ objMan = [[HJObjManager alloc] initWithLoadingBufferSize:6 memCacheSize:20];
+
+ //if you are using for full screen images, you'll need a smaller memory cache than the defaults,
+ //otherwise the cached images will get you out of memory quickly
+ //objMan = [[HJObjManager alloc] initWithLoadingBufferSize:6 memCacheSize:1];
+
+ // Create a file cache for the object manager to use
+ // A real app might do this durring startup, allowing the object manager and cache to be shared by several screens
+ NSString* cacheDirectory = [NSHomeDirectory() stringByAppendingString:@"/Library/Caches/imgcache/flickr/"] ;
+ HJMOFileCache* fileCache = [[[HJMOFileCache alloc] initWithRootPath:cacheDirectory] autorelease];
+ objMan.fileCache = fileCache;
+
+ // Have the file cache trim itself down to a size & age limit, so it doesn't grow forever
+ fileCache.fileCountLimit = 100;
+ fileCache.fileAgeLimit = 60*60*24*7; //1 week
+ [fileCache trimCacheUsingBackgroundThread];
+
+ [self startParsing];
+}
+
+- (UINavigationController *)topicsNavigationController {
+ if (topicsNavigationController == nil) {
+ topicsNavigationController = [[UINavigationController alloc] initWithRootViewController:self.picDumpViewController];
+ }
+ return topicsNavigationController;
+}
+
+- (PicDumpViewController *)picDumpViewController {
+ if (picDumpViewController == nil) {
+ picDumpViewController = [[PicDumpViewController alloc] initWithNibName:@"PicDumpViewController" bundle:nil];
+ }
+ return picDumpViewController;
+}
+
+- (PhotoViewController *)photoViewController {
+ if (photoViewController == nil) {
+ photoViewController = [[[PhotoViewController alloc] init] autorelease];
+ }
+ return photoViewController;
+}
+
+//- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+//
+// self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+// if (self) {
+// // Initialization code.
+// }
+// return self;
+//}
+
+
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
+
+ [super setSelected:selected animated:animated];
+
+ // Configure the view for the selected state.
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow];
+ if (selectedRowIndexPath != nil) {
+ [self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:NO];
+ }
+}
+
+- (void)startParsing {
+ NSLog(@"startParsing");
+ //self.navigationItem.rightBarButtonItem.enabled = NO;
+ // Reset the title
+ self.title = NSLocalizedString(@"Getting Recent Picdumps...", @"Loading");
+ // Allocate the array for song storage, or empty the results of previous parses
+ if (topics == nil) {
+ self.topics = [NSMutableArray array];
+ } else {
+ [topics removeAllObjects];
+ [self.tableView reloadData];
+ }
+ // Create the parser, set its delegate, and start it.
+ self.parser = [[[AcidCowFeedburnerParser alloc] init] autorelease];
+ parser.delegate = self;
+ [parser start];
+}
+
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return [topics count];
+}
+
+//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+//{
+// return 100;
+//}
+
+- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ static NSString *kCellIdentifier = @"TopicTableViewCell";
+ TopicTableViewCell *cell = (TopicTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
+
+ HJManagedImageV* mi;
+ HJManagedImageV* micon;
+
+ if (cell == nil) {
+ NSArray *tmpCell = [[NSBundle mainBundle] loadNibNamed:@"TopicTableViewCell" owner:self options:nil];
+
+ for (id currentObj in tmpCell) {
+ if ([currentObj isKindOfClass:[TopicTableViewCell class]]) {
+ cell = (TopicTableViewCell *)currentObj;
+ break;
+ }
+ }
+
+ micon = [[[HJManagedImageV alloc] initWithFrame:CGRectMake(105,72,16,16)] autorelease];
+ mi = [[[HJManagedImageV alloc] initWithFrame:CGRectMake(3,3,94,94)] autorelease];
+ mi.tag = 998;
+ mi.tag = 999;
+ [cell addSubview:micon];
+ [cell addSubview:mi];
+
+ }
+ else {
+ //Get a reference to the managed image view that was already in the recycled cell, and clear it
+ micon = (HJManagedImageV*)[cell viewWithTag:998];
+ mi = (HJManagedImageV*)[cell viewWithTag:999];
+ [micon clear];
+ [mi clear];
+ }
+
+ //cell.useDarkBackground = (indexPath.row % 2 == 0);
+ cell.title = [[topics objectAtIndex:indexPath.row] title];
+ cell.picCount = [NSString stringWithFormat:@"%@", [[topics objectAtIndex:indexPath.row] picCount]];
+ //cell.iconUrl = [[topics objectAtIndex:indexPath.row] iconUrl];
+
+// CGRect frame;
+// frame.size.width=94; frame.size.height=94;
+// frame.origin.x=3; frame.origin.y=3;
+// AsyncImageView* asyncImage = [[[AsyncImageView alloc]
+// initWithFrame:frame] autorelease];
+// asyncImage.tag = 999;
+// NSURL* url = [imageDownload
+// thumbnailURLAtIndex:indexPath.row];
+ micon.url = [NSURL URLWithString:@"http://www.acidcow.com/favicon.ico"];
+ mi.url = [NSURL URLWithString:[[topics objectAtIndex:indexPath.row] iconUrl]];
+ mi.squareCropped = true;
+
+ //tell the object manager to manage the managed image view,
+ //this causes the cached image to display, or the image to be loaded, cached, and displayed
+ [objMan manage:mi];
+ [objMan manage:micon];
+
+// [asyncImage loadImageFromURL:url];
+
+// [cell.contentView addSubview:asyncImage];
+
+ return cell;
+}
+
+- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+// self.picDumpViewController.topic = [topics objectAtIndex:indexPath.row];
+ self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"back"
+ style:UIBarButtonItemStyleBordered
+ target:nil
+ action:nil];
+// [self.navigationController pushViewController:picDumpViewController animated:YES];
+//
+ photoViewController = [[[PhotoViewController alloc] init] autorelease];
+ photoViewController.topic = [topics objectAtIndex:indexPath.row];
+ [self.navigationController pushViewController:photoViewController animated:YES];
+
+}
+
+#pragma mark <iTunesRSSParserDelegate> Implementation
+
+- (void)parserDidEndParsingData:(XMLParser *)parser {
+ self.title = [NSString stringWithFormat:NSLocalizedString(@"Recent Picdumps (%d)", @"Recent"), [topics count]];
+ [self.tableView reloadData];
+ //self.navigationItem.rightBarButtonItem.enabled = YES;
+ self.parser = nil;
+}
+
+- (void)parser:(XMLParser *)parser didParseTopics:(NSArray *)parsedTopics {
+ [topics addObjectsFromArray:parsedTopics];
+ // Three scroll view properties are checked to keep the user interface smooth during parse. When new objects are delivered
+ // by the parser, the table view is reloaded to display them. If the table is reloaded while
+ // the user is scrolling, this can result in eratic behavior. dragging, tracking, and decelerating can be checked
+ // for this purpose. When the parser finishes, reloadData will be called in parserDidEndParsingData:, guaranteeing
+ // that all data will ultimately be displayed even if reloadData is not called in this method because of user interaction.
+ if (!self.tableView.dragging && !self.tableView.tracking && !self.tableView.decelerating) {
+ self.title = [NSString stringWithFormat:NSLocalizedString(@"Recent Picdumps (%d)", @"Recent"), [topics count]];
+ [self.tableView reloadData];
+ }
+}
+
+- (void)parser:(XMLParser *)parser didFailWithError:(NSError *)error {
+ // handle errors as appropriate to your application...
+}
+
+#pragma mark -
+#pragma mark View lifecycle
+
+/*
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
+ // self.navigationItem.rightBarButtonItem = self.editButtonItem;
+}
+*/
+
+/*
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+}
+*/
+/*
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+}
+*/
+/*
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+}
+*/
+/*
+- (void)viewDidDisappear:(BOOL)animated {
+ [super viewDidDisappear:animated];
+}
+*/
+/*
+// Override to allow orientations other than the default portrait orientation.
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ // Return YES for supported orientations.
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+*/
+
+
+#pragma mark -
+#pragma mark Table view data source
+
+//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+// // Return the number of sections.
+// return <#number of sections#>;
+//}
+//
+//
+//- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+// // Return the number of rows in the section.
+// return <#number of rows in section#>;
+//}
+
+
+// Customize the appearance of table view cells.
+//- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+//
+// static NSString *CellIdentifier = @"Cell";
+//
+// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+// if (cell == nil) {
+// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+// }
+//
+// // Configure the cell...
+//
+// return cell;
+//}
+
+
+/*
+// Override to support conditional editing of the table view.
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the specified item to be editable.
+ return YES;
+}
+*/
+
+
+/*
+// Override to support editing the table view.
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ if (editingStyle == UITableViewCellEditingStyleDelete) {
+ // Delete the row from the data source.
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
+ }
+ else if (editingStyle == UITableViewCellEditingStyleInsert) {
+ // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
+ }
+}
+*/
+
+
+/*
+// Override to support rearranging the table view.
+- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
+}
+*/
+
+
+/*
+// Override to support conditional rearranging of the table view.
+- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the item to be re-orderable.
+ return YES;
+}
+*/
+
+
+#pragma mark -
+#pragma mark Table view delegate
+
+//- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+// // Navigation logic may go here. Create and push another view controller.
+// /*
+// <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
+// // ...
+// // Pass the selected object to the new view controller.
+// [self.navigationController pushViewController:detailViewController animated:YES];
+// [detailViewController release];
+// */
+//}
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Relinquish ownership any cached data, images, etc. that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
+ // For example: self.myOutlet = nil;
+}
+
+- (void)dealloc {
+ [objMan release];
+ [topics release];
+ //[detailController release];
+ [parser release];
+ [super dealloc];
+}
+
+
+@end
+
diff --git a/Classes/XMLParser.h b/Classes/XMLParser.h
new file mode 100644
index 0000000..d919ba2
--- /dev/null
+++ b/Classes/XMLParser.h
@@ -0,0 +1,59 @@
+//
+// XMLParser.h
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//typedef enum {
+// XMLParserTypeAbstract = -1,
+// XMLParserTypeNSXMLParser = 0,
+// XMLParserTypeLibXMLParser
+//} XMLParserType;
+
+@class XMLParser, Topic;
+
+@protocol XMLParserDelegate <NSObject>
+
+@optional
+// Called by the parser when parsing is finished.
+- (void)parserDidEndParsingData:(XMLParser *)parser;
+// Called by the parser in the case of an error.
+- (void)parser:(XMLParser *)parser didFailWithError:(NSError *)error;
+// Called by the parser when one or more songs have been parsed. This method may be called multiple times.
+- (void)parser:(XMLParser *)parser didParseTopics:(NSArray *)parsedTopics;
+
+@end
+
+@interface XMLParser : NSObject {
+ id <XMLParserDelegate> delegate;
+ NSMutableArray *parsedTopics;
+}
+
+@property (nonatomic, assign) id <XMLParserDelegate> delegate;
+@property (nonatomic, retain) NSMutableArray *parsedTopics;
+
+//+ (NSString *)parserName;
+//+ (XMLParserType)parserType;
+
+- (void)start;
+
+// Subclasses must implement this method. It will be invoked on a secondary thread to keep the application responsive.
+// Although NSURLConnection is inherently asynchronous, the parsing can be quite CPU intensive on the device, so
+// the user interface can be kept responsive by moving that work off the main thread. This does create additional
+// complexity, as any code which interacts with the UI must then do so in a thread-safe manner.
+- (void)downloadAndParse:(NSURL *)url;
+
+// Subclasses should invoke these methods and let the superclass manage communication with the delegate.
+// Each of these methods must be invoked on the main thread.
+- (void)downloadStarted;
+- (void)downloadEnded;
+- (void)parseEnded;
+- (void)parsedTopic:(Topic *)topic;
+- (void)parseError:(NSError *)error;
+//- (void)addToParseDuration:(NSNumber *)duration;
+
+@end
diff --git a/Classes/XMLParser.m b/Classes/XMLParser.m
new file mode 100644
index 0000000..d39939e
--- /dev/null
+++ b/Classes/XMLParser.m
@@ -0,0 +1,81 @@
+//
+// XMLParser.m
+// acidcow
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "XMLParser.h"
+#import "Topic.h"
+
+static NSUInteger kCountForNotification = 10;
+
+@implementation XMLParser
+
+@synthesize delegate, parsedTopics;
+
+
+- (void)start {
+ //self.startTimeReference = [NSDate timeIntervalSinceReferenceDate];
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
+ self.parsedTopics = [NSMutableArray array];
+ NSURL *url = [NSURL URLWithString:@"http://feeds.feedburner.com/acidcow_com?format=xml"];
+ [NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:url];
+}
+
+- (void)dealloc {
+ [parsedTopics release];
+ [super dealloc];
+}
+
+- (void)downloadAndParse:(NSURL *)url {
+ //NSAssert([self isMemberOfClass:[iTunesRSSParser class]] == NO, @"Object is of abstract base class iTunesRSSParser");
+}
+
+- (void)downloadStarted {
+ //NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ //self.downloadStartTimeReference = [NSDate timeIntervalSinceReferenceDate];
+ [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
+}
+
+- (void)downloadEnded {
+ //NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ //NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - self.downloadStartTimeReference;
+ //downloadDuration += duration;
+ [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
+}
+
+- (void)parseEnded {
+ //NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseTopics:)] && [parsedTopics count] > 0) {
+ [self.delegate parser:self didParseTopics:parsedTopics];
+ }
+ [self.parsedTopics removeAllObjects];
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parserDidEndParsingData:)]) {
+ [self.delegate parserDidEndParsingData:self];
+ }
+ //NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - self.startTimeReference;
+ //totalDuration = duration;
+ //WriteStatisticToDatabase([[self class] parserType], downloadDuration, parseDuration, totalDuration);
+}
+
+- (void)parsedTopic:(Topic *)topic {
+ //NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ [self.parsedTopics addObject:topic];
+ if (self.parsedTopics.count > kCountForNotification) {
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseTopics:)]) {
+ [self.delegate parser:self didParseTopics:parsedTopics];
+ }
+ [self.parsedTopics removeAllObjects];
+ }
+}
+
+- (void)parseError:(NSError *)error {
+ //NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didFailWithError:)]) {
+ [self.delegate parser:self didFailWithError:error];
+ }
+}
+
+@end