-
Notifications
You must be signed in to change notification settings - Fork 180
/
Action.m
472 lines (396 loc) · 13.8 KB
/
Action.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
//
// Action.m
// ControlPlane
//
// Created by David Symonds on 3/04/07.
//
#import "Action.h"
#import "DSLogger.h"
#import "PrefsWindowController.h"
#import "ActionSubmenuItem.h"
@implementation Action
+ (NSString *)typeForClass:(Class)klass
{
// Hack "Action" off class name (6 chars)
// TODO: make this a bit more robust?
NSString *className = NSStringFromClass(klass);
return [className substringToIndex:([className length] - 6)];
}
+ (Class)classForType:(NSString *)type
{
NSString *classString = [NSString stringWithFormat:@"%@Action", type];
Class klass = NSClassFromString(classString);
if (!klass) {
NSLog(@"ERROR: No implementation class '%@'!", classString);
return nil;
}
return klass;
}
+ (Action *)actionFromDictionary:(NSDictionary *)dict
{
NSString *type = [dict valueForKey:@"type"];
if (!type) {
NSLog(@"ERROR: Action doesn't have a type!");
return nil;
}
Action *obj = [[[Action classForType:type] alloc] initWithDictionary:dict];
return [obj autorelease];
}
- (id)init
{
if ([[self class] isEqualTo:[Action class]]) {
[NSException raise:@"Abstract Class Exception"
format:@"Error, attempting to instantiate Action directly."];
}
if (!(self = [super init]))
return nil;
// Some sensible defaults
type = [[Action typeForClass:[self class]] retain];
context = [@"" retain];
when = [@"Arrival" retain];
delay = [[NSNumber alloc] initWithDouble:0];
enabled = [[NSNumber alloc] initWithBool:YES];
return self;
}
- (id)initWithDictionary:(NSDictionary *)dict
{
if ([[self class] isEqualTo:[Action class]]) {
[NSException raise:@"Abstract Class Exception"
format:@"Error, attempting to instantiate Action directly."];
}
if (!(self = [super init]))
return nil;
type = [[Action typeForClass:[self class]] retain];
context = [[dict valueForKey:@"context"] copy];
when = [[dict valueForKey:@"when"] copy];
delay = [[dict valueForKey:@"delay"] copy];
enabled = [[dict valueForKey:@"enabled"] copy];
return self;
}
- (void)dealloc
{
[type release];
[context release];
[when release];
[delay release];
[enabled release];
[super dealloc];
}
- (NSMutableDictionary *)dictionary
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[type copy] autorelease], @"type",
[[context copy] autorelease], @"context",
[[when copy] autorelease], @"when",
[[delay copy] autorelease], @"delay",
[[enabled copy] autorelease], @"enabled",
nil];
}
+ (NSString *)helpTextForActionOfType:(NSString *)type
{
return [[Action classForType:type] helpText];
}
- (NSComparisonResult)compareDelay:(Action *)other
{
return [[self valueForKey:@"delay"] compare:[other valueForKey:@"delay"]];
}
- (void)notImplemented:(NSString *)methodName
{
[NSException raise:@"Abstract Class Exception"
format:@"Error, -[%@ %@] not implemented.",
[self class], methodName];
}
- (NSString *)description
{
[self notImplemented:@"description"];
return @"Not implemented!";
}
- (BOOL)execute:(NSString **)errorString
{
[self notImplemented:@"execute"];
*errorString = @"Not implemented!";
return NO;
}
+ (NSString *)helpText
{
return @"Sorry, no help text written yet!";
}
+ (NSString *)creationHelpText
{
return @"<Sorry, help text coming soon!>";
}
+ (NSString *)friendlyName {
return @"Not implemented";
}
+ (BOOL) shouldWaitForScreensaverExit {
return NO;
}
+ (BOOL) shouldWaitForScreenUnlock {
return NO;
}
+ (NSString *)menuCategory {
return @"";
}
- (void)executeAppleScriptForReal:(NSString *)script
{
appleScriptResult_ = nil;
NSAppleScript *as = [[[NSAppleScript alloc] initWithSource:script] autorelease];
if (!as) {
NSLog(@"AppleScript failed to construct! Script was:\n%@", script);
return;
}
NSDictionary *errorDict;
if (![as compileAndReturnError:&errorDict]) {
NSLog(@"AppleScript failed to compile! Script was:\n%@\nError dictionary: %@", script, errorDict);
return;
}
appleScriptResult_ = [as executeAndReturnError:&errorDict];
if (!appleScriptResult_)
NSLog(@"AppleScript failed to execute! Script was:\n%@\nError dictionary: %@", script, errorDict);
}
- (BOOL)executeAppleScript:(NSString *)script
{
// NSAppleScript is not thread-safe, so this needs to happen on the main thread. Ick.
[self performSelectorOnMainThread:@selector(executeAppleScriptForReal:)
withObject:script
waitUntilDone:YES];
return (appleScriptResult_ ? YES : NO);
}
- (NSArray *)executeAppleScriptReturningListOfStrings:(NSString *)script
{
if (![self executeAppleScript:script])
return nil;
if ([appleScriptResult_ descriptorType] != typeAEList)
return nil;
long count = [appleScriptResult_ numberOfItems], i;
NSMutableArray *list = [NSMutableArray arrayWithCapacity:count];
for (i = 1; i <= count; ++i) { // Careful -- AppleScript lists are 1-based
NSAppleEventDescriptor *elt = [appleScriptResult_ descriptorAtIndex:i];
if (!elt) {
NSLog(@"Oops -- couldn't get descriptor at index %ld", i);
continue;
}
NSString *val = [elt stringValue];
if (!val) {
NSLog(@"Oops -- couldn't turn descriptor at index %ld into string", i);
continue;
}
[list addObject:val];
}
return list;
}
@end
#pragma mark -
#import "DefaultBrowserAction.h"
#import "DefaultPrinterAction.h"
#import "DesktopBackgroundAction.h"
#import "DisplayBrightnessAction.h"
#import "DisplaySleepTimeAction.h"
#import "FirewallRuleAction.h"
#import "IChatAction.h"
#import "ITunesPlaylistAction.h"
#import "LockKeychainAction.h"
#import "MailIMAPServerAction.h"
#import "MailSMTPServerAction.h"
#import "MailIntervalAction.h"
#import "MountAction.h"
#import "MuteAction.h"
#import "NetworkLocationAction.h"
#import "OpenAction.h"
#import "OpenURLAction.h"
#import "QuitApplicationAction.h"
#import "ScreenSaverPasswordAction.h"
#import "ScreenSaverStartAction.h"
#import "ScreenSaverTimeAction.h"
#import "ShellScriptAction.h"
#import "SpeakAction.h"
#import "StartTimeMachineAction.h"
#import "TimeMachineDestinationAction.h"
#import "ToggleBluetoothAction.h"
#import "ToggleFileSharingAction.h"
#import "ToggleFirewallAction.h"
#import "ToggleFTPAction.h"
#import "ToggleInternetSharingAction.h"
#import "ToggleNaturalScrollingAction.h"
#import "TogglePrinterSharingAction.h"
#import "ToggleRemoteLoginAction.h"
#import "ToggleTFTPAction.h"
#import "ToggleTimeMachineAction.h"
#import "ToggleWebSharingAction.h"
#import "ToggleWiFiAction.h"
#import "UnmountAction.h"
#import "VPNAction.h"
@implementation ActionSetController
- (id)init
{
if (!(self = [super init]))
return nil;
// get system version
SInt32 major = 0, minor = 0;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
classes = [[NSArray alloc] initWithObjects:
[DefaultBrowserAction class],
[DefaultPrinterAction class],
[DesktopBackgroundAction class],
[DisplayBrightnessAction class],
[DisplaySleepTimeAction class],
[IChatAction class],
[ITunesPlaylistAction class],
[LockKeychainAction class],
[MailIMAPServerAction class],
[MailSMTPServerAction class],
[MailIntervalAction class],
[MountAction class],
[MuteAction class],
[NetworkLocationAction class],
[OpenAction class],
[OpenURLAction class],
[QuitApplicationAction class],
[ScreenSaverPasswordAction class],
[ScreenSaverStartAction class],
[ScreenSaverTimeAction class],
[ShellScriptAction class],
[SpeakAction class],
[StartTimeMachineAction class],
[TimeMachineDestinationAction class],
[ToggleBluetoothAction class],
[ToggleFileSharingAction class],
[ToggleFirewallAction class],
[ToggleFTPAction class],
[ToggleInternetSharingAction class],
[ToggleNaturalScrollingAction class],
[TogglePrinterSharingAction class],
[ToggleRemoteLoginAction class],
[ToggleTFTPAction class],
[ToggleTimeMachineAction class],
[ToggleWebSharingAction class],
[ToggleWiFiAction class],
[UnmountAction class],
[VPNAction class],
nil];
if (NO) {
// Purely for the benefit of 'genstrings'
NSLocalizedString(@"DefaultBrowser", @"Action type");
NSLocalizedString(@"DefaultPrinter", @"Action type");
NSLocalizedString(@"DesktopBackground", @"Action type");
NSLocalizedString(@"DisplayBrightness", @"Action type");
NSLocalizedString(@"iChat", @"Action type");
NSLocalizedString(@"iTunesPlaylist", @"Action type");
NSLocalizedString(@"LockKeychain", @"Action type");
NSLocalizedString(@"MailIMAPServer", @"Action type");
NSLocalizedString(@"MailSMTPServer", @"Action type");
NSLocalizedString(@"MailInterval", @"Action type");
NSLocalizedString(@"Mount", @"Action type");
NSLocalizedString(@"Mute", @"Action type");
NSLocalizedString(@"NetworkLocation", @"Action type");
NSLocalizedString(@"Open", @"Action type");
NSLocalizedString(@"OpenURL", @"Action type");
NSLocalizedString(@"QuitApplication", @"Action type");
NSLocalizedString(@"ScreenSaverPassword", @"Action type");
NSLocalizedString(@"ScreenSaverStart", @"Action type");
NSLocalizedString(@"ScreenSaverTime", @"Action type");
NSLocalizedString(@"ShellScript", @"Action type");
NSLocalizedString(@"Speak", @"Action type");
NSLocalizedString(@"StartTimeMachine", @"Action type");
NSLocalizedString(@"TimeMachineDestination", @"Action type");
NSLocalizedString(@"ToggleBluetooth", @"Action type");
NSLocalizedString(@"ToggleFileSharing", @"Action type");
NSLocalizedString(@"ToggleFirewall", @"Action type");
NSLocalizedString(@"ToggleInternetSharing", @"Action type");
NSLocalizedString(@"ToggleNaturalScrolling", @"Action type");
NSLocalizedString(@"TimeMachineAction",@"Action type");
NSLocalizedString(@"ToggleWiFi", @"Action type");
NSLocalizedString(@"Unmount", @"Action type");
NSLocalizedString(@"VPN", @"Action type");
}
// hack to remove the DisplayBrightnessAction on ML
if (major == 10 && minor > 7) {
NSMutableArray *tmp = [classes mutableCopy];
[tmp removeObject:[DisplayBrightnessAction class]];
classes = tmp;
}
if (major == 10 && minor < 7) {
NSMutableArray *tmp = [classes mutableCopy];
[tmp removeObject:[ToggleNaturalScrollingAction class]];
classes = tmp;
}
// build a list of menu categories
NSMutableDictionary *menuCategoryBuilder = [NSMutableDictionary dictionary];
NSMutableDictionary *tmpDict = nil;
ActionSubmenuItem *tmp = nil;
for (id currentClass in classes) {
// if the object exists then we've seen this category before
// and we simply want to add the class to the object we just found
if ([menuCategoryBuilder objectForKey:[currentClass menuCategory]]) {
tmp = [menuCategoryBuilder objectForKey:[currentClass menuCategory]];
tmpDict = [NSMutableDictionary dictionaryWithCapacity:3];
[tmpDict setObject:currentClass forKey:@"class"];
[tmpDict setObject:[currentClass class] forKey:@"representedObject"];
[tmp setTarget:prefsWindowController];
[tmp addObject:tmpDict];
}
else {
tmp = [[ActionSubmenuItem alloc] init];
tmpDict = [NSMutableDictionary dictionaryWithCapacity:3];
[tmpDict setObject:currentClass forKey:@"class"];
[tmpDict setObject:[currentClass class] forKey:@"representedObject"];
[tmp setTarget:prefsWindowController];
[tmp addObject:tmpDict];
[menuCategoryBuilder setObject:tmp forKey:[currentClass menuCategory]];
[tmp release];
}
}
//NSLog(@"looks like %@", menuCategoryBuilder);
menuCategories = [menuCategoryBuilder copy];
return self;
}
- (void)dealloc
{
[classes release];
[menuCategories release];
[super dealloc];
}
- (NSArray *)types
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[classes count]];
NSEnumerator *en = [classes objectEnumerator];
Class klass;
while ((klass = [en nextObject])) {
[array addObject:[Action typeForClass:klass]];
}
return array;
}
#pragma mark NSMenu delegates
- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(int)index shouldCancel:(BOOL)shouldCancel
{
//Class klass = [classes objectAtIndex:index];
NSArray *menuCategoryList = [menuCategories allKeys];
menuCategoryList = [menuCategoryList sortedArrayUsingSelector:@selector(compare:)];
NSString *friendlyName = [[classes objectAtIndex:index] friendlyName];
NSString *categoryName = [menuCategoryList objectAtIndex:index];
//NSString *localisedType = NSLocalizedString(type, @"Action type");
NSMenu *newSubMenu = [[[NSMenu alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"%@ Actions", @""), friendlyName]] retain];
[newSubMenu setDelegate:[menuCategories objectForKey:categoryName]];
//NSString *title = [NSString stringWithFormat:NSLocalizedString(@"'%@' Actions...", @"Menu item"),
//friendlyName];
//[item setTitle:title];
[item setSubmenu:newSubMenu];
[item setTitle:[NSString stringWithFormat:NSLocalizedString(@"%@ Actions", @""), categoryName]];
[item setRepresentedObject:[classes objectAtIndex:index]];
//DSLog(@"menu category %@", [[classes objectAtIndex:index] menuCategory]);
//[item setTarget:prefsWindowController];
//[item setAction:@selector(addAction:)];
//[item setRepresentedObject:klass];
return YES;
}
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
{
// TODO: support keyboard menu jumping?
return NO;
}
- (NSUInteger)numberOfItemsInMenu:(NSMenu *)menu
{
return [menuCategories count];
}
@end