]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/DomainBrowser/macOS/CNDomainBrowserView.m
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / DomainBrowser / macOS / CNDomainBrowserView.m
1 /*
2 *
3 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18 #import "CNDomainBrowserView.h"
19 #import "_CNDomainBrowser.h"
20 #import "CNDomainBrowserPathUtils.h"
21
22 #define BROWSER_CELL_SPACING 4
23 #define INITIAL_LEGACYBROWSE 1
24
25 @implementation NSBrowser(PathArray)
26
27 - (NSArray *)pathArrayToColumn:(NSInteger)column includeSelectedRow:(BOOL)includeSelection
28 {
29 NSMutableArray * pathArray = [NSMutableArray array];
30 if (!includeSelection) column--;
31 for (NSInteger c = 0 ; c <= column ; c++)
32 {
33 NSBrowserCell *cell = [self selectedCellInColumn: c];
34 if (cell) [pathArray addObject: [cell stringValue]];
35 }
36
37 return(pathArray);
38 }
39
40 @end
41
42 @interface CNDomainBrowserView ()
43
44 @property (strong) _CNDomainBrowser * bonjour;
45
46 @property (strong) NSTableView * instanceTable;
47 @property (strong) NSArrayController * instanceC;
48 @property (strong) NSTableColumn * instanceNameColumn;
49 @property (strong) NSTableColumn * instanceServiceTypeColumn;
50 @property (strong) NSTableColumn * instancePathPopupColumn;
51
52 @property (strong) NSBrowser * browser;
53 #if INITIAL_LEGACYBROWSE
54 @property (assign) BOOL initialPathSet;
55 #endif
56
57 @end
58
59 @implementation CNDomainBrowserView
60
61 - (instancetype)initWithFrame:(NSRect)frameRect
62 {
63 if (self = [super initWithFrame: frameRect])
64 {
65 [self commonInit];
66 }
67 return(self);
68 }
69
70 - (nullable instancetype)initWithCoder:(NSCoder *)coder
71 {
72 if (self = [super initWithCoder: coder])
73 {
74 [self commonInit];
75 }
76 return(self);
77 }
78
79 - (void)awakeFromNib
80 {
81 [super awakeFromNib];
82
83 self.bonjour = [[_CNDomainBrowser alloc] initWithDelegate:(id<_CNDomainBrowserDelegate>)self];
84 _bonjour.browseRegistration = _browseRegistration;
85 _bonjour.ignoreLocal = _ignoreLocal;
86 _bonjour.ignoreBTMM = _ignoreBTMM;
87 }
88
89
90 - (void)contentViewsInit
91 {
92 NSRect frame = self.frame;
93 self.instanceC = [[NSArrayController alloc] init];
94
95 // Bottom browser
96 frame.origin.x = frame.origin.y = 0;
97 NSBrowser * browserView = [[NSBrowser alloc] initWithFrame: frame];
98 browserView.delegate = (id<NSBrowserDelegate>)self;
99 browserView.action = @selector(clickAction:);
100 browserView.titled = NO;
101 browserView.separatesColumns = NO;
102 browserView.allowsEmptySelection = YES;
103 browserView.allowsMultipleSelection = NO;
104 browserView.takesTitleFromPreviousColumn = NO;
105 browserView.hasHorizontalScroller = YES;
106 browserView.columnResizingType = NSBrowserNoColumnResizing;
107 browserView.minColumnWidth = 50;
108 browserView.translatesAutoresizingMaskIntoConstraints = NO;
109 self.browser = browserView;
110
111 [self addSubview: browserView];
112
113 [self addConstraint:
114 [NSLayoutConstraint constraintWithItem:_browser
115 attribute:NSLayoutAttributeLeft
116 relatedBy:NSLayoutRelationEqual
117 toItem:self
118 attribute:NSLayoutAttributeLeft
119 multiplier:1
120 constant:0]];
121 [self addConstraint:
122 [NSLayoutConstraint constraintWithItem:_browser
123 attribute:NSLayoutAttributeRight
124 relatedBy:NSLayoutRelationEqual
125 toItem:self
126 attribute:NSLayoutAttributeRight
127 multiplier:1
128 constant:0]];
129 [self addConstraint:
130 [NSLayoutConstraint constraintWithItem:_browser
131 attribute:NSLayoutAttributeBottom
132 relatedBy:NSLayoutRelationEqual
133 toItem:self
134 attribute:NSLayoutAttributeBottom
135 multiplier:1
136 constant:0]];
137 [self addConstraint:
138 [NSLayoutConstraint constraintWithItem:_browser
139 attribute:NSLayoutAttributeTop
140 relatedBy:NSLayoutRelationEqual
141 toItem:self
142 attribute:NSLayoutAttributeTop
143 multiplier:1
144 constant:0]];
145 }
146
147 - (void)commonInit
148 {
149 [self contentViewsInit];
150 }
151
152 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
153 {
154 [super viewWillMoveToSuperview: newSuperview];
155 if (newSuperview && !_bonjour)
156 {
157 [self awakeFromNib];
158 }
159 }
160
161 - (void)setDomainSelectionToPathArray:(NSArray *)pathArray
162 {
163 NSInteger column = 0;
164 for (NSString * nextPathComponent in pathArray)
165 {
166 NSArray * subPath = [self.browser pathArrayToColumn: column includeSelectedRow: NO];
167 NSArray * rowArray = [[self.bonjour subDomainsAtDomainPath: subPath] sortedArrayUsingComparator: ^(id obj1, id obj2) {
168 return (NSComparisonResult)[ obj1[_CNSubDomainKey_subPath] compare: obj2[_CNSubDomainKey_subPath]];
169 }];
170 NSInteger nextRow = [rowArray indexOfObjectPassingTest: ^BOOL(id obj, NSUInteger index, BOOL *stop) {
171 (void)index;
172 (void)stop;
173 return [obj[_CNSubDomainKey_subPath] isEqualToString: nextPathComponent];
174 }];
175 [self.browser selectRow: nextRow inColumn: column++];
176 }
177 }
178
179 - (NSInteger)maxNumberOfVisibleSubDomainRows
180 {
181 NSInteger result = 0;
182
183 for (NSInteger i = self.browser.firstVisibleColumn ; i <= self.browser.lastVisibleColumn ; i++)
184 {
185 NSInteger rows = [self browser: self.browser numberOfRowsInColumn: i];
186 result = MAX(rows, result);
187 }
188
189 return(result);
190 }
191
192 #pragma mark - Public Methods
193
194 - (void)setIgnoreLocal:(BOOL)ignoreLocal
195 {
196 _ignoreLocal = ignoreLocal;
197 self.bonjour.ignoreLocal = _ignoreLocal;
198 }
199
200 - (void)setBrowseRegistration:(BOOL)browseRegistration
201 {
202 _browseRegistration = browseRegistration;
203 self.bonjour.browseRegistration = _browseRegistration;
204 }
205
206 - (NSString *)selectedDNSDomain
207 {
208 NSArray * pathArray = [self.browser pathArrayToColumn: self.browser.selectedColumn includeSelectedRow: YES];
209 return(DomainPathToDNSDomain(pathArray));
210 }
211
212 - (NSString *)defaultDNSDomain
213 {
214 return(DomainPathToDNSDomain(self.bonjour.defaultDomainPath));
215 }
216
217 - (NSArray *)flattenedDNSDomains
218 {
219 return(self.bonjour.flattenedDNSDomains);
220 }
221
222
223 - (void)startBrowse
224 {
225 [self.bonjour startBrowser];
226 }
227
228 - (void)stopBrowse
229 {
230 [self.bonjour stopBrowser];
231 _initialPathSet = NO;
232 }
233
234 - (BOOL)isBrowsing
235 {
236 return(self.bonjour.isBrowsing);
237 }
238
239 - (CGFloat)minimumHeight
240 {
241 return self.selectedDNSDomain.length ? [self.browser frameOfRow: [self.browser selectedRowInColumn: self.browser.lastVisibleColumn] inColumn: self.browser.lastVisibleColumn].size.height : 0.0;
242 }
243
244 - (void)showSelectedRow
245 {
246 for( NSInteger i = self.browser.firstVisibleColumn ; i <= self.browser.lastVisibleColumn ; i++ )
247 {
248 NSInteger selRow = [self.browser selectedRowInColumn: i];
249 if( selRow != NSNotFound ) [self.browser scrollRowToVisible: selRow inColumn: i];
250 }
251 }
252
253 - (BOOL)foundInstanceInMoreThanLocalDomain
254 {
255 return( [_bonjour foundInstanceInMoreThanLocalDomain] );
256 }
257
258
259 #pragma mark - Notifications
260
261 - (void)browser:(NSBrowser *)sender selectionDidChange:(NSArray *)pathArray
262 {
263 if (_delegate && [_delegate respondsToSelector: @selector(domainBrowserDomainSelected:)] &&
264 sender == self.browser)
265 {
266 [_delegate domainBrowserDomainSelected: pathArray ? DomainPathToDNSDomain(pathArray) : nil];
267 }
268 }
269
270 #pragma mark - NSBrowserDelegate
271
272 - (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column
273 {
274 return ([self.bonjour subDomainsAtDomainPath: [sender pathArrayToColumn: column includeSelectedRow: NO]].count);
275 }
276
277 - (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(NSUInteger)row column:(NSInteger)column
278 {
279 // Get the name
280 NSMutableArray * pathArray = [NSMutableArray arrayWithArray: [sender pathArrayToColumn: column includeSelectedRow: NO]];
281 NSArray * rowArray = [[self.bonjour subDomainsAtDomainPath: pathArray] sortedArrayUsingComparator: ^(id obj1, id obj2) {
282 return (NSComparisonResult)[ obj1[_CNSubDomainKey_subPath] compare: obj2[_CNSubDomainKey_subPath]];
283 }];
284 if (row < rowArray.count)
285 {
286 NSDictionary * item = [rowArray objectAtIndex: row];
287 NSString *val = item[_CNSubDomainKey_subPath];
288 [cell setStringValue: val];
289
290 // See if it's a leaf
291 [pathArray addObject: val];
292 ((NSBrowserCell*)cell).leaf = (![self.bonjour subDomainsAtDomainPath: pathArray].count);
293
294 // Make Default domain bold
295 if ([item[_CNSubDomainKey_defaultFlag] boolValue]) ((NSBrowserCell*)cell).font = [NSFont boldSystemFontOfSize: [NSFont systemFontSizeForControlSize: sender.controlSize]];
296 else ((NSBrowserCell*)cell).font = [NSFont controlContentFontOfSize: [NSFont systemFontSizeForControlSize: sender.controlSize]];
297 }
298 }
299
300 - (CGFloat)browser:(NSBrowser *)sender shouldSizeColumn:(NSInteger)column forUserResize:(BOOL)forUserResize toWidth:(CGFloat)suggestedWidth
301 {
302 (void)forUserResize;
303 CGFloat newSize = 0;
304
305 NSArray * pathArray = [NSArray arrayWithArray: [sender pathArrayToColumn: column includeSelectedRow: NO]];
306 NSArray * rowArray = [[self.bonjour subDomainsAtDomainPath: pathArray] sortedArrayUsingComparator: ^(id obj1, id obj2) {
307 return (NSComparisonResult)[ obj1[_CNSubDomainKey_subPath] compare: obj2[_CNSubDomainKey_subPath]];
308 }];
309
310 for (NSDictionary * next in rowArray)
311 {
312 NSFont * font = [next[_CNSubDomainKey_defaultFlag] boolValue] ?
313 [NSFont boldSystemFontOfSize: [NSFont systemFontSizeForControlSize: sender.controlSize]]:
314 [NSFont controlContentFontOfSize: [NSFont systemFontSizeForControlSize: sender.controlSize]];
315 NSArray * itemArray = [pathArray arrayByAddingObjectsFromArray: [NSArray arrayWithObject: next[_CNSubDomainKey_subPath]]];
316 NSBrowserCell * cell = [[NSBrowserCell alloc] initTextCell: next[_CNSubDomainKey_subPath]];
317 cell.font = font;
318 cell.leaf = ([self.bonjour subDomainsAtDomainPath: itemArray].count == 0);
319 newSize = MAX(newSize, cell.cellSize.width + BROWSER_CELL_SPACING);
320 }
321
322 if (!newSize) newSize = suggestedWidth;
323 newSize = (NSInteger)(newSize + 0.5);
324
325 return(newSize);
326 }
327
328 #pragma mark - _CNDomainBrowser Delegates
329
330 - (void)bonjourBrowserDomainUpdate:(NSArray *)defaultDomainPath
331 {
332 (void)defaultDomainPath;
333 [self.browser loadColumnZero];
334 #if INITIAL_LEGACYBROWSE
335 if( !_initialPathSet )
336 {
337 _initialPathSet = YES;
338 [_delegate domainBrowserDomainUpdate: [NSString string]];
339 }
340 else
341 #endif
342 {
343 [self setDomainSelectionToPathArray: self.bonjour.defaultDomainPath];
344 if (_delegate && [_delegate respondsToSelector: @selector(domainBrowserDomainUpdate:)])
345 {
346 [_delegate domainBrowserDomainUpdate: defaultDomainPath ? DomainPathToDNSDomain(defaultDomainPath) : [NSString string]];
347 }
348 }
349 }
350
351 #pragma mark - Commands
352
353 - (IBAction)clickAction:(id)sender
354 {
355 (void)sender;
356 NSArray * pathArray = [self.browser pathArrayToColumn: self.browser.selectedColumn includeSelectedRow: YES];
357 if (!pathArray.count && (([NSEvent modifierFlags] & NSEventModifierFlagOption ) != NSEventModifierFlagOption)) pathArray = self.bonjour.defaultDomainPath;
358 [self setDomainSelectionToPathArray: pathArray];
359 [self browser: self.browser selectionDidChange: pathArray];
360 }
361
362 @end
363
364 @interface CNBonjourDomainCell()
365
366 @property(strong) NSMutableArray * browserCells;
367
368 @end
369
370 @implementation CNBonjourDomainCell
371
372 - (instancetype)init
373 {
374 self = [super init];
375 if (self)
376 {
377 self.browserCells = [NSMutableArray array];
378 }
379 return self;
380 }
381
382 - (instancetype)initWithCoder:(NSCoder *)coder
383 {
384 self = [super initWithCoder:coder];
385 if (self)
386 {
387 self.browserCells = [NSMutableArray array];
388 }
389 return self;
390 }
391
392 - (id)copyWithZone:(NSZone *)zone
393 {
394 CNBonjourDomainCell *cell = [super copyWithZone: zone];
395 if (cell)
396 {
397 cell.browserCells = [NSMutableArray arrayWithArray: self.browserCells];
398 }
399 return cell;
400 }
401
402 - (void) setObjectValue:(id)objectValue
403 {
404 [super setObjectValue: objectValue];
405
406 [self.browserCells removeAllObjects];
407 if ([objectValue isKindOfClass: [NSString class]])
408 {
409 NSUInteger count = 0;
410 NSArray * subPaths = DNSDomainToDomainPath(objectValue);
411 for (NSString * nextPath in subPaths)
412 {
413 NSBrowserCell * nextCell = [[NSBrowserCell alloc] initTextCell: nextPath];
414 nextCell.leaf = (++count == subPaths.count);
415 [self.browserCells addObject: nextCell];
416 }
417 }
418 }
419
420 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
421 {
422 CGFloat usedWidth = BROWSER_CELL_SPACING / 2;
423 for (NSBrowserCell * nextCell in self.browserCells)
424 {
425 NSRect nextRect = cellFrame;
426 nextRect.size.width = cellFrame.size.width - usedWidth;
427 nextRect.origin.x += usedWidth;
428
429 NSSize cellSize = [nextCell cellSizeForBounds: nextRect];
430 CGFloat yOffset = (nextRect.size.height - cellSize.height) / 2;
431 nextRect.size.width = cellSize.width;
432 nextRect.size.height = cellSize.height;
433 nextRect.origin.y += yOffset;
434
435 [nextCell drawInteriorWithFrame: nextRect
436 inView: controlView];
437 usedWidth += nextRect.size.width + BROWSER_CELL_SPACING;
438 }
439 }
440
441 @end
442
443 @interface CNBonjourDomainView()
444
445 @property(strong) CNBonjourDomainCell * cell;
446
447 @end
448
449 @implementation CNBonjourDomainView
450
451 - (instancetype)initWithFrame:(NSRect)frameRect
452 {
453 self = [super initWithFrame:frameRect];
454 if (self)
455 {
456 self.cell = [[CNBonjourDomainCell alloc] init];
457 }
458 return self;
459 }
460
461 - (instancetype)initWithCoder:(NSCoder *)coder
462 {
463 self = [super initWithCoder:coder];
464 if (self)
465 {
466 self.cell = [[CNBonjourDomainCell alloc] init];
467 }
468 return self;
469 }
470
471 - (void) setDomain:(NSString *)domain
472 {
473 if (![domain isEqualToString: self.cell.stringValue])
474 {
475 self.cell.stringValue = domain;
476 self.needsDisplay = YES;
477 }
478 }
479
480 - (NSString *)domain
481 {
482 return self.cell.stringValue;
483 }
484
485 - (void) drawRect:(NSRect)dirtyRect
486 {
487 (void)dirtyRect; // Unused
488 [self.cell drawInteriorWithFrame: self.bounds inView: self];
489 }
490
491 @end