NSOutlineView drag and drop: how to prevent parent being dropped on one of its children - cocoa

I'm setting up drag and drop for my NSOutlineView and I want to prevent parents being dropped on one of their children. The parent and children are all of the Group entity. I understand I need to do this in the outlineView:validatedrop method. I think I need to create an NSFetchRequest with the NSPredictate to get all the children of the parent, but I don't know what the predicate should be.
Edit: it should be recursively, so it should also get the children of children, if any.
Any suggestions?

- (NSDragOperation)outlineView:(NSOutlineView *)ov validateDrop:
(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)childIndex;
In this above method, you can manage the drag item can or can't accept. Please, return NSDragOperationNone, means prevent drop.

SOLVED:
Instead of digging into the core data objects, I just look at the NSTreeNodes instead, following Apple's sample code.
Bindings take care of updating the underlying core data model. Once again, if it looks too complicated, it's probably not the correct approach.

Related

RootViewController need to resort/reload data array xcode 4.3

I'm implementing an example, in that example, I read in data from a database, put it in an array, sort it, and it's displayed using the RootViewController. The DB read and array load happen before the RVC code. So, it works, I get the data in the window created by the RVC and there's a nav controller there as well.
I want to add a button or something to the nav controller so that when you hit it, it sends a value back to the RootViewController.m file, then based on that value, I want to resort the array and display it once again in the RootViewController window.
I'm not sure how to do this. What changes would I have to make to the .xib and the RootViewController.m file?
Please, I'm a confused nube. %-0 Thank you very much.
There's a fair amount to this, so I'll give some general points and if any of them cause problems, it may be easier to work out smaller details.
In you RVC's viewDidLoad method, you can create a button and set it as the right or left button in your controller's navigationItem.
You can associate a tap on that button with a method in your controller that can do whatever you want when the button is tapped. (But a button doesn't send values, really, so you may have to explain more about that idea.)
I assume the RVC has a table view if you're showing array contents, so once the array (mutable array, I'd assume) is re-sorted, you can tell the table view to reload its data.
In answer to your secondary question, once you have resorted your array (or generally updated your data however you wish) you can force the table view to reload programmatically
[tableView reloadData];
Where 'tableView' is your instance variable pointing to your table view

What is a NSBrowserTableView as compared to an NSBrowser?

I'm implementing a -(void)delete: method so I can handle the delete key in my Cocoa app. I want it to do different things depending on what's selected: for text-fields, I want the default behaviour (remove char to the left), but for NSBrowser items, I want it to delete the item.
I thought I would ask the Window for it's first responder, and then see if that first responder is equal to the pointer for my NSBrowser, but it never matched. When I debug it, I find that the firstResponder is pointing to an instance of NSBrowserTableView, but I can't find that in the documentation.
What is it?
And how else could I test to see if my firstResponder is a particular tableView? (I Thought of subclassing NSBrowser but I tend to avoid subclassing, and my second thought was to add a tag, but I like my first method best, if only the firstResponder would point to my NSBrowser instance when one of the items in the browser is selected. )
Thoughts?
Actually, #trudyscousin is only partially correct. This class is definitely not a subclass of NSBrowser.
NSBrowserTableView is a private subclass of NSTableView used by NSBrowser to display each column. The table view is used so there is a separate place to draw the branch image (the little arrow drawn next to folders) while leaving the rest of the row to be drawn by either the default or user-defined cell.
If you think about it, it actually makes sense that the table view (rather than the browser) be the first responder, because then the table for the active column gets first crack at responding to keystrokes, and NSBrowser can let NSTableView do what it already knows how to. (For example, jumping to the first row that matches a letter typed by the user.)
Fortunately, NSBrowserTableView has a pointer back to the browser it works for. You can access this via its -(NSBrowser*)browser method. I recommend you don't subclass NSBrowser for this particular case, since you'd have to have a deep knowledge of its private implementation to do anything useful.
You can't find that in the documentation because it's private. My guess is that, when you instantiate a NSBrowser or a NSTableView, you're actually instantiating a subclass of this private class, which itself is a subclass of NSControl (which is pointed out in the documentation as being the superclass of both NSBrowser and NSTableView). Another example is NSString represented as 'NSCFString,' which I take as an allusion to the fact that CFString and NSString are "toll-free bridged."
Take this with as many grains of salt as you wish, but the way I'd go about gaining insight into the first responder is inserting a NSLog statement in my code and breaking just beyond it, seeing what was printed in the log. You could set the view's tag and display that in the statement. Or you could ask for your first repsponder's class
NSStringFromClass([myFirstResponder class])
and display that.
Hope this helped.

