Custom accessory view for NSSavePanel of NSDocument - macos

Consider a Cocoa NSDocument that supports two document types (e.g. BMP and JPEG). While one type has no save options (e.g. BMP), the other does (e.g. compression level for JPEG).
How do you implement this?
I understand that you should override prepareSavePanel: and set the accessory view of the given NSSavePanel. However, doing this replaces the default accessory view with the document type pop-up.
Is it necessary to recreate the document type pop-up if when using a custom accessory view?
If yes, how can pass the selected document type and the additional save options to the NSDocument write methods?
If no, how can I show the additional save options (e.g. compression level) only if the corresponding document (e.g. JPEG) type has been selected? Is there a delegate method for document type changes in the NSSavePanel?

I'm pretty certain that yes, if you want a custom accessory view like this, you have to provide the entire thing. It's a shame — but there's no harm in filing a radar!
It might be possible to provide your own accessory view in -prepareSavePanel: and then override -fileTypeFromLastRunSavePanel to return the type selected. However, NSDocument's docs make no promises that it'll respect that, annoyingly. Try it and see!
if that doesn't work, it looks like you need to provide your own implementation of -runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo:. Follow Apple's description of the method:
The default implementation of this method first makes sure that any editor registered using Cocoa Bindings' NSEditorRegistration informal protocol has committed its changes, then creates a save panel, adds a standard "file format" accessory view if there is more than one file type for the user to choose from and [self shouldRunSavePanelWithAccessoryView] returns YES, sets various attributes of the panel, invokes [self prepareSavePanel:theSavePanel] to provide an opportunity for customization, then presents the panel. If the user OKs the panel -saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo: is invoked.
The docs for -writableTypesForSaveOperation: do note:
You can invoke this method when creating a custom save panel accessory view to easily present the same set of types that NSDocument would in its standard file format popup menu.

Related

How to show NSTextView-like context menu

I want to present NSTextView-like context menu for a certain NSString object (say, "text") each time -rightMouseDown-method of my custom NSResponder-subclass get called.
Where can I obtain these items for some NSString?
UPD
Created NSTextView instance and used its -menuForEvent:-method. Some of items was disabled (like 'copy' and so on), I reset its target value to custom object with overridden selectors (like -copy: and others).
NSTextView is an NSView subclass, which provides a class method +defaultMenu. So you can ask for NSMenu * menu = [NSTextView defaultMenu]; to get your very own copy to do with as you please. You may have to walk its structure and customize individual NSMenuItem instances to adjust their target/action but most (all?) should work just fine with nil-target (sends action to first responder) and their default action.
I must admit, however, I'm not sure what you mean by "Where can I obtain these items for some NSString?" The context menu is opened from some UI control (like a text view) and sends its action (like -checkSpelling...) to some target (like the first responder; which should be something like a text view, which acts as the view for a string or attributed string) to act upon.

Trying to see the delegate of an NSTextView of an NSCell

I am having a devil of a time trying to figure out how to get the address of the Text Field Editor (NSTextView) of an NSCell—NSFormCell and NSTextFieldCell in particular? NSCell does not have a property to access it. I did figure out the editor is not allocated until one is actually editing the field.
I want to set the delegate so I can capture keystrokes for auto-completion.
By default, there's a single field editor for each window. Even if a control or cell uses a custom field editor, it's still vended by the window. You would call -[NSWindow fieldEditor:forObject:] to obtain the field editor for a given control.
However, the delegate of the field editor is always set to the control on whose behalf it is working. Setting the delegate to something else is likely to break things. So, you would typically use a custom subclass of the control and implement your delegate methods there.
Finally, controlling completions is normally done using -textView:completions:forPartialWordRange:indexOfSelectedItem: in the text view delegate, not by capturing keystrokes.

Advice needed for developing multiple window Mac application

