3 * Copyright (c) 2016 Apple Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #import "CNDomainBrowserViewController.h"
19 #import "_CNDomainBrowser.h"
20 #import "CNDomainBrowserPathUtils.h"
21 #import <AssertMacros.h>
23 #define LocalizedStringFromTableInMyBundle(key, table, comment) \
24 NSLocalizedStringFromTableInBundle(key, table, [NSBundle bundleForClass: [self class]], comment)
27 @interface CNTableViewController()
28 @property (copy) NSArray * pathArray;
29 @property (assign) NSInteger selectionIndex;
32 @implementation CNTableViewController
34 - (instancetype)initWithStyle:(UITableViewStyle)style
36 if (self = [super initWithStyle: style])
38 _selectionIndex = NSNotFound;
39 _pathArray = [NSArray array];
46 @interface CNDomainBrowserViewController ()
48 @property _CNDomainBrowser * bonjour;
50 @property (strong) NSMutableDictionary * instanceInfoStrings;
51 @property (strong) NSMutableDictionary * instanceStatusViews;
55 @implementation CNDomainBrowserViewController
57 - (instancetype)initWithStyle:(UITableViewStyle)style
59 if (self = [super initWithStyle: style])
66 - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
68 if (self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil])
75 - (nullable instancetype)initWithCoder:(NSCoder *)coder
77 if (self = [super initWithCoder: coder])
86 self.bonjour = [[_CNDomainBrowser alloc] initWithDelegate: (id<_CNDomainBrowserDelegate>)self];
87 self.bonjour.browseRegistration = _browseRegistration;
88 self.bonjour.ignoreLocal = _ignoreLocal;
89 self.instanceInfoStrings = [NSMutableDictionary dictionary];
90 self.instanceStatusViews = [NSMutableDictionary dictionary];
93 - (void)viewWillAppear:(BOOL)animated
95 [super viewWillAppear: animated];
96 NSArray * pathArray = DNSDomainToDomainPath(_selectedDNSDomain);
97 if ((self.isMovingToParentViewController || self.isBeingPresented) && (pathArray.count > 1))
99 [self updateUIToDomainPathArray: pathArray];
107 self.tableView.allowsMultipleSelectionDuringEditing = NO;
108 self.tableView.dataSource = (id<UITableViewDataSource>)self;
109 self.tableView.delegate = (id<UITableViewDelegate>)self;
111 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: LocalizedStringFromTableInMyBundle(@"_dnsBrowser.browsedomains.cancel", @"DomainBrowser", nil)
112 style: UIBarButtonItemStylePlain
114 action: @selector(cancelAction:)];
118 - (void)updateUIToDomainPathArray:(NSArray *)newPathArray
120 if (newPathArray.count > 1)
122 CNTableViewController * controller = nil;
123 NSMutableArray * newSubPathArray = [NSMutableArray array];
124 for (NSString * nextPathComponent in newPathArray)
126 BOOL animate = NO;//(newPathArray.count == ++count);
127 controller = [self pushNewBrowseController: newSubPathArray.lastObject animated: animate];
128 controller.pathArray = newSubPathArray;
129 [newSubPathArray addObject: nextPathComponent];
134 - (IBAction)cancelAction:(id)sender
136 (void)sender; // Unused
137 [self.navigationController dismissViewControllerAnimated: YES completion: nil];
140 - (CNTableViewController *)pushNewBrowseController:(NSString *)title animated:(BOOL)animated
142 CNTableViewController *tvc = [[CNTableViewController alloc] initWithStyle: self.tableView.style];
144 tvc.clearsSelectionOnViewWillAppear = NO;
145 tvc.tableView.dataSource = (id<UITableViewDataSource>)self;
146 tvc.tableView.delegate = (id<UITableViewDelegate>)self;
147 [self.navigationController pushViewController: tvc animated: animated];
151 - (CNTableViewController *)controllerForTableView:(UITableView *)tableView
153 CNTableViewController * result = nil;
155 for (CNTableViewController * next in self.navigationController.viewControllers)
157 if ([next isKindOfClass: [CNTableViewController class]] && next.tableView == tableView)
167 - (NSArray *)selectedPathArrayForTableView:(UITableView *)tableView includeSelectedRow:(BOOL)includeSelection
169 NSMutableArray * pathArray = [NSMutableArray array];
170 CNTableViewController * controller = [self controllerForTableView: tableView];
172 [pathArray addObjectsFromArray: controller.pathArray];
174 if (includeSelection && controller.selectionIndex != NSNotFound)
176 NSArray * rowArray = [[self.bonjour subDomainsAtDomainPath: pathArray] sortedArrayUsingComparator: ^(id obj1, id obj2) {
177 return (NSComparisonResult)[ obj1[_CNSubDomainKey_subPath] compare: obj2[_CNSubDomainKey_subPath]];
179 if (controller.selectionIndex < (NSInteger)rowArray.count)
181 [pathArray addObject: rowArray[controller.selectionIndex][_CNSubDomainKey_subPath]];
188 - (void)plusButtonPressed:(UIControl *)button withEvent:(UIEvent *)event
190 UITableView * tableView = ((UITableViewController *)self.navigationController.topViewController).tableView;
191 NSIndexPath * indexPath = [tableView indexPathForRowAtPoint: [[[event touchesForView: button] anyObject] locationInView: tableView]];
192 if (indexPath != nil && self.delegate)
194 [self controllerForTableView: tableView].selectionIndex = indexPath.row;
195 NSArray * pathArray = [self selectedPathArrayForTableView: ((CNTableViewController *)self.navigationController.topViewController).tableView includeSelectedRow: YES];
196 _selectedDNSDomain = DomainPathToDNSDomain(pathArray);
197 [self.navigationController dismissViewControllerAnimated: YES completion: ^{
198 if ([self.delegate respondsToSelector: @selector(domainBrowserDomainSelected:)])
200 [self.delegate domainBrowserDomainSelected: _selectedDNSDomain];
206 #pragma mark - Public Methods
208 - (void)setIgnoreLocal:(BOOL)ignoreLocal
210 _ignoreLocal = ignoreLocal;
211 self.bonjour.ignoreLocal = _ignoreLocal;
214 - (void)setBrowseRegistration:(BOOL)browseRegistration
216 _browseRegistration = browseRegistration;
217 self.bonjour.browseRegistration = _browseRegistration;
220 - (NSString *)defaultDNSDomain
222 return(DomainPathToDNSDomain(self.bonjour.defaultDomainPath));
225 - (NSArray *)flattenedDNSDomains
227 return(self.bonjour.flattenedDNSDomains);
232 [self.bonjour startBrowser];
237 [self.bonjour stopBrowser];
242 return(self.bonjour.isBrowsing);
245 #pragma mark - TableView Delegates
247 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
249 (void)tableView; // Unused
250 (void)indexPath; // Unused
251 return UITableViewAutomaticDimension;
254 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
256 (void)tableView; // Unused
260 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
262 (void)section; // Unused
263 return([self.bonjour subDomainsAtDomainPath: [self selectedPathArrayForTableView: tableView includeSelectedRow: NO]].count);
266 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
268 UITableViewCell *cell = nil;
270 static NSString *MyIdentifier = @"browse_cell_id";
271 cell = [tableView dequeueReusableCellWithIdentifier: MyIdentifier];
274 cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: MyIdentifier];
278 NSMutableArray * pathArray = [NSMutableArray arrayWithArray: [self selectedPathArrayForTableView: tableView includeSelectedRow: NO]];
279 NSArray * rowArray = [[self.bonjour subDomainsAtDomainPath: pathArray] sortedArrayUsingComparator: ^(id obj1, id obj2) {
280 return (NSComparisonResult)[ obj1[_CNSubDomainKey_subPath] compare: obj2[_CNSubDomainKey_subPath]];
282 if (indexPath.row < (NSInteger)rowArray.count)
284 NSDictionary * item = [rowArray objectAtIndex: indexPath.row];
285 NSString *val = item[_CNSubDomainKey_subPath];
286 cell.textLabel.text = val;
289 BOOL selected = ([self controllerForTableView: tableView].selectionIndex == indexPath.row);
290 if (selected) [tableView selectRowAtIndexPath: indexPath animated: NO scrollPosition: UITableViewScrollPositionNone];
292 // Make Default domain bold
293 if ([item[_CNSubDomainKey_defaultFlag] boolValue]) cell.textLabel.font = [UIFont boldSystemFontOfSize: [UIFont labelFontSize]];
294 else cell.textLabel.font = nil;
296 // See if it's a leaf
297 [pathArray addObject: val];
298 cell.accessoryType = [self.bonjour subDomainsAtDomainPath: pathArray].count ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
300 // Add the "+" button
301 UIButton * plus = [UIButton buttonWithType: UIButtonTypeContactAdd];
302 [plus addTarget: self action: @selector(plusButtonPressed:withEvent:) forControlEvents: UIControlEventTouchUpInside];
303 plus.userInteractionEnabled = YES;
304 [cell.contentView addSubview: plus];
306 plus.translatesAutoresizingMaskIntoConstraints = NO;
307 [plus.widthAnchor constraintEqualToConstant: plus.frame.size.width].active = YES;
308 [plus.heightAnchor constraintEqualToConstant: plus.frame.size.height].active = YES;
309 [plus.centerYAnchor constraintEqualToAnchor: cell.layoutMarginsGuide.centerYAnchor].active = YES;
310 [plus.trailingAnchor constraintEqualToAnchor: cell.layoutMarginsGuide.trailingAnchor constant: -20].active = YES;
316 - (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
318 UITableViewCell * cell = [tableView cellForRowAtIndexPath: indexPath];
319 return((cell.accessoryType == UITableViewCellAccessoryNone) ? nil : indexPath);
322 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
324 UITableViewCell * cell = [tableView cellForRowAtIndexPath: indexPath];
325 if (cell.accessoryType != UITableViewCellAccessoryNone)
328 CNTableViewController * controller = [self controllerForTableView: tableView];
329 NSArray * lastpathArray = controller.pathArray;
330 controller.selectionIndex = indexPath.row;
331 NSString * title = cell.textLabel.text;
332 controller = [self pushNewBrowseController: title animated: YES];
333 controller.pathArray = [lastpathArray arrayByAddingObject: title];
335 [tableView deselectRowAtIndexPath: indexPath animated: YES];
338 #pragma mark - _CNDomainBrowser Delegates
340 - (void)bonjourBrowserDomainUpdate:(NSArray *)defaultDomainPath
342 _selectedDNSDomain = DomainPathToDNSDomain(defaultDomainPath);
343 [((UITableViewController *)self.navigationController.topViewController).tableView reloadData];
344 if ([self.delegate respondsToSelector: @selector(bonjourBrowserDomainUpdate:)])
346 [self.delegate bonjourBrowserDomainUpdate: _selectedDNSDomain];