]>
Commit | Line | Data |
---|---|---|
c9b9ae52 A |
1 | /* |
2 | * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | ||
23 | Change History (most recent first): | |
24 | ||
25 | $Log: BrowserController.m,v $ | |
26 | Revision 1.18 2003/08/12 19:55:07 cheshire | |
27 | Update to APSL 2.0 | |
28 | ||
29 | */ | |
30 | ||
31 | #import "BrowserController.h" | |
32 | ||
33 | #include "arpa/inet.h" | |
34 | ||
35 | void | |
36 | MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) | |
37 | { | |
38 | DNSServiceDiscovery_handleReply(msg); | |
39 | } | |
40 | ||
41 | void browse_reply ( | |
42 | DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType | |
43 | const char *replyName, | |
44 | const char *replyType, | |
45 | const char *replyDomain, | |
46 | DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information | |
47 | void *context | |
48 | ) | |
49 | { | |
50 | [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; | |
51 | return; | |
52 | } | |
53 | ||
54 | void enum_reply ( | |
55 | DNSServiceDomainEnumerationReplyResultType resultType, | |
56 | const char *replyDomain, | |
57 | DNSServiceDiscoveryReplyFlags flags, | |
58 | void *context | |
59 | ) | |
60 | { | |
61 | [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; | |
62 | ||
63 | return; | |
64 | } | |
65 | ||
66 | void resolve_reply ( | |
67 | struct sockaddr *interface, | |
68 | struct sockaddr *address, | |
69 | const char *txtRecord, | |
70 | DNSServiceDiscoveryReplyFlags flags, | |
71 | void *context | |
72 | ) | |
73 | { | |
74 | [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]]; | |
75 | ||
76 | return; | |
77 | } | |
78 | ||
79 | @implementation BrowserController //Begin implementation of BrowserController methods | |
80 | ||
81 | - (void)registerDefaults | |
82 | { | |
83 | NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; | |
84 | ||
85 | NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.", | |
86 | @"_ssh._tcp.", @"_telnet._tcp.", | |
87 | @"_http._tcp.", | |
88 | @"_printer._tcp.", @"_ipp._tcp.", | |
89 | @"_ichat._tcp.", @"_eppc._tcp.", | |
90 | @"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil]; | |
91 | NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)", | |
92 | @"Secure Shell (ssh)", @"Telnet", | |
93 | @"Web Server (http)", | |
94 | @"LPR Printer", @"IPP Printer", | |
95 | @"iChat", @"Remote AppleEvents", | |
96 | @"AppleShare Server", @"SMB File Server", @"Mystery Service", nil]; | |
97 | ||
98 | [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; | |
99 | [regDict setObject:nameArray forKey:@"SrvNameKeys"]; | |
100 | ||
101 | [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; | |
102 | } | |
103 | ||
104 | ||
105 | - (id)init | |
106 | { | |
107 | [self registerDefaults]; | |
108 | ||
109 | browse_client = nil; | |
110 | ||
111 | return [super init]; | |
112 | } | |
113 | ||
114 | - (void)awakeFromNib //BrowserController startup procedure | |
115 | { | |
116 | SrvType=NULL; | |
117 | Domain=NULL; | |
118 | srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name | |
119 | srvnameKeys = [NSMutableArray array]; | |
120 | ||
121 | domainKeys = [NSMutableArray array]; | |
122 | [domainKeys retain]; | |
123 | ||
124 | nameKeys = [NSMutableArray array]; | |
125 | [nameKeys retain]; | |
126 | ||
127 | [srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes | |
128 | [srvnameKeys retain]; //Keep arrays in memory until BrowserController closes | |
129 | [typeField setDataSource:self]; //Set application fields' data source to BrowserController | |
130 | [typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width. | |
131 | [nameField setDataSource:self]; | |
132 | [nameField sizeLastColumnToFit]; | |
133 | [domainField setDataSource:self]; | |
134 | [domainField sizeLastColumnToFit]; | |
135 | ||
136 | [nameField setDoubleAction:@selector(connect:)]; | |
137 | ||
138 | //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their | |
139 | //[srvnameKeys addObject:@"File Transfer (ftp)"]; | |
140 | //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays | |
141 | //[srvnameKeys addObject:@"Printer (lpr)"]; | |
142 | //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays | |
143 | //[srvnameKeys addObject:@"Web Server (http)"]; | |
144 | //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays | |
145 | //[srvnameKeys addObject:@"AppleShare Server (afp)"]; | |
146 | ||
147 | [ipAddressField setStringValue:@""]; | |
148 | [portField setStringValue:@""]; | |
149 | [textField setStringValue:@""]; | |
150 | ||
151 | [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; | |
152 | [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; | |
153 | ||
154 | ||
155 | [typeField reloadData]; //Reload (redraw) data in fields | |
156 | [domainField reloadData]; | |
157 | ||
158 | [self loadDomains:self]; | |
159 | ||
160 | } | |
161 | ||
162 | - (void)dealloc //Deallocation method | |
163 | { | |
164 | [srvtypeKeys release]; | |
165 | [srvnameKeys release]; | |
166 | [nameKeys release]; | |
167 | [domainKeys release]; | |
168 | } | |
169 | ||
170 | -(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row | |
171 | { | |
172 | if (row<0) return; | |
173 | } | |
174 | ||
175 | - (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods | |
176 | { | |
177 | if (theTableView == typeField) | |
178 | { | |
179 | return [srvnameKeys count]; | |
180 | } | |
181 | if (theTableView == domainField) | |
182 | { | |
183 | return [domainKeys count]; | |
184 | } | |
185 | if (theTableView == nameField) | |
186 | { | |
187 | return [nameKeys count]; | |
188 | } | |
189 | if (theTableView == serviceDisplayTable) | |
190 | { | |
191 | return [srvnameKeys count]; | |
192 | } | |
193 | return 0; | |
194 | } | |
195 | ||
196 | - (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex | |
197 | { | |
198 | if (theTableView == typeField) | |
199 | { | |
200 | return [srvnameKeys objectAtIndex:rowIndex]; | |
201 | } | |
202 | if (theTableView == domainField) | |
203 | { | |
204 | return [domainKeys objectAtIndex:rowIndex]; | |
205 | } | |
206 | if (theTableView == nameField) | |
207 | { | |
208 | return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex]; | |
209 | } | |
210 | if (theTableView == serviceDisplayTable) | |
211 | { | |
212 | if (theColumn == typeColumn) { | |
213 | return [srvtypeKeys objectAtIndex:rowIndex]; | |
214 | } | |
215 | if (theColumn == nameColumn) { | |
216 | return [srvnameKeys objectAtIndex:rowIndex]; | |
217 | } | |
218 | return 0; | |
219 | } | |
220 | else | |
221 | return(0); | |
222 | } //End of mandatory TableView methods | |
223 | ||
224 | - (IBAction)handleTypeClick:(id)sender //Handle clicks for Type | |
225 | { | |
226 | int index=[sender selectedRow]; //Find index of selected row | |
227 | if (index==-1) return; //Error checking | |
228 | SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type | |
229 | SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type | |
230 | ||
231 | [ipAddressField setStringValue:@""]; | |
232 | [portField setStringValue:@""]; | |
233 | [textField setStringValue:@""]; | |
234 | ||
235 | [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records | |
236 | } | |
237 | ||
238 | - (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain | |
239 | { | |
240 | int index=[sender selectedRow]; //Find index of selected row | |
241 | if (index==-1) return; //Error checking | |
242 | Domain = [domainKeys objectAtIndex:index]; //Save desired Domain | |
243 | ||
244 | [ipAddressField setStringValue:@""]; | |
245 | [portField setStringValue:@""]; | |
246 | [textField setStringValue:@""]; | |
247 | ||
248 | if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records | |
249 | } | |
250 | ||
251 | - (IBAction)handleNameClick:(id)sender //Handle clicks for Name | |
252 | { | |
253 | int index=[sender selectedRow]; //Find index of selected row | |
254 | if (index==-1) return; //Error checking | |
255 | Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name | |
256 | ||
257 | { | |
258 | CFMachPortRef cfMachPort; | |
259 | CFMachPortContext context; | |
260 | Boolean shouldFreeInfo; | |
261 | dns_service_discovery_ref dns_client; | |
262 | mach_port_t port; | |
263 | CFRunLoopSourceRef rls; | |
264 | ||
265 | context.version = 1; | |
266 | context.info = 0; | |
267 | context.retain = NULL; | |
268 | context.release = NULL; | |
269 | context.copyDescription = NULL; | |
270 | ||
271 | [ipAddressField setStringValue:@"?"]; | |
272 | [portField setStringValue:@"?"]; | |
273 | [textField setStringValue:@"?"]; | |
274 | // start an enumerator on the local server | |
275 | dns_client = DNSServiceResolverResolve | |
276 | ( | |
277 | (char *)[Name UTF8String], | |
278 | (char *)[SrvType UTF8String], | |
279 | (char *)(Domain?[Domain UTF8String]:""), | |
280 | resolve_reply, | |
281 | nil | |
282 | ); | |
283 | ||
284 | port = DNSServiceDiscoveryMachPort(dns_client); | |
285 | ||
286 | if (port) { | |
287 | cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); | |
288 | ||
289 | /* Create and add a run loop source for the port */ | |
290 | rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); | |
291 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
292 | CFRelease(rls); | |
293 | } else { | |
294 | printf("Could not obtain client port\n"); | |
295 | return; | |
296 | } | |
297 | } | |
298 | } | |
299 | ||
300 | - (IBAction)loadDomains:(id)sender | |
301 | { | |
302 | CFMachPortRef cfMachPort; | |
303 | CFMachPortContext context; | |
304 | Boolean shouldFreeInfo; | |
305 | dns_service_discovery_ref dns_client; | |
306 | mach_port_t port; | |
307 | CFRunLoopSourceRef rls; | |
308 | ||
309 | context.version = 1; | |
310 | context.info = 0; | |
311 | context.retain = NULL; | |
312 | context.release = NULL; | |
313 | context.copyDescription = NULL; | |
314 | ||
315 | // start an enumerator on the local server | |
316 | dns_client = DNSServiceDomainEnumerationCreate | |
317 | ( | |
318 | 0, | |
319 | enum_reply, | |
320 | nil | |
321 | ); | |
322 | ||
323 | port = DNSServiceDiscoveryMachPort(dns_client); | |
324 | ||
325 | if (port) { | |
326 | cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); | |
327 | ||
328 | /* Create and add a run loop source for the port */ | |
329 | rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); | |
330 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
331 | CFRelease(rls); | |
332 | } else { | |
333 | printf("Could not obtain client port\n"); | |
334 | return; | |
335 | } | |
336 | } | |
337 | ||
338 | - (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application | |
339 | { | |
340 | const char * DomainC; | |
341 | const char * TypeC=[theType UTF8String]; //Type in C string format | |
342 | ||
343 | if (theDomain) { | |
344 | DomainC = [theDomain UTF8String]; //Domain in C string format | |
345 | } else { | |
346 | DomainC = ""; | |
347 | } | |
348 | ||
349 | [nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones | |
350 | [nameField reloadData]; //Reload (redraw) names to show the old data is gone | |
351 | ||
352 | // get rid of the previous browser if one exists | |
353 | if (browse_client) { | |
354 | DNSServiceDiscoveryDeallocate(browse_client); | |
355 | browse_client = nil; | |
356 | } | |
357 | ||
358 | // now create a browser to return the values for the nameField ... | |
359 | { | |
360 | CFMachPortRef cfMachPort; | |
361 | CFMachPortContext context; | |
362 | Boolean shouldFreeInfo; | |
363 | mach_port_t port; | |
364 | CFRunLoopSourceRef rls; | |
365 | ||
366 | context.version = 1; | |
367 | context.info = 0; | |
368 | context.retain = NULL; | |
369 | context.release = NULL; | |
370 | context.copyDescription = NULL; | |
371 | ||
372 | // start an enumerator on the local server | |
373 | browse_client = DNSServiceBrowserCreate | |
374 | ( | |
375 | (char *)TypeC, | |
376 | (char *)DomainC, | |
377 | browse_reply, | |
378 | nil | |
379 | ); | |
380 | ||
381 | port = DNSServiceDiscoveryMachPort(browse_client); | |
382 | ||
383 | if (port) { | |
384 | cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); | |
385 | ||
386 | /* Create and add a run loop source for the port */ | |
387 | rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); | |
388 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
389 | CFRelease(rls); | |
390 | } else { | |
391 | printf("Could not obtain client port\n"); | |
392 | return; | |
393 | } | |
394 | } | |
395 | ||
396 | } | |
397 | ||
398 | ||
399 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed | |
400 | { | |
401 | return YES; | |
402 | } | |
403 | ||
404 | - (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting | |
405 | { | |
406 | [domainField reloadData]; | |
407 | return YES; | |
408 | } | |
409 | ||
410 | - (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags | |
411 | { | |
412 | // new domain received | |
413 | if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) { | |
414 | // add the domain to the list | |
415 | [domainKeys addObject:domain]; | |
416 | } else { | |
417 | // remove the domain from the list | |
418 | NSEnumerator *dmnEnum = [domainKeys objectEnumerator]; | |
419 | NSString *aDomain = nil; | |
420 | ||
421 | while (aDomain = [dmnEnum nextObject]) { | |
422 | if ([aDomain isEqualToString:domain]) { | |
423 | [domainKeys removeObject:domain]; | |
424 | break; | |
425 | } | |
426 | } | |
427 | } | |
428 | // update the domain table | |
429 | [domainField reloadData]; | |
430 | return; | |
431 | } | |
432 | ||
433 | ||
434 | ||
435 | - (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags | |
436 | { | |
437 | ||
438 | //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type); | |
439 | ||
440 | if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) { | |
441 | ||
442 | if (type == DNSServiceBrowserReplyRemoveInstance) { | |
443 | if ([nameKeys containsObject:name]) { | |
444 | [nameKeys removeObject:name]; | |
445 | } | |
446 | } | |
447 | if (type == DNSServiceBrowserReplyAddInstance) { | |
448 | if (![nameKeys containsObject:name]) { | |
449 | [nameKeys addObject:name]; | |
450 | } | |
451 | } | |
452 | ||
453 | // If not expecting any more data, then reload (redraw) Name TableView with newly found data | |
454 | if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0) | |
455 | [nameField reloadData]; | |
456 | } | |
457 | return; | |
458 | } | |
459 | ||
460 | - (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord | |
461 | { | |
462 | if (address->sa_family != AF_INET) return; // For now we only handle IPv4 | |
463 | //printf("interface length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)interface)->sin_len, ((struct sockaddr_in *)interface)->sin_port, ((struct sockaddr_in *)interface)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)interface)->sin_addr))); | |
464 | //printf("address length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)address)->sin_len, ((struct sockaddr_in *)address)->sin_port, ((struct sockaddr_in *)address)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))); | |
465 | NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))]; | |
466 | int port = ((struct sockaddr_in *)address)->sin_port; | |
467 | ||
468 | [ipAddressField setStringValue:ipAddr]; | |
469 | [portField setIntValue:port]; | |
470 | [textField setStringValue:txtRecord]; | |
471 | ||
472 | return; | |
473 | } | |
474 | ||
475 | - (void)connect:(id)sender | |
476 | { | |
477 | NSString *ipAddr = [ipAddressField stringValue]; | |
478 | int port = [portField intValue]; | |
479 | NSString *txtRecord = [textField stringValue]; | |
480 | ||
481 | if (!txtRecord) txtRecord = @""; | |
482 | ||
483 | if (!ipAddr || !port) return; | |
484 | ||
485 | if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", ipAddr, port]]]; | |
486 | else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", ipAddr, port]]]; | |
487 | else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", ipAddr, port]]]; | |
488 | else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", ipAddr, port]]]; | |
489 | else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", ipAddr, port]]]; | |
490 | else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", ipAddr, port]]]; | |
491 | else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", ipAddr, port]]]; | |
492 | else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", ipAddr, port]]]; | |
493 | else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", ipAddr, port]]]; | |
494 | ||
495 | return; | |
496 | } | |
497 | ||
498 | - (IBAction)handleTableClick:(id)sender | |
499 | { | |
500 | //populate the text fields | |
501 | } | |
502 | ||
503 | - (IBAction)removeSelected:(id)sender | |
504 | { | |
505 | // remove the selected row and force a refresh | |
506 | ||
507 | int selectedRow = [serviceDisplayTable selectedRow]; | |
508 | ||
509 | if (selectedRow) { | |
510 | ||
511 | [srvtypeKeys removeObjectAtIndex:selectedRow]; | |
512 | [srvnameKeys removeObjectAtIndex:selectedRow]; | |
513 | ||
514 | [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; | |
515 | [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; | |
516 | ||
517 | [typeField reloadData]; | |
518 | [serviceDisplayTable reloadData]; | |
519 | } | |
520 | } | |
521 | ||
522 | - (IBAction)addNewService:(id)sender | |
523 | { | |
524 | // add new entries from the edit fields to the arrays for the defaults | |
525 | ||
526 | if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) { | |
527 | [srvtypeKeys addObject:[serviceTypeField stringValue]]; | |
528 | [srvnameKeys addObject:[serviceNameField stringValue]]; | |
529 | ||
530 | [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; | |
531 | [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; | |
532 | ||
533 | [typeField reloadData]; | |
534 | [serviceDisplayTable reloadData]; | |
535 | } | |
536 | ||
537 | } | |
538 | ||
539 | ||
540 | ||
541 | @end |