I’ve been reading through several books on Mac development, but cannot find the information I’m looking for.
The books all describe how to make floating windows or panes, but never mention how to make them all in one window. A simplified example of what I’m looking to create is shown below:
Basically, there will be three windows; A selector window with radio buttons to choose which NSDocument is currently being used, a window underneath that with buttons that show different windows to the right that allow viewing and manipulation of certain data.
For a example, each NSDocument may have a color value that can be set in the window shown by clicking view A, and some text strings that can be set in the window shown by clicking view B.
So the questions are:
Is it appropriate to use a single NSDocument sub-class for each Doc #1 and Doc #2?
Which classes should I use to set up the application as shown? NSWindowController? NSWindow? NSPanel?
I’m only looking for guidance on what to read up on, so any pointers are appreciated.
EDIT:
To clarify this further, I want to have a table view where the buttons are (View A & B), and by clicking them they will cause the other window/view to change it's contents.
It's like the split view in the iPad settings application, there is a table view on the left, and when it's pressed the right side changes.
The radio buttons are there only to illustrate that I want more than one Document. I'm guessing I need more than one to handle this? Or perhaps I should place them all in a single NSDocument? Somehow that doesn't seem right.
To achieve what you want you need one window (NSWindow), one window controller and various views each with their own view controller. There are several ways you could set this up, all depending on your requirements:
You'd have at least 3 views (instances of NSView): one for the selection of the document class, one for the view selection and one for the content. Each view is controlled by a view controller (instance of NSViewController). Additionally you can opt to wrap the views in split views (NSSplitView) so your user can resize the real estate available to each view.
You have one window with a window controller. If you choose a Document based app template in Xcode, Xcode will generate a subclass of NSDocument which you can use as your window controller (or choose to use Core Data and Xcode will generate a subclass of NSPersistentDocument with all bells and whistles you need to access Core Data for document persistency).
So to come back to your questions:
1: Yes, but depending on your requirements. If Doc #1 is a completely different thing than Doc #2 than you might need to re-evaluate. For example Doc #1 might have completely different persistent requirements than #2.
2: There's no single scenario here, but one that worked for me: Take the project template for a document based app (with or without Core Data). Use the generated subclass of NSDocument (or NSPersistentDocument) as your window controller. Use NSView to implement the views in your window where each view is managed by its own controller, which is an instance of NSViewController.
I know this is an old question, but a way to do it how you want would be to use: ContainerViews and set their embed segue to be the view controllers you want.

Accessing objects on one nib file from another nib file

I have two nib files - Main.nib and Preference.nib
In Main.nib file I have an instance of NSView class. Its window has a NSPopUpButton which on clicking shows a menu. In the menu I have show Preferences menu item.
Menu item on clicking shows a preferences panel containing a color well item.
On clicking color well a color panel is displayed to choose the color.
The problem is how to apply that color to main application window.
My preference panel window is in Preference.nib file.
So problem is accessing NSView from another Nib Window.
Is there a way so that I can make connection between preference panel and my main application window(NSView)
You're thinking about this at the wrong level. NSView and NSWindow are view objects in the Model-View-Controller pattern and shouldn't be used for holding application data. The color you select in your preference panel is application data and should be stored in an appropriate model object.
You could, for example, use bindings to bind the color well to the NSUserDefaultsController object to store that data (assuming this is an application-wide setting). You didn't say exactly what the color is used for in your main window, but if the object that uses it is bindings aware, you can bind that object to the same value on the NSUserDefaultsController and you're done.
Otherwise, you can respond to the color well's action message to store the color in an appropriate place and then send a notification using NSNotificationCenter to tell other objects that the color has changed. You'll need to sign up any object that needs to take action when the value changes for your notification message.
Here are some resources:
Here's an overview of the model-view-controller pattern that explains how Cocoa programs are structured
This is a high level explanation of how Cocoa bindings work
Here's a bunch of documents about using notifications

How to get notifications of NSView isHidden changes?

I am building a Cocoa desktop application. I want to know when a NSView's isHidden status has changed. So far using target/action doesn't help, and I can't find anything in NSNotification for this task. I would like to avoid overriding the setHidden method, because then I'll have to override all the NSView derived class that I am using.
UPDATE: I ended up using KVO. The path for "isHidden" is "hidden", probably because the setter is "setHidden".
You could use Key-Value Observing to observe the isHidden property of the NSView(s). When you receive a change notification from one of these views, you can check if it or one of its superviews is hidden with -isHiddenOrHasHiddenAncestor.
A word of warning: getting Key-Value Observing right is slightly tricky. I would highly recommend reading this post by Michael Ash, or using the -[NSObject gtm_addObserver:forKeyPath:selector:userInfo:options] method from the NSObject+KeyValueObserving category from the Google Toolbox for Mac.
More generally, one can override viewWillMoveToWindow: or the other related methods in NSView to tell when a view will actually be showing (i.e. it's window is in the window display list AND the view is not hidden). Thus the dependency on KVO for the 'hidden' key used above is removed, which only works if setIsHidden has been called on that view. In the override, 'window' (or [self window]) will indicate whether the view is being put into a visible view hierarchy (window is non-nil) or being taken out of it (window is nil).
I use it for example to start/stop a timer to update a control from online data periodically - when I only want to update while the control is visible.
Could you override the setter method for the hidden property so that it will trigger some custom notification within your application?

Resources