Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Execute commands consistently #699

Merged
merged 6 commits into from

2 participants

@skurfer
Owner

This contains fixes for #623 and #670.

Long story short, you couldn’t have a trigger for something like “Finder Selection → Compress”. Now you can.

The problem was that there were different places that could execute an action and they didn’t all behave the same way. Rather than duplicate code, I’ve attempted to move things around so the same code is used no matter how an action is run.

I initially didn’t like QSommand doing so much with the interface controller, but I’ve talked myself into it. I can go into more detail if anyone is similarly uneasy.

I’ve tried executing things every way I can think of (using the interface, triggers, droplets, encapsulated commands, commands saved to disk, commands run after delay) and they all seem to function correctly.

skurfer added some commits
@skurfer skurfer group similar functionality together so it can be modified more easily
`executePartialCommand:` and `performOnDirectObject:indirectObject:` were each being called in two different places
b4fea8d
@skurfer skurfer add a missing method declaration cd0e212
@skurfer skurfer handle actions that return results when commands are executed
Previously, these actions would only work correctly if run by the user from the interface. Now they should work in other contexts.

fixes #623
8e6113a
@skurfer skurfer remove objects selected by the comma trick for triggers that ask for …
…input - fixes #670
f2d98df
@skurfer skurfer quiet an NSLog() when not debugging ce3fe60
@pjrobertson pjrobertson referenced this pull request
Closed

iTunes Module Crash #702

@pjrobertson
Owner

Funny I should only stumble upon this now after years of QS use, just as you've open this...

Seems like #670 also applies to objects grabbed with ⌘⎋. Would it be possible to fix that? Should be pretty easy I imagine. The ⌘⎋ command runs the receiveObject method in QSController.m. I came across it when fixing the getSelectionJump pull #643

@skurfer
Owner

Seems like #670 also applies to objects grabbed with ⌘⎋. Would it be possible to fix that?

Same goes for things opened with the qs command-line tool. I’ll see if I can fix them both.

@skurfer skurfer referenced this pull request in quicksilver/CommandLineTool-qsplugin
Merged

General updates #2

@skurfer
Owner

I’ll see if I can fix them both.

Done. :-)

@pjrobertson
Owner

Oooh the command line tool. Never actually used that. Any good?

This all looks good. i'll keep playing

@skurfer
Owner

Oooh the command line tool. Never actually used that. Any good?

Yeah, I forget it’s there most of the time, but I’ve rediscovered it in the past couple of weeks. It’s pretty cool. One time I love having it is when I’m in a plug-in’s directory doing git stuff and I want to open the Xcode project. There are lots of ways to do it, but the fastest I’ve found is to just run qs ., then hit / to go into the folder, at which point it’s pretty easy to find the project file.

@pjrobertson

You can put this line and the next into one, to save doing the test twice, but some thing it's more difficult to read:

if ([actionObject isKindOfClass:[QSRankedObject class]] && QSAction * rankedAction = [(QSRankedObject *)actionObject object]) {

You might have to declare rankedAction before though. I really don't think this would affect the performance of QS though!

@pjrobertson
Owner

I see the problem with the current command line tool. I'm assuming your change over there fixes it.

Other than that, everything looks good.

P.S. I use a different method to you for getting e.g. the current folder/file/whatever in terminal/whatever.
With the User Interface Access module install, I have a trigger for Current DocumentSelect in Command Window.

I think this is probably my most used thing in Quicksilver since neurolepsy introduced it a year or so ago. You can use it on practically anything: Safari URLs, Terminal Windows, non-cocoa apps (MS Word etc.), Preview...

@pjrobertson pjrobertson merged commit e6ae4d6 into quicksilver:master
@skurfer
Owner

I see the problem with the current command line tool. I'm assuming your change over there fixes it.

Yep.

You can use it on practically anything: Safari URLs, Terminal Windows, non-cocoa apps (MS Word etc.), Preview...

Wow. I knew it was good, but I didn’t know it worked on Terminal’s current directory. I wonder if it’s only because we have it in the title bar? Anyway, cool.

BTW, my favorite new trigger (now that this is merged): Finder Selection → Get Path (scoped to Finder only)

@pjrobertson
Owner
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 31, 2012
  1. @skurfer

    group similar functionality together so it can be modified more easily

    skurfer authored
    `executePartialCommand:` and `performOnDirectObject:indirectObject:` were each being called in two different places
  2. @skurfer
  3. @skurfer

    handle actions that return results when commands are executed

    skurfer authored
    Previously, these actions would only work correctly if run by the user from the interface. Now they should work in other contexts.
    
    fixes #623
  4. @skurfer
Commits on Feb 3, 2012
  1. @skurfer
Commits on Feb 20, 2012
  1. @skurfer
This page is out of date. Refresh to see the latest.
View
1  Quicksilver/Code-App/QSController.m
@@ -500,6 +500,7 @@ - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard {
}
- (void)receiveObject:(QSObject *)object {
+ [[self interfaceController] clearObjectView:[[self interfaceController] dSelector]];
[[self interfaceController] selectObject:object];
[[self interfaceController] actionActivate:nil];
}
View
68 Quicksilver/Code-QuickStepCore/QSCommand.m
@@ -102,7 +102,9 @@ - (QSObject *)saveCommand:(QSObject *)dObject toPath:(QSObject *)iObject {
id commandObject = [(QSCommand*)dObject dObject];
BOOL asDroplet = [[commandObject identifier] isEqualToString:@"QSDropletItemProxy"];
+#ifdef DEBUG
NSLog(@"droplet %d", asDroplet);
+#endif
NSString *destination = [[[[iObject singleFilePath] stringByAppendingPathComponent:[dObject name]] stringByAppendingPathExtension:asDroplet?@"app":@"qscommand"] firstUnusedFilePath];
if (asDroplet) {
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"QSDroplet" ofType:@"app"] toPath:destination error:nil];
@@ -398,34 +400,58 @@ - (QSObject *)execute {
#ifdef DEBUG
if (VERBOSE) NSLog(@"Execute Command: %@", self);
#endif
+ QSInterfaceController *controller = [(QSController *)[NSApp delegate] interfaceController];
int argumentCount = [(QSAction *)actionObject argumentCount];
- if (argumentCount<2) {
- return [actionObject performOnDirectObject:directObject indirectObject:indirectObject];
- } else if (argumentCount == 2) {
- if ([indirectObject objectForType:QSTextProxyType]) {
- [[(QSController *)[NSApp delegate] interfaceController] executePartialCommand:[NSArray arrayWithObjects:directObject, actionObject, indirectObject, nil]];
- } else if (indirectObject) {
- return [aObject performOnDirectObject:directObject indirectObject:indirectObject];
- } else {
- if (!indirectObject) {
- NSString *selectClass = [[NSUserDefaults standardUserDefaults] stringForKey:@"QSUnidentifiedObjectSelector"];
- id handler = [QSReg getClassInstance:selectClass];
- NSLog(@"handler %@ %@", selectClass, handler);
- if (handler && [handler respondsToSelector:@selector(completeAndExecuteCommand:)]) {
- [handler completeAndExecuteCommand:self];
- return nil;
+ if (argumentCount == 2 && (!indirectObject || [[indirectObject primaryType] isEqualToString:QSTextProxyType])) {
+ // indirect object required, but is either missing or asking for text input
+ if (!indirectObject) {
+ // attempt to use the Missing Object Selector
+ NSString *selectClass = [[NSUserDefaults standardUserDefaults] stringForKey:@"QSUnidentifiedObjectSelector"];
+ id handler = [QSReg getClassInstance:selectClass];
+#ifdef DEBUG
+ NSLog(@"handler %@ %@", selectClass, handler);
+#endif
+ if (handler && [handler respondsToSelector:@selector(completeAndExecuteCommand:)]) {
+ [handler completeAndExecuteCommand:self];
+ return nil;
+ }
+ }
+ // use Quicksilver's interface to get the missing object
+ [controller executePartialCommand:[NSArray arrayWithObjects:directObject, actionObject, indirectObject, nil]];
+ } else {
+ // indirect object is either present, or unnecessary - run the action
+ QSObject *returnValue = [actionObject performOnDirectObject:directObject indirectObject:indirectObject];
+ if (returnValue) {
+ // if the action returns something, wipe out the first pane
+ /* (The main object would get replaced anyway. This is only done to
+ remove objects selected by the comma trick before the action was run.) */
+ [controller clearObjectView:[controller dSelector]];
+ // put the result in the first pane and in the results list
+ [[controller dSelector] performSelectorOnMainThread:@selector(setObjectValue:) withObject:returnValue waitUntilDone:YES];
+ if (actionObject) {
+ if ([actionObject isKindOfClass:[QSRankedObject class]] && [(QSRankedObject *)actionObject object]) {
+ QSAction* rankedAction = [(QSRankedObject *)actionObject object];
+ if (rankedAction != actionObject) {
+ [rankedAction retain];
+ [actionObject release];
+ actionObject = rankedAction;
+ }
+ }
+ // bring the interface back to show the result
+ if ([actionObject displaysResult]) {
+ // send focus to the second pane if the user has set the preference
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QSJumpToActionOnResult"]) {
+ [controller actionActivate:nil];
+ }
+ [controller showMainWindow:controller];
}
}
- [[(QSController *)[NSApp delegate] interfaceController] executePartialCommand:[NSArray arrayWithObjects:directObject, actionObject, indirectObject, nil]];
+ return returnValue;
}
- return nil;
}
return nil;
-// NS_DURING
-// NS_HANDLER
-// ;
-// NS_ENDHANDLER
}
+
- (void)executeFromMenu:(id)sender {
//NSLog(@"sender %@", NSStringFromClass([sender class]) );
QSObject *object = [self execute];
View
1  Quicksilver/Code-QuickStepInterface/QSInterfaceController.h
@@ -96,4 +96,5 @@
- (BOOL)preview;
- (void)setPreview:(BOOL)flag;
+- (void)clearObjectView:(QSSearchObjectView *)view;
@end
View
27 Quicksilver/Code-QuickStepInterface/QSInterfaceController.m
@@ -515,29 +515,8 @@ - (void)executeCommandThreaded {
dObject = [(QSRankedObject*)dObject object];
if( [iObject isKindOfClass:[QSRankedObject class]] )
iObject = [(QSRankedObject*)iObject object];
- QSObject *returnValue = [action performOnDirectObject:dObject indirectObject:iObject];
- if (returnValue) {
- // if the action returns something, wipe out the first pane
- /* (The main object would get replaced anyway. This is only done to
- remove objects selected by the comma trick before the action was run.) */
- [self clearObjectView:dSelector];
- // put the result in the first pane and in the results list
- [dSelector performSelectorOnMainThread:@selector(setObjectValue:) withObject:returnValue waitUntilDone:YES];
- if (action) {
- if ([action isKindOfClass:[QSRankedObject class]] && [(QSRankedObject *)action object]) {
- QSAction* rankedAction = [(QSRankedObject *)action object];
- if (rankedAction != action) {
- [rankedAction retain];
- [action release];
- action = rankedAction;
- }
- }
- // bring the interface back to show the result
- if ([action displaysResult]) {
- [self actionActivate:nil];
- }
- }
- }
+ QSCommand *command = [QSCommand commandWithDirectObject:dObject actionObject:action indirectObject:iObject];
+ [command execute];
#ifdef DEBUG
if (VERBOSE) NSLog(@"Command executed (%dms) ", (int)(-[startDate timeIntervalSinceNow] *1000));
#endif
@@ -546,6 +525,8 @@ - (void)executeCommandThreaded {
}
- (void)executePartialCommand:(NSArray *)array {
+ // remove objects previously selected by the comma trick
+ [self clearObjectView:dSelector];
[dSelector setObjectValue:[array objectAtIndex:0]];
if ([array count] == 1) {
[self updateActionsNow];
Something went wrong with that request. Please try again.