Creating a view with draggable text elements

I am trying to create a view for a kind of brainstorming application like, for example, OmniGraffle, with elements that contain textviews and can be dragged around. (Also, the should be connectable with arrows, but that is not (yet) the problem)
I did my homework and searched via google and read books about cocoa, but there seems to be no similar example around.
Since I am also new to cocoa, I’m a bit helpless here.
The thing I am sure of is, that I need a custom view in which I can create my elements - what I tried until now to do that is:
First, I searched for the syntax to add subwindows to a window to create my elements. Subwindows, I imagined, would automatically be movable and come to front and so on.
The problem: As the experienced Cocoa-programmers of you probably are not surprised, I was stunned to find nothing about anything like that - this seems to be something, that is just not intended in Cocoa?!
Then I thought about creating subviews that contain a custom view for the title bar drawing (where the user can click to drag the element) and a NSTextView.
Problems:
I read, that it is not so clever to create dozens of subviews in a window because that would be very slow (or would that be not so bad in this case because all the subviews would be instances of always the same class?).
Also I can’t find out how to load a subview from a nib- or xib-file. Would I need a viewController? Or would that make the dozens-of-instances-problem even worse?
And Apple tells you not to overlap subviews (okay, that would be not so important, but I really wonder how the guys at OmniGroup made OmniGraffle...)
Because of that, I now wanted to do the title-bar-drawing in the surrounding custom view and create the textview programmatically (as I understand, a text-“view“ ist not really a view and takes its functionality from NSCell to reduce all the effort with the views?).
Problems:
Even that failed because I was not able to create a textview that doesn’t fill the complete window (the initWithFrame: of the [[NSScrollView alloc] initWithFrame: aRect] just seems to be ignored or do I get that wrong?).
Also, there should be some buttons on each element in the final application. I imagine that would be easier to accomplish with a subview from a nib-file for each element?
Well, now that nothing works and the more I read, the more problems seem to occur, I am pretty confused and frustrated.
How could I realize such a program? Could someone please push me in the right direction?
I created a class for the draggable elements where I save position, size and text in instance variables. In my view, every new element instance is added to an array (for now, this works without a controller). The array is used to draw all the elements in a loop in drawRect:. For the text of the element I just use a NSTextFieldCell which is set to the saved text from every element in the same loop.
That way it is also possible to overlap the elements.

How do I implement a customized list in Cocoa?

