//
// ServerTableController.m
// p4scout
//
// Created by Work on 8/30/08.
// Copyright 2008 Perforce Software, Inc. All rights reserved.
//
#import "ServerTableController.h"
#import "ServerDetailViewController.h"
#import "P4ConnectionEditor.h"
#import "P4Server.h"
#import "ServerTableCell.h"
#import "p4scoutAppDelegate.h"
#import "UIToolbarAdditions.h"
#import "P4DefaultsKeys.h"
#define CellActivityViewTag 1
// Uncomment this to update the suggested label dynamically when entering
// server information in the form.
//#define SHOW_SUGGESTED_LABEL
static const int ActivityButtonBarItemPosition = 2;
enum eTableSections {
SavedServers = 0
, FoundServers
, NumberOfSections
};
@interface ServerTableController ()
-(void)initMemberVariables;
-(UITableViewCell*)visibleCellForServer:(P4Server*)server;
-(void)configureCell:(UITableViewCell *)cell withServer:(P4Server *)server;
-(void)updateRefreshButton;
-(int)reachableServerCount;
-(void)addActiveServer:(P4Server*)server;
-(void)removeActiveServer:(P4Server*)server;
-(void)stopObservingServer:(P4Server*)server;
-(void)startObservingServer:(P4Server*)server;
@end
@implementation ServerTableController
@synthesize servers=_servers, tableView=_tableView, browsing;
@synthesize refreshButton;
-(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
if (!(self = [super initWithNibName:nibName bundle:nibBundle]))
return self;
[self initMemberVariables];
return self;
}
- (id)initWithCoder:(NSCoder *)decoder
{
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
if (!(self = [super initWithCoder:decoder]))
return self;
[self initMemberVariables];
return self;
}
-(void)initMemberVariables
{
_servers = [[NSMutableArray arrayWithCapacity:1] retain];
foundServers = [[NSMutableArray arrayWithCapacity:1] retain];
serverBrowser = [[NSNetServiceBrowser alloc] init];
serverBrowser.delegate = self;
[serverBrowser scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)dealloc {
for ( P4Server * server in _servers )
{
[self stopObservingServer:server];
}
for ( P4Server * server in foundServers )
{
[self stopObservingServer:server];
}
[_servers release];
[foundServers release];
[_tableView release];
[serverBrowser removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[serverBrowser release];
[super dealloc];
}
// Implement viewDidLoad to do additional setup after loading the view.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidFinishLaunching:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self forKeyPath:kServerDiscoveryPreferenceKey options:NSKeyValueObservingOptionNew context:nil];
[defaults addObserver:self forKeyPath:kDefaultUserPreferenceKey options:NSKeyValueObservingOptionNew context:nil];
[super viewDidLoad];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidFinishLaunchingNotification
object:nil];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObserver:self forKeyPath:kServerDiscoveryPreferenceKey];
[defaults removeObserver:self forKeyPath:kDefaultUserPreferenceKey];
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.browsing = [defaults boolForKey:kServerDiscoveryPreferenceKey];
}
#pragma mark NSNetService methods
-(void)netService:(NSNetService *)netService didNotResolve:(NSDictionary *)errorDict
{
for (P4Server * server in foundServers)
{
if (![server.label isEqualToString:netService.name])
continue;
[self stopObservingServer:server];
[foundServers removeObject:server];
[_tableView reloadData];
break;
}
[netService stop];
[netService removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[netService release];
}
-(void)netServiceDidResolveAddress:(NSNetService *)netService
{
for (P4Server * server in foundServers)
{
if (![server.label isEqualToString:netService.name])
continue;
server.p4port = [NSString stringWithFormat:@"%@:%d", netService.hostName, netService.port];
[server refresh:self];
break;
}
[netService stop];
[netService removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[netService release];
}
-(void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
didFindService:(NSNetService *)netService
moreComing:(BOOL)moreServicesComing
{
P4Server * server = [[[P4Server alloc] initAsDiscovered:YES] autorelease];
server.label = netService.name;
// we'll set the p4port value after we've resolved the address to avoid further
// lookups by the client API
// server.p4port = netService.name;
[netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
netService.delegate = self;
[netService retain];
[netService resolveWithTimeout:60];
[self startObservingServer:server];
[foundServers addObject:server];
if (!moreServicesComing )
{
[_tableView reloadData];
[self updateRefreshButton];
}
}
-(void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
didRemoveService:(NSNetService *)netService
moreComing:(BOOL)moreServicesComing
{
for (P4Server * server in foundServers)
{
if ([server.label isEqualToString:netService.name])
{
[self stopObservingServer:server];
[foundServers removeObject:server];
break;
}
}
if ( !moreServicesComing )
{
[_tableView reloadData];
[self updateRefreshButton];
}
}
-(NSArray*)serverListForSection:(int)section
{
switch (section) {
case SavedServers:
return _servers;
break;
case FoundServers:
return foundServers;
break;
default:
NSAssert(false, @"Should never hit default condition!");
return 0;
break;
}
}
-(P4Server*)serverForIndexPath:(NSIndexPath*)indexPath
{
NSArray * serverList = [self serverListForSection:indexPath.section];
P4Server * s = [serverList objectAtIndex:indexPath.row];
return s;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ( foundServers.count )
return NumberOfSections;
return 1;
}
-(void)startObservingServer:(P4Server*)server
{
[server addObserver:self forKeyPath:InfoPropertyKey options:NSKeyValueObservingOptionNew context:NULL];
[server addObserver:self forKeyPath:ConnectionStatePropertyKey options:NSKeyValueObservingOptionNew context:NULL];
[server addObserver:self forKeyPath:ActivePropertyKey options:NSKeyValueObservingOptionNew context:NULL];
[server addObserver:self forKeyPath:ReachablePropertyKey options:NSKeyValueObservingOptionNew context:NULL];
[server addObserver:self forKeyPath:LabelPropertyKey options:NSKeyValueObservingOptionNew context:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(serverErrorReceived:) name:P4ServerErrorReceivedNotification object:server];
}
-(void)stopObservingServer:(P4Server*)s
{
[s removeObserver:self forKeyPath:ConnectionStatePropertyKey];
[s removeObserver:self forKeyPath:InfoPropertyKey];
[s removeObserver:self forKeyPath:ActivePropertyKey];
[s removeObserver:self forKeyPath:ReachablePropertyKey];
[s removeObserver:self forKeyPath:LabelPropertyKey];
[[NSNotificationCenter defaultCenter] removeObserver:self name:P4ServerErrorReceivedNotification object:s];
}
-(void)serverErrorReceived:(NSNotification*)errorNotification
{
UITableViewCell *cell = [self visibleCellForServer:[errorNotification object]];
if ( cell )
[self configureCell:cell withServer:[errorNotification object]];
}
-(void)removeServer:(P4Server *)s
{
if ( s == nil )
return;
if ( [_servers containsObject:s] || [foundServers containsObject:s] )
{
[self stopObservingServer:s];
if ( s.active )
[self removeActiveServer:s];
if ( [_servers containsObject:s] )
{
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:[_servers indexOfObject:s] inSection:SavedServers];
[_servers removeObject:s];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationRight];
}
else if ( [foundServers containsObject:s] )
{
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:[foundServers indexOfObject:s] inSection:FoundServers];
[foundServers removeObject:s];
if ( [foundServers count] == 0 )
{
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:FoundServers] withRowAnimation:UITableViewRowAnimationNone];
}
else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
}
[self updateRefreshButton];
}
}
-(void)addServer:(P4Server *)s
{
if ( s == nil )
return;
if ( [_servers containsObject:s] )
return;
if ( s.active )
[self addActiveServer:s];
[self startObservingServer:s];
[_servers addObject:s];
[self.tableView reloadData];
[self updateRefreshButton];
}
-(void)setServers:(NSArray *)s
{
if ( s == _servers )
return;
for ( P4Server * server in _servers )
{
[self stopObservingServer:server];
if ( server.active )
[self removeActiveServer:server];
}
[_servers release]; _servers = nil;
if (!s)
return;
_servers = [[NSMutableArray arrayWithArray:s] retain];
for ( P4Server * server in _servers )
{
[self startObservingServer:server];
if ( server.active )
[self addActiveServer:server];
}
[self.tableView reloadData];
[self updateRefreshButton];
}
-(UITableViewCell*)visibleCellForServer:(P4Server*)server
{
NSArray *visibleCells = self.tableView.visibleCells;
NSArray *indexPaths = self.tableView.indexPathsForVisibleRows;
if ([visibleCells count] == 0)
return nil;
if ([indexPaths count] == 0)
return nil;
int i = 0;
for (UITableViewCell *cell in visibleCells) {
NSIndexPath * indexPath = [indexPaths objectAtIndex:i++];
P4Server * searchedServer = [self serverForIndexPath:indexPath];
if ( searchedServer == server)
{
return cell;
}
}
return nil;
}
-(NSString *)nameForServer:(P4Server *)server
{
NSString * name = server.label;
#ifdef SHOW_SUGGESTED_LABEL
if ( name == nil && server.connectionState == Online )
name = [server.info objectForKey:@"serverName"];
if (name == nil && server.connectionState == Online)
name = [server.info objectForKey:@"serverAddress"];
#endif
if (name == nil)
name = server.p4port;
if (name == nil)
name = @"perforce:1666";
return name;
}
-(void)configureCell:(UITableViewCell *)c withServer:(P4Server *)s
{
ServerTableCell * cell = (ServerTableCell*)c;
UIActivityIndicatorView * activityView = cell.activityView;
UIImageView * imageView = cell.imageView;
UILabel * serverLabel = cell.serverLabel;
serverLabel.text = [self nameForServer:s];
if ( activityView )
{
if ( s.active )
[activityView startAnimating];
else
[activityView stopAnimating];
}
switch ( s.connectionState )
{
case Connecting:
case Online:
imageView.image = [UIImage imageNamed:@"StatusOnline.png"];
break;
case Offline:
imageView.image = [UIImage imageNamed:@"StatusOffline.png"];
break;
case Idle:
break;
default:
NSAssert(NO, @"Unrecognized connectionState");
break;
}
if (s.hasAlert )
imageView.image = [UIImage imageNamed:@"StatusAlert.png"];
if (!s.reachable )
{
imageView.image = [UIImage imageNamed:@"StatusUnreachable.png"];
}
}
-(void)addActiveServer:(P4Server*)server
{
numberOfActiveServers++;
if ( numberOfActiveServers == 1 )
{
[toolbar insertItem:activityIndicatorViewItem inItemsAtIndex:ActivityButtonBarItemPosition animated:NO];
[activityIndicatorView startAnimating];
statusLabel.textAlignment = UITextAlignmentLeft;
statusLabel.text = @"Updating...";
}
}
-(void)removeActiveServer:(P4Server*)server
{
numberOfActiveServers--;
if ( numberOfActiveServers == 0 )
{
[activityIndicatorView stopAnimating];
[toolbar removeItemFromItemsAtIndex:ActivityButtonBarItemPosition animated:NO];
statusLabel.textAlignment = UITextAlignmentCenter;
NSDateFormatter * dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
statusLabel.text = [NSString stringWithFormat:@"Last update: %@",
[dateFormatter stringFromDate:[NSDate date]]];
}
}
-(void)setBrowsing:(BOOL)b
{
if ( browsing == b )
return;
browsing = b;
if ( browsing )
{
[serverBrowser searchForServicesOfType:@"_p4._tcp" inDomain:@"local"];
}
else
{
[serverBrowser stop];
NSArray * array = [NSArray arrayWithArray:foundServers];
for ( P4Server * s in array )
[self removeServer:s];
[_tableView reloadData];
}
}
-(void)observeValueForKeyPath:(NSString *)key
ofObject:(id)object
change:(NSDictionary *)dict
context:(void *)context
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ( object == defaults )
{
if ( [key isEqualToString:kServerDiscoveryPreferenceKey] )
{
self.browsing = [defaults boolForKey:kServerDiscoveryPreferenceKey];
}
else if ( [key isEqualToString:kDefaultUserPreferenceKey] )
{
[self refresh:self];
}
}
if ( [key isEqualToString:ActivePropertyKey] )
{
BOOL isActive = [[dict objectForKey:NSKeyValueChangeNewKey] boolValue];
if ( isActive )
[self addActiveServer:object];
else
[self removeActiveServer:object];
}
if ( [key isEqualToString:ReachablePropertyKey] )
{
[self updateRefreshButton];
}
UITableViewCell *cell = [self visibleCellForServer:object];
if ( cell )
[self configureCell:cell withServer:object];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self serverListForSection:section] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ( foundServers.count == 0 )
return nil;
switch (section) {
case SavedServers:
return @"Saved";
break;
case FoundServers:
return @"Discovered";
break;
default:
NSAssert( false, @"Should never hit default condition" );
return nil;
break;
};
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSAssert ( indexPath.section < NumberOfSections, @"More sections than defined" );
static NSString *CellIdentifier = @"ServerTableCell";
ServerTableCell *cell = (ServerTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray * a = [[NSBundle mainBundle] loadNibNamed:@"ServerTableViewCell" owner:self options:nil];
cell = (ServerTableCell *)[a lastObject];
}
if (cell == nil) {
CGRect frame = CGRectMake(0, 0, 300, 44);
cell = [[[UITableViewCell alloc] initWithFrame:frame
reuseIdentifier:CellIdentifier] autorelease];
cell.imageView.image = [UIImage imageNamed:@"StatusUnreachable.png"];
cell.showsReorderControl = true;
UIActivityIndicatorView * av = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
av.tag = CellActivityViewTag;
av.hidesWhenStopped;
[cell.contentView addSubview:av];
av.hidden = true;
[av release];
}
// Configure the cell
P4Server * s = [self serverForIndexPath:indexPath];
[self configureCell:cell withServer:s];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (self.editing)
return;
ServerDetailViewController * detail = [[ServerDetailViewController alloc] initWithNibName:@"ServerDetail" bundle:nil];
detail.server = [self serverForIndexPath:indexPath];
[self.navigationController pushViewController:detail animated:YES];
[detail release];
}
- (BOOL)tableView:(UITableView*)tableView
canEditRowAtIndexPath:(NSIndexPath*)indexPath
{
return ( indexPath.section == SavedServers );
}
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( editingStyle != UITableViewCellEditingStyleDelete )
return;
if ( indexPath.section != SavedServers )
return;
[self removeServer:[_servers objectAtIndex:indexPath.row]];
}
/*
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
*/
/*
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
*/
/*
- (void)viewWillDisappear:(BOOL)animated {
}
*/
/*
- (void)viewDidDisappear:(BOOL)animated {
}
*/
/*
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
*/
#pragma mark -
#pragma mark actions
-(IBAction)cancel:(id)sender
{
[self.navigationController dismissModalViewControllerAnimated:YES];
[self setEditing:NO animated:YES];
}
- (IBAction)save:(id)sender
{
[self.navigationController dismissModalViewControllerAnimated:YES];
if (connectionEditorController.isNewServer)
{
[connectionEditorController.server refresh:self];
[self addServer:connectionEditorController.server];
}
// Dismiss the modal view to return to the main list
[self setEditing:NO animated:YES];
}
-(int)reachableServerCount
{
int serverCount = 0;
for ( P4Server * s in _servers )
if (s.reachable) serverCount++;
for ( P4Server * s in foundServers )
if (s.reachable) serverCount++;
return serverCount;
}
-(void)updateRefreshButton
{
self.refreshButton.enabled = ([self reachableServerCount] > 0);
}
-(IBAction)refresh:(id)sender
{
for ( P4Server * s in _servers )
[s refresh:self];
for ( P4Server * s in foundServers )
[s refresh:self];
}
-(IBAction)add:(id)sender
{
connectionEditorController.server = [[[P4Server alloc] init] autorelease];
connectionEditorController.title = @"New Server";
connectionEditorController.isNewServer = true;
[self.navigationController presentModalViewController:serverAddDialogController animated:YES];
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
8980 |
Matt Attaway |
Add source code for both the Android and iOS versions of P4Scout. |
|
|