bzr branch
/browse/iphone/tappity
| Line | Revision | Contents |
| 1 | 46 | /* |
| 2 | File: DomainViewController.m |
|
| 3 | Abstract: View controller for the domain list. |
|
| 4 | This object manages a NSNetServiceBrowser configured to look for Bonjour |
|
| 5 | domains. |
|
| 6 | It has two arrays of NSString objects that are displayed in two sections of a |
|
| 7 | table view. |
|
| 8 | When the service browser reports that it has discovered a domain, that domain |
|
| 9 | is added to the first array. |
|
| 10 | When a domain goes away it is removed from the first array. |
|
| 11 | It allows the user to add/remove their own domains from the second array, which |
|
| 12 | is displayed in the second section of the table. |
|
| 13 | When an item in the table view is selected, the delegate is called with the |
|
| 14 | corresponding domain. |
|
| 15 | ||
| 16 | Version: 2.8 |
|
| 17 | |
|
| 18 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
|
| 19 | Inc. ("Apple") in consideration of your agreement to the following |
|
| 20 | terms, and your use, installation, modification or redistribution of |
|
| 21 | this Apple software constitutes acceptance of these terms. If you do |
|
| 22 | not agree with these terms, please do not use, install, modify or |
|
| 23 | redistribute this Apple software. |
|
| 24 | |
|
| 25 | In consideration of your agreement to abide by the following terms, and |
|
| 26 | subject to these terms, Apple grants you a personal, non-exclusive |
|
| 27 | license, under Apple's copyrights in this original Apple software (the |
|
| 28 | "Apple Software"), to use, reproduce, modify and redistribute the Apple |
|
| 29 | Software, with or without modifications, in source and/or binary forms; |
|
| 30 | provided that if you redistribute the Apple Software in its entirety and |
|
| 31 | without modifications, you must retain this notice and the following |
|
| 32 | text and disclaimers in all such redistributions of the Apple Software. |
|
| 33 | Neither the name, trademarks, service marks or logos of Apple Inc. may |
|
| 34 | be used to endorse or promote products derived from the Apple Software |
|
| 35 | without specific prior written permission from Apple. Except as |
|
| 36 | expressly stated in this notice, no other rights or licenses, express or |
|
| 37 | implied, are granted by Apple herein, including but not limited to any |
|
| 38 | patent rights that may be infringed by your derivative works or by other |
|
| 39 | works in which the Apple Software may be incorporated. |
|
| 40 | |
|
| 41 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
|
| 42 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
|
| 43 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
|
| 44 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
|
| 45 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
| 46 | |
|
| 47 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
|
| 48 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
| 49 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
| 50 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
|
| 51 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
|
| 52 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
|
| 53 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
|
| 54 | POSSIBILITY OF SUCH DAMAGE. |
|
| 55 | |
|
| 56 | Copyright (C) 2009 Apple Inc. All Rights Reserved. |
|
| 57 | |
|
| 58 | */ |
|
| 59 | ||
| 60 | #import "DomainViewController.h" |
|
| 61 | ||
| 62 | #define kProgressIndicatorSize 20.0 |
|
| 63 | ||
| 64 | @interface DomainViewController () |
|
| 65 | @property(nonatomic, assign) BOOL showDisclosureIndicators; |
|
| 66 | @property(nonatomic, retain) NSMutableArray* domains; |
|
| 67 | @property(nonatomic, retain) NSMutableArray* customs; |
|
| 68 | @property(nonatomic, retain) NSString* customTitle; |
|
| 69 | @property(nonatomic, retain) NSString* addDomainTitle; |
|
| 70 | @property(nonatomic, retain) NSNetServiceBrowser* netServiceBrowser; |
|
| 71 | @property(nonatomic, assign) BOOL showCancelButton; |
|
| 72 | ||
| 73 | - (void)addButtons:(BOOL)editing; |
|
| 74 | - (void)addAction:(id)sender; |
|
| 75 | - (void)editAction:(id)sender; |
|
| 76 | @end |
|
| 77 | ||
| 78 | @implementation DomainViewController |
|
| 79 | ||
| 80 | @synthesize delegate = _delegate; |
|
| 81 | @synthesize showDisclosureIndicators = _showDisclosureIndicators; |
|
| 82 | @synthesize domains = _domains; |
|
| 83 | @synthesize customs = _customs; |
|
| 84 | @synthesize customTitle = _customTitle; |
|
| 85 | @synthesize addDomainTitle = _addDomainTitle; |
|
| 86 | @dynamic netServiceBrowser; |
|
| 87 | @synthesize showCancelButton = _showCancelButton; |
|
| 88 | ||
| 89 | // Initialization. BonjourBrowser invokes this during its initialization. |
|
| 90 | - (id)initWithTitle:(NSString*)title showDisclosureIndicators:(BOOL)show customsTitle:(NSString*)customsTitle customs:(NSMutableArray*)customs addDomainTitle:(NSString*)addDomainTitle showCancelButton:(BOOL)showCancelButton { |
|
| 91 | if ((self = [super initWithStyle:UITableViewStylePlain])) { |
|
| 92 | self.title = title; |
|
| 93 | self.domains = [[[NSMutableArray alloc] init] autorelease]; |
|
| 94 | self.showDisclosureIndicators = show; |
|
| 95 | self.customTitle = customsTitle; |
|
| 96 | self.customs = customs ? customs : [NSMutableArray array]; |
|
| 97 | self.addDomainTitle = addDomainTitle; |
|
| 98 | self.showCancelButton = showCancelButton; |
|
| 99 | [self addButtons:self.tableView.editing]; |
|
| 100 | } |
|
| 101 | ||
| 102 | return self; |
|
| 103 | } |
|
| 104 | ||
| 105 | // Stores newBrowser in the _netServiceBrowser instance variable. If _netServiceBrowser has already been set, |
|
| 106 | // this first sends it a -stop message before releasing it. |
|
| 107 | - (void)setNetServiceBrowser:(NSNetServiceBrowser*)newBrowser { |
|
| 108 | [_netServiceBrowser stop]; |
|
| 109 | [newBrowser retain]; |
|
| 110 | [_netServiceBrowser release]; |
|
| 111 | _netServiceBrowser = newBrowser; |
|
| 112 | } |
|
| 113 | ||
| 114 | ||
| 115 | - (NSNetServiceBrowser*)netServiceBrowser { |
|
| 116 | return _netServiceBrowser; |
|
| 117 | } |
|
| 118 | ||
| 119 | ||
| 120 | - (void)addAddButton:(BOOL)right { |
|
| 121 | // add + button as the nav bar's custom right view |
|
| 122 | UIBarButtonItem *addButton = [[UIBarButtonItem alloc] |
|
| 123 | initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addAction:)]; |
|
| 124 | if (right) self.navigationItem.rightBarButtonItem = addButton; |
|
| 125 | else self.navigationItem.leftBarButtonItem = addButton; |
|
| 126 | [addButton release]; |
|
| 127 | } |
|
| 128 | ||
| 129 | - (void)addButtons:(BOOL)editing { |
|
| 130 | if (editing) { |
|
| 131 | // Add the "done" button to the navigation bar |
|
| 132 | UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] |
|
| 133 | initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneAction:)]; |
|
| 134 | |
|
| 135 | self.navigationItem.leftBarButtonItem = doneButton; |
|
| 136 | [doneButton release]; |
|
| 137 | ||
| 138 | [self addAddButton:YES]; |
|
| 139 | } else { |
|
| 140 | if ([self.customs count]) { |
|
| 141 | // Add the "edit" button to the navigation bar |
|
| 142 | UIBarButtonItem *editButton = [[UIBarButtonItem alloc] |
|
| 143 | initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(editAction:)]; |
|
| 144 | |
|
| 145 | self.navigationItem.leftBarButtonItem = editButton; |
|
| 146 | [editButton release]; |
|
| 147 | } else { |
|
| 148 | [self addAddButton:NO]; |
|
| 149 | } |
|
| 150 | |
|
| 151 | if (self.showCancelButton) { |
|
| 152 | // add Cancel button as the nav bar's custom right view |
|
| 153 | UIBarButtonItem *addButton = [[UIBarButtonItem alloc] |
|
| 154 | initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction)]; |
|
| 155 | self.navigationItem.rightBarButtonItem = addButton; |
|
| 156 | [addButton release]; |
|
| 157 | } else { |
|
| 158 | self.navigationItem.rightBarButtonItem = nil; |
|
| 159 | } |
|
| 160 | } |
|
| 161 | } |
|
| 162 | ||
| 163 | - (BOOL)commonSetup { |
|
| 164 | self.netServiceBrowser = [[[NSNetServiceBrowser alloc] init] autorelease]; |
|
| 165 | if(!self.netServiceBrowser) { |
|
| 166 | return NO; |
|
| 167 | } |
|
| 168 | |
|
| 169 | [self.netServiceBrowser setDelegate:self]; |
|
| 170 | return YES; |
|
| 171 | } |
|
| 172 | ||
| 173 | // A cover method to -[NSNetServiceBrowser searchForBrowsableDomains]. |
|
| 174 | - (BOOL)searchForBrowsableDomains { |
|
| 175 | if (![self commonSetup]) return NO; |
|
| 176 | [self.netServiceBrowser searchForBrowsableDomains]; |
|
| 177 | return YES; |
|
| 178 | } |
|
| 179 | ||
| 180 | // A cover method to -[NSNetServiceBrowser searchForRegistrationDomains]. |
|
| 181 | - (BOOL)searchForRegistrationDomains { |
|
| 182 | if (![self commonSetup]) return NO; |
|
| 183 | [self.netServiceBrowser searchForRegistrationDomains]; |
|
| 184 | return YES; |
|
| 185 | } |
|
| 186 | ||
| 187 | ||
| 188 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { |
|
| 189 | return 1 + ([self.customs count] ? 1 : 0); |
|
| 190 | } |
|
| 191 | ||
| 192 | ||
| 193 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
|
| 194 | return [(section ? self.customs : self.domains) count]; |
|
| 195 | } |
|
| 196 | ||
| 197 | ||
| 198 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { |
|
| 199 | return section ? self.customTitle : @"Bonjour"; // Note that "Bonjour" is the proper name of the technology, therefore should not be localized |
|
| 200 | } |
|
| 201 | ||
| 202 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
|
| 203 | UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; |
|
| 204 | if (cell == nil) { |
|
| 205 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease]; |
|
| 206 | } |
|
| 207 | |
|
| 208 | // Set up the text for the cell |
|
| 209 | cell.textLabel.text = [(indexPath.section ? self.customs : self.domains) objectAtIndex:indexPath.row]; |
|
| 210 | cell.textLabel.textColor = [UIColor blackColor]; |
|
| 211 | cell.accessoryType = self.showDisclosureIndicators ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; |
|
| 212 | return cell; |
|
| 213 | } |
|
| 214 | ||
| 215 | ||
| 216 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { |
|
| 217 | return indexPath.section && tableView.editing; |
|
| 218 | } |
|
| 219 | ||
| 220 | ||
| 221 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
|
| 222 | [self.delegate domainViewController:self didSelectDomain:[(indexPath.section ? self.customs : self.domains) objectAtIndex:indexPath.row]]; |
|
| 223 | } |
|
| 224 | ||
| 225 | ||
| 226 | - (void)updateUI { |
|
| 227 | // Sort the domains by name, then modify the selection, as it may have moved |
|
| 228 | [self.domains sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; |
|
| 229 | [self.tableView reloadData]; |
|
| 230 | } |
|
| 231 | ||
| 232 | /* |
|
| 233 | The 'domain' parameter passed to netServiceBrowser:didRemoveDomain:moreComing: and netServiceBrowser:didFindDomain:moreComing: may contain escaped characters. This function unescapes them before they are added to or removed from the list that is displayed to the user. |
|
| 234 | */ |
|
| 235 | - (NSString*) transmogrify:(NSString*)aString { |
|
| 236 | |
|
| 237 | NSString* tmp = [NSString stringWithString:aString]; |
|
| 238 | const char *ostr = [tmp UTF8String]; |
|
| 239 | const char *cstr = ostr; |
|
| 240 | char *ptr = (char*) ostr; |
|
| 241 | |
|
| 242 | while (*cstr) { |
|
| 243 | char c = *cstr++; |
|
| 244 | if (c == '\\') |
|
| 245 | { |
|
| 246 | c = *cstr++; |
|
| 247 | if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) |
|
| 248 | { |
|
| 249 | NSInteger v0 = cstr[-1] - '0'; // then interpret as three-digit decimal |
|
| 250 | NSInteger v1 = cstr[ 0] - '0'; |
|
| 251 | NSInteger v2 = cstr[ 1] - '0'; |
|
| 252 | NSInteger val = v0 * 100 + v1 * 10 + v2; |
|
| 253 | if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it |
|
| 254 | } |
|
| 255 | } |
|
| 256 | *ptr++ = c; |
|
| 257 | } |
|
| 258 | ptr--; |
|
| 259 | *ptr = 0; |
|
| 260 | return [NSString stringWithUTF8String:ostr]; |
|
| 261 | } |
|
| 262 | ||
| 263 | ||
| 264 | - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser didRemoveDomain:(NSString*)domain moreComing:(BOOL)moreComing { |
|
| 265 | [self.domains removeObject:[self transmogrify:domain]]; |
|
| 266 | |
|
| 267 | // moreComing really means that there are no more messages in the queue from the Bonjour daemon, so we should update the UI. |
|
| 268 | // When moreComing is set, we don't update the UI so that it doesn't 'flash'. |
|
| 269 | if (!moreComing) |
|
| 270 | [self updateUI]; |
|
| 271 | } |
|
| 272 | ||
| 273 | ||
| 274 | - (void)netServiceBrowser:(NSNetServiceBrowser*)netServiceBrowser didFindDomain:(NSString*)domain moreComing:(BOOL)moreComing { |
|
| 275 | NSString* tmp = [self transmogrify:domain]; |
|
| 276 | if (![self.domains containsObject:tmp]) [self.domains addObject:tmp]; |
|
| 277 | ||
| 278 | // moreComing really means that there are no more messages in the queue from the Bonjour daemon, so we should update the UI. |
|
| 279 | // When moreComing is set, we don't update the UI so that it doesn't 'flash'. |
|
| 280 | if (!moreComing) |
|
| 281 | [self updateUI]; |
|
| 282 | } |
|
| 283 | ||
| 284 | ||
| 285 | - (void)doneAction:(id)sender { |
|
| 286 | [self.tableView setEditing:NO animated:YES]; |
|
| 287 | [self addButtons:self.tableView.editing]; |
|
| 288 | } |
|
| 289 | ||
| 290 | ||
| 291 | - (void)editAction:(id)sender { |
|
| 292 | [self.tableView setEditing:YES animated:YES]; |
|
| 293 | [self addButtons:self.tableView.editing]; |
|
| 294 | } |
|
| 295 | ||
| 296 | ||
| 297 | - (IBAction)cancelAction { |
|
| 298 | [self.delegate domainViewController:self didSelectDomain:nil]; |
|
| 299 | } |
|
| 300 | ||
| 301 | ||
| 302 | - (void)addAction:(id)sender { |
|
| 303 | SimpleEditViewController* sevc = [[SimpleEditViewController alloc] initWithTitle:self.addDomainTitle currentText:nil]; |
|
| 304 | [sevc setDelegate:self]; |
|
| 305 | UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:sevc]; |
|
| 306 | [sevc release]; |
|
| 307 | [self.navigationController presentModalViewController:nc animated:YES]; |
|
| 308 | [nc release]; |
|
| 309 | } |
|
| 310 | ||
| 311 | ||
| 312 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { |
|
| 313 | assert(editingStyle == UITableViewCellEditingStyleDelete); |
|
| 314 | assert(indexPath.section == 1); |
|
| 315 | [self.customs removeObjectAtIndex:indexPath.row]; |
|
| 316 | if (![self.customs count]) { |
|
| 317 | [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationRight]; |
|
| 318 | } else { |
|
| 319 | [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; |
|
| 320 | } |
|
| 321 | [self addButtons:self.tableView.editing]; |
|
| 322 | } |
|
| 323 | ||
| 324 | ||
| 325 | - (void) simpleEditViewController:(SimpleEditViewController*)sevc didGetText:(NSString*)text { |
|
| 326 | [self.navigationController dismissModalViewControllerAnimated:YES]; |
|
| 327 | ||
| 328 | if (![text length]) |
|
| 329 | return; |
|
| 330 | |
|
| 331 | if (![self.customs containsObject:text]) { |
|
| 332 | [self.customs addObject:text]; |
|
| 333 | [self.customs sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; |
|
| 334 | } |
|
| 335 | |
|
| 336 | [self addButtons:self.tableView.editing]; |
|
| 337 | [self.tableView reloadData]; |
|
| 338 | NSUInteger ints[2] = {1,[self.customs indexOfObject:text]}; |
|
| 339 | NSIndexPath* indexPath = [NSIndexPath indexPathWithIndexes:ints length:2]; |
|
| 340 | [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES]; |
|
| 341 | } |
|
| 342 | ||
| 343 | ||
| 344 | - (void)dealloc { |
|
| 345 | [_domains release]; |
|
| 346 | [_customs release]; |
|
| 347 | [_customTitle release]; |
|
| 348 | [_addDomainTitle release]; |
|
| 349 | [_netServiceBrowser release]; |
|
| 350 | |
|
| 351 | [super dealloc]; |
|
| 352 | } |
|
| 353 | ||
| 354 | @end |
Loggerhead 1.17 is a web-based interface for Bazaar branches