I want to build a Cocoa App with a list of entries very similar to the ToDo list of Things.app (see the screencast). The question is whether I should use
a TableView,
a CollectionView or
a WebView.
I think it could work with all of them, but which one suits the following requirements best?
have a list of entries -> 1 column & many rows
reordering with drag & drop
select single entries & use keys for actions like delete
open up an entry: the row should expand to show more input fields
customized look: rounded corners, shadow, background gradient
So far my research says that the TableView has most of the functionality, but is harder to customize in its appearance, the CollectionView doesn't have drag & drop (right?) but is easy to design and the WebView would take much effort to not hurt the user experience and I can't bind my model directly to input fields.
What pros and cons am I missing and what would you recommend to use?
A WebView doesn't make sense. You might as well create a web application if you use a WebView. An NSCollectionView is more for grid like data, like TV listings per hour.
NSTableView is the only one that makes sense in this case. I've implemented all 5 bullet points with with an NSTableView without issue. You need to extend NSTableView and do some custom drawing for the customized look. That's the hardest part.
open up an entry: the row should expand to show more input fields
You need an outline view. A table view is for flat lists.
Note that NSOutlineView is a subclass of NSTableView, so all the table-view features work on an outline view as well.
There are people who've done this already. One that I've used successfully is by Matteo Bertozzi and is available here: http://th30z.netsons.org/2009/03/cocoa-sidebar-with-badges-take-2/ It might take a bit of massaging to get it to work properly (especially if you need complex drag-and-drop behavior), but for basic functionality, such as getting the section titles and items in the list, it works excellently.
Edit: This has come up before and is a common question on the cocoa-dev email list. Here are some other options.
Just took a look at Things.app itself using "F-script anywhere".
They've used a subclass of NSTableView called "DetailTableView" which presents the condensed todo items. Collapsed todo items are implemented using a custom cell called "ToDoCell", but the expanded look you get when editing is interesting. In that case they've got a custom view called "ToDoEditView" which is inserted as a subview of the DetailTableView when required. I suspect this editing view is temporarily added as a subview in the correct location and the corresponding row of the tableview gets resized temporarily while it is present.
All pretty speculative .. I'd love to know the details of how this was done. It's an awesome UI.
I'm approaching the very same problem in my app (with one big list similar to the Things todo list) and I think a table view would make a lot of sense here.
The trick is having your cells ("rows") expand when double-clicked. That's about all the progress I've made so far.

NSTableView and NSOutlineView drag-and-drop

I have an NSTableView and an NSOutlineView, both with their content provided by bindings, that I'd like to have some drag-and-drop functionality:
Drag rows from Table A onto a row of Outline B, where they will be copied into a data structure which the row in Outline B represents.
Drag a row from Outline B onto another row in Outline B, which will copy the data represented by the first row into the data represented in the second row.
I've read Apple's drag-and-drop documentation and gotten just about nowhere. It doesn't really seem to apply to what I need to do. What am I missing?
The page you linked to is pretty clear about what you need to do. In table A's data source, implement registerForDraggedTypes: and tableView:writeRowsWithIndexes:toPasteboard: to put some private TableAPasteboardType data on the pasteboard.
In outline B's data source, implement the same two methods and put some private OutlineBPasteboardType data on the pasteboard.
Finally, implement tableView:validateDrop:proposedRow:proposedDropOperation: and tableView:acceptDrop:row:dropOperation: to check the pasteboard for either TableAPasteboardType or OutlineBPasteboardType and make the appropriate changes to your bound model, depending.
It's pretty straightforward once you just plow in and do it.
You need a data source—AFAIK, you can't make this happen with Bindings alone.
The unfinished Adium Xtras Creator, which is under the BSD license, includes an array controller that you can set as the data source to get drag-and-drop in a Bindings-powered table view.
This requirement may not apply to NSOutlineView and NSTreeController. I haven't tried that.
In MacOS 10.7 some new protocols were added to implement this.
There is a lack of documentation for tables at the moment but you can find some nice examples:
DragNDropOutlineView
SourceView
TableViewPlayground
For NSTableViwew the Protocol NSTableViewDataSource defines the following methods:
(BOOL)tableView:writeRowsWithIndexes:toPasteboard:
tableView:validateDrop:proposedRow:proposedDropOperation:
tableView:acceptDrop:row:dropOperation:
For NSOutlineView the Protocol NSOutlineViewDataSource defines the following methods:
(BOOL)outlineView:writeItems:toPasteboard:
(NSDragOperation)outlineView:validateDrop:proposedItem:proposedChildIndex:
(BOOL)outlineView:acceptDrop:item:childIndex:
These are the minimum requirements to implement for each view type.
The use cases are quite similar.
If the toPasteboard: method returns YES, the drag is started.
The validateDrop: method controls which target node is allowed by updating the marker in the view
Return YES for the acceptDrop: method if the drop was successful
This lead to two sub-usecases you have to manage. The first one is a drag & drop within the same view or the same operation between two views. Additionally you may distinguish between move, copy or delete operations. A nice example is how the breakpoints work with drag & drop in Xcode.
The tableView has some additional methods to customize drag & drop, but the ones I mentioned are the key methods to get it working.

Resources