-shortcutRecorder:keyComboDidChange: isn’t executed - cocoa

I am writing a Cocoa application and I'd like to implement a global hotkey function.
I implemented the ShortcutRecorder.framework from Waffle Software and I added a customView to my xib. Then I subclassed the CustomView to SRRecorderControl. Now I see the Recorder in my Window, but how can I get the KeyCombo and how can I react on that?
I implemented the keyComboDidChange method with no luck to get the keycode. What am I doing wrong?
Here is my code for getting the keycode:
- (void)shortcutRecorder:(SRRecorderControl *)aRecorder keyComboDidChange:(KeyCombo)newKeyCombo
{
if (aRecorder == shortcutRecorder)
{
NSLog{"We got a new Key Combo");
}
}
shortcutrecorder is my IBOutlet btw.
Do I have to implement a protocol or setDelegate:self or something like that?
Edited to add
Actually I have declared my shortcutRecorder outlet in my Preferences.h. Then in the Identity Inspector I put "Preferences" as Custom Class for Files Owner in and I connect the delegate to my Shortcut Recorder... but the keyComboDidChange is never called ... I don't understand why.

Let me explain the steps I took to get it working:
Create a window xib
The file's owner of this class is, in my case, PreferencesWindowController
Create a referencing outlet from the window to the file's owner, by right clicking the window and dragging it to the file's owner
Add the custom view to your window
You have to connect the delegate of the ShortcutRecorder to the "File's owner". To do this, right-click the SRRecorderControl and drag the delegate to the "File's owner" on your left.
After this: the ShortcutRecorder only records the hotkey and leaves it to you what to do with it. You need to use the PTHotKeyCenter (which is shipped with ShortcutRecorder) or you could implement the shortcut handling yourself.
The ShortcutRecorder contains a great demo which demonstrates the use of the ShortcutRecorder in combination with the PTHotKeyCenter. It works like this:
Listen to events from the ShortcutRecorder (which you already do, but without setting the delegate)
Check if the globalHotKey variable is set
If so, unload the previous hotkey
Init a new hotkey with the settings from the ShortcutRecorder
Set the target and action to actually capture the hotkey, once pressed
Save the hotkey to the shared center (from this moment on, the hotkey will work)
Little sample, from their source:
if (globalHotKey != nil)
{
[[PTHotKeyCenter sharedCenter] unregisterHotKey: globalHotKey];
[globalHotKey release];
globalHotKey = nil;
}
globalHotKey = [[PTHotKey alloc] initWithIdentifier:#"SRTest"
keyCombo:[PTKeyCombo keyComboWithKeyCode:[shortcutRecorder keyCombo].code
modifiers:[shortcutRecorder cocoaToCarbonFlags: [shortcutRecorder keyCombo].flags]]];
[globalHotKey setTarget: self];
[globalHotKey setAction: #selector(hitHotKey:)];
[[PTHotKeyCenter sharedCenter] registerHotKey: globalHotKey];
The only thing left to do is the hotkey handler:
- (void)hitHotKey:(PTHotKey *)hotKey
{
NSLog(#"Hotkey pressed!");
}
You could easily save the hotkey settings to the UserDefaults to load them every time you application starts.

Actually I have declared my shortcutRecorder outlet in my Preferences.h. Then in the Identity Inspector I put "Preferences" as Custom Class for Files Owner in and I connect the delegate to my Shortcut Recorder... but the keyComboDidChange is never called ... I don't understand why -.-

Related

OSX: Prevent an window from closing when user hits cmd+w key

In my MAC app, in one use case, I prompt an window to the user and give him 2 options (say buttons Save and Cancel). I want to force the user to select either of the 2 buttons to close the window.
But currently I find that if the user hits "Command + w" key when window has the focus, the window gets closed. In the .xib resource file, I uncheck the "close" option but that only disables the close option in the window UI.
How do I make sure that my window ignores the "Command+w" key and stays as is without closing.
Have also tried removing the notification by adding below code in awakeFromNib method but did not help.
[[NSNotificationCenter defaultCenter] removeObserver:NSWindowWillCloseNotification ];
Have also tried to implement "windowShouldClose" delegate method and return NO, but this method is never called. The documentation too says that this method is not reliable.
You should use an NSAlert for this sort of prompt, probably run as a sheet on the window. That would avoid the problem of closing it.
In any case, the window's delegate can implement -windowShouldClose: to control if a window is allowed to close. You can make an object (often the window controller) be its delegate by declaring that it adopts the NSWindowDelegate protocol and connecting the window's delegate outlet to that object.
I recently had to solve a similar problem. I'm not sure that this is the 'right' way to do it. But it worked for my purposes, and might work for you.
By default, I think, the 'Close Window' (CMD+W) menu item is bound to the action 'performClose' on first-responder. If you remove this binding and instead bind to a custom IBAction on your application delegate or main window controller, it allows you to conditionally call the close method of the current key-window if it is not matching the instance that you want to keep alive.
#property (strong, nonatomic) MyWindowController *unstoppable;
-(IBAction)killActiveWindow:(id)sender
{
NSWindow *keyWindow = [[NSApplication sharedApplication]keyWindow];
if ([keyWindow isNotEqualTo: unstoppable.window]){
NSLog(#" CMD+W Closing Window %#",keyWindow.title);
[keyWindow close];
}
}

Control-drag to AppDelegate only

Xcode 6.1, OSX not ios, allows me to Control-drag from a button in MainMenu.xib to AppDelegate only. This is unfortunate for me because my IBAction must include view methods like [self setNeedsDisplay:YES] . I need to Control-drag it into my MyView file, which will tolerate them. This also makes more sense.
Control-dragging from the button to any file other than AppDelegate does nothing.
Identity Inspector > Class is set to MYView.
How can I make this work, and how does the fix work?
Also, why is it now restricted to AppDelegate? Perhaps a timing issue?
Thanks ahead,
Nick
Try dragging an empty object from the object library to the area where you see 'AppDelegate' then selecting it and setting its' class to 'MyView' then secondary dragging to that object to create the IBAction.

NSMenuDelegate methods not called for contextual menu

I have a Document based application. I want to add a contextual menu that displays context-sensitive info when the user right-clicks selected text in an NSTextView.
I have followed the advice in the Apple documentation and
Added an NSMenu as a root object in my XIB file.
Connected the NSMenu instance to the menu outlet of the NSTextView.
Connected an IBAction to the NSMenuItem inside the NSMenu.
So far so good. Every thing works as expected: the menu item appears and the action is called when it is selected.
I need to get the selected text from the NSTextView before the menu appears so that I can configure my menu item appropriately. According to the docs
If you need to customize the contextual menu, you can do so by setting
an appropriate object as the menu’s delegate and implementing the
menuWillOpen: method to customize the menu as you see fit just before
it appears.
I connect the delegate of the NSMenu to File's Owner. None of the delegate methods are called. ( menuWillOpen: is the only one I need, but I've tried others, too).
I set a breakpoint inside the IBAction that gets called when the menu item is selected. If I inspect the menu with the debugger I can see that the delegate is correctly set to the object that implements the delegate method.
Is there anything else to check? Anything I'm doing blatantly wrong?
Xcode v4.6.3
SDK v10.8
Deployment target 10.7
After some digging, this is what I found: NSTextView builds a different NSMenu instance to use as the contextual menu, probably by overriding -menuForEvent: or some similar internal method. This new menu copies the menu items from the menu you created in Interface Builder (in fact, it creates new menu item instances whose attributes are copied from the original menu items) but it does not copy the menu delegate, which is why your menu delegate does not receive -menuWillOpen:. I am not sure whether this is intentional or not. Reading that documentation quote you posted, it seems to be a bug.
What you can do is to set the delegate of your NSTextView instance to an object whose class conforms to NSTextViewDelegate (maybe your File’s Owner, which already conforms to NSMenuDelegate) and implement the following method:
- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex
{
// if the menu delegate is not self, set another object
[menu setDelegate:self];
return menu;
}
This will make sure that the contextual menu created by the text view uses your delegate.
NB: since NSTextView creates a different contextual menu, it could be the case that it might want to set the menu delegate to itself or some other internal object. In my tests the delegate is nil, so it looks like it’s safe. Alternatively, you could discard the proposed menu argument and return your own NSMenu instance with the delegate correctly set.
Finding this thread saved me a lot of time...thanks! Here's an implementation that works in an NSView in Swift. myNSMenu is an outlet from Storyboard to appDelegate and a subclass of NSMenu. Without the assignment of the delegate in the code below, the NSMenuDelegate functions were not called.
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
appDelegate.myNSMenu.delegate = appDelegate.myNSMenu
NSMenu.popUpContextMenu(appDelegate.myNSMenu, withEvent: theEvent, forView: self)

How to handle NSTextView preferences (spelling and grammar, substitutions,...)

The NSTextView class allows the user to dis-/enable features like "spelling while typing" with the context menu (right click). But when I use a NSTextView in my own app, those preferences are not saved automatically by the text view itself, which means that I have to save them separately - right?
Now I also want to allow the user to change those settings in my app preferences (like in TextEdit). What I do is to save the text view preferences in the user defaults, which means that every time the user changes the setting in the app preferences, I apply those settings and save them. It's pretty easy to accomplish that except the one case where the user changes the text view setting with the context menu and not through the app preferences.
My question now: How can I get notified when the settings of a NSTextView is changed, so I can save them?
I've done a project where I have subclassed NSTextView and I can easily catch any setting changes that have been made by the user.
So, for you to do this, simply create a new .h & .m file and declare it like this:
(in the .h file)
#interface BrutellaTextView : NSTextView
#end
(in the .m file)
#interface BrutellaTextView
- (void)setContinuousSpellCheckingEnabled:(BOOL)flag
{
// here I am just setting user defaults... you may choose
// to have some other place to save the settings
NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
if(userDefaults)
{
[userDefaults setBool: flag forKey: #"continuousSpellCheckingEnabled"];
}
// and to get the functionality to actually happen,
// call the superclass
[super setContinuousSpellCheckingEnabled: flag];
}
#end
(and you can override other NSTextView methods to capture when other settings change, such as setRulerVisible:).
Now, when you are in your XIB file, make sure to set the CustomClass of your text view to be BrutellaTextView and you'll be all set!
There are no notifications that you can register to get NSTextView settings changes, so as far as I'm concerned, this is the best way to do what you're trying to do.
I hope this answer helps you out!
You could also use NSTextViews method
toggleAutomaticSpellingCorrection:
This method is only called, when the user changes the setting via menu/contextMenu and not if the programmer changes the setting programatically (e.g. temporarily disabling when inserting text).

2 Action Connections in IB to one Button

I have an xcode app that I am building using IB for mac. THe app launches with windowA already opened. There is a button on WindowA that when pressed I would like to open WindowB and Consequently close WindowA.
I have found in the tutorials that there can only be 1 sent action connection per object, so all I can do at this point is have windowA close, or call WindowB to open on top.
Can anyone tell me how I can accomplish both using the same button?
Well, actually, you can call a single method. But within this method, you may accomplish several things. For example close a window and open another.
It's not necessary to create a complete class - which would correspond to a .m file. You just add another method
- (IBAction) doIt:(id) sender
to an existing class file. Connect this to your button in IB. Therefore, you set "File's Owner" to the class where your IBAction is (or, better, put the IBAction method in "File's Owner" class file).
In interface builder, select menu item File>Reload all class files
Now, right click "File's Owner" in interface builder. You should see your action there. Drag the round circle on the right of the popup to your button. Now, each time your button is clicked, the method should be called.

Resources