Cocoa Drag and Drop, reading back the data - cocoa

Ok, I have a NSOutlineView set up, and I want it to capture PDF's if a pdf is dragged into the NSOutlineView.
My first question, I have the following code:
[outlineView registerForDraggedTypes:[NSArray arrayWithObjects:NSStringPboardType, NSFilenamesPboardType, nil]];
In all the apple Docs and examples I've seen I've also seen something like MySupportedType being an object registered for dragging. What does this mean? Do I change the code to be:
[outlineView registerForDraggedTypes:[NSArray arrayWithObjects:#"pdf", NSStringPboardType, NSFilenamesPboardType, nil]];
Currently I have it set up to recognize drag and drop, and I can even make it spit out the URL of the dragged file once the drag is accepted, however, this leads me to my second question. I want to keep a copy of those PDF's app side. I suppose, and correct me if I'm wrong, that the best way to do this is to grab the data off the clipboard, save it in some persistant store, and that's that. (as apposed to using some sort of copy command and literally copying the file to the app director.)
That being said, I'm not sure how to do that. I've the code:
- (BOOL)outlineView:(NSOutlineView *)ov acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)childIndex
{
NSPasteboard *pboard = [info draggingPasteboard];
NSURL *fileURL;
if ( [[pboard types] containsObject:NSURLPboardType] ) {
fileURL = [NSURL URLFromPasteboard:pboard];
// Perform operation using the file’s URL
}
NSData *data = [pboard dataForType:#"NSPasteboardTypePDF"];
But this never actually gets any data. Like I said before, it does get the URL, just not the data.
Does anyone have any advise on how to get this going? Thanks so much!

Dragged Types
Dragged types are just strings that define a system pasteboard type (like NSFilenamesPboardType) or your app's own internal type (like "MyWidgetIdentifierType" to identify a widget by some internal ID).
A drag type of "PDF" doesn't get you anything. You might as well call it "Bob8374Type" ... if you don't give your app the ability to recognize the type (or nothing ever puts anything into the pasteboard for that type), it's utterly useless. You're working with dragged files, so NSFilenamesPboardType is correct.
NSPasteboardTypePDF won't help you unless there is NSPasteboardTypePDF data on the pasteboard. When files are dragged, you get NSFilenamesPboardType. Doesn't matter if the file is .pdf or .xyz; you're only getting paths.
Copy the File or Store a Path
You need to decide whether you intend to copy the dropped PDF or just store a path (or better yet, file system reference) to it. If you're going to copy the PDF, you'll need to make sure you're aware of the proper storage locations (like the Application Support folder, etc).
Assuming you really do want to copy the dropped pdfs somewhere, you don't need the PDF data. You can use NSFileManager to copy (or move) the file at the given path to a new location. If you have some other storage mechanism (ie, you want to suck the PDF data of the file into some other data structure), you can just get the PDF data directly using NSData's +dataWithContentsOfURL:options:error: and do with its data what you please.

Related

Register for global file drag events in Cocoa

I'm trying to be notified when a OS X user is dragging any file in OS X, not only in my app.
My current approach was using addGlobalMonitorForEventsMatchingMask:handler: on NSEvent, as follows:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask handler:^(NSEvent* event) {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSLog(#"%#", [pb propertyListForType:NSFilenamesPboardType]);
}];
This works partially - the handler is being called when I start dragging a file from my desktop or Finder, however it also is being called when I perform every other operation that contains a left-mouse-drag, e.g. moving a window. The issue is that the NSDragPboard still seems to contain the latest dragged file URL e.g. when I let off the file and start moving a window, which makes it hard to distinguish between these operations.
TL;DR - I am interested in file drag operations system-wide. I do not need any information about the dragged file itself, just the information that a file drag operation has been started or stopped. I would appreciate any hint to a possible solution for this question.
After having talked to Apple DTS, this is most likely a bug. I have filed rdar://25892115 for this issue. There currently seems to be no way to solve my original question with the given API.
To solve my problem, I am now using the Accessibility API to figure out if the item below the cursor is a file (kAXFilenameAttribute is not NULL).
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSArray* filenames = [pb propertyListForType:NSFilenamesPboardType];
NSInteger changeCount = pb.changeCount;
//when moving a window. the changeCount is not changed, use it to distinguish
if (filenames.count > 0 && self.lastChangeCount != changeCount){
self.lastChangeCount = changeCount;
//your code here
}

How do you display multiple files in a gui field on Mac OSX

Using a Windows file dialog if multiple files are selected they are double quote limited and separated by a space as follows:
"C:\MusicMatched2\Gold Greatest Hits" "C:\MusicMatched2\The Trials of Van Occupanther"
But is this what OSX does as well, I can't seem to find an example with applications on my mac, but I need to do the correct way to display multiple filenames in a simple textfield of a Gui application on a mac.
You're not supposed to present file paths to the user on the Mac. First of all, the user's view of the file hierarchy is not the same as the low-level view represented by paths. The Finder and the standard file dialogs maintain a consistent illusion of a file system hierarchy which doesn't actually exist. For example, each volume is a separate root in the Finder, but volumes other than the boot volume appear under /Volumes in the Unix file system. The Finder has a Computer view which has no representation in the Unix file system. Often, the Finder will represent the user's home folder as a independent root, even though the user can also usually get there from (boot drive) > Users > (user's account name).
There are other subtleties. For example, the names of various folders can be localized for display. However, they are never localized at the Unix path level. So, there will always be a folder whose path is /Applications, but it would be displayed as "Programme" on a German system.
Your app should not pierce this veil and expose users to the Unix-style paths of files.
There are APIs for obtaining the "display name" of a file or the array of display components to display (which is not the same as obtaining the display name of each element of the path). You could concatenate the display components with some separator string to make a text string for a path, but there's not really a standard separator.
The more correct approach is to use a "path control" (NSPathControl). This is a GUI widget that displays a file's location in the standard manner, with the standard separator, etc.
If you need to display multiple file locations, you might consider doing what the Finder does when you do a search. It shows the matches in a list and as you select them it shows the location of each in a path control below the list. You could also show a table with the display names in one column and a path control showing the location in another column.
A typical use of NSOpenPanel looks like this
-(void)showFileSelector {
NSOpenPanel *openpanel = [NSOpenPanel openPanel];
[openpanel setDelegate:self];
[openpanel setCanCreateDirectories:NO];
[openpanel setCanChooseFiles:YES];
[openpanel setAllowsMultipleSelection:YES];
[openpanel setCanChooseDirectories:NO];
[openpanel beginSheetModalForWindow:self.window
completionHandler:^(NSInteger result) {
if (result == NSOKButton) {
[self doSomethingWithURLS:openpanel.URLs];
}
}];
}
so in your case the most important switch is [openpanel setAllowsMultipleSelection:YES];
and you are going to want to make your object which presents the panel conform to NSOpenSavePanelDelegate (or some other object) then call
[openpanel setDelegate:self];
which has the following method available
- (void)panelSelectionDidChange:(id)sender where the sender is the panel
- (void)panelSelectionDidChange:(NSOpenPanel *)sender {
NSArray *urlsSelected = sender.URLs;
NSMutableArray *final = [#[] mutableCopy];
for (NSURL *url in urlsSelected) {
[final addObject:[NSString stringWithFormat:#"\"%#\"",[url path]]];
}
[self.labelToPresentIn setStringValue:[final componentsJoinedByString:#","];
}
So each time the selection changes your label will change.
Thats a explicit answer to how to do what you are asking. Where you choose to present the label with that info is up to you. NSOpenPanel has a property accessoryView which allows you to glue an arbitrary view onto the panel. Like this…
So for a panel with multiple selection enabled.
As the other commenters mention don't try and make your OS X app look and act like Windows. Windows is Windows and OS X is OS X. Different OS's , Different UX.
What you are asking would look a bit weird in OS X. But maybe you have a better idea of what you want.
Try and observe how other OS X apps use the save and open panels.

Core data app does not save my data, why?

I am writing my first core data app. For some reason, my edits are gone after quitting and restarting the app. Saving, closing window, and re-opening (without quitting) retains the data. If I open the saved file in textedit, I see my edited field. Something must go wrong during reading. Perhaps I forgot to change a setting somewhere? Does anyone recognize this behavior?
Are you saving the NSManagedObjectContext after making changes to your model objects?
Try
NSError *error;
if (![myContext save:&error]) {
// Handle error
}
The save message returns a BOOL for success. You should check that value to see if the save was successful. If not, the NSError reference that you pass in will contain information on why the save may have failed.
If you post your code, I might be able to help further.
The array controllers should have "prepares content" turned on. Otherwise, loading does not populate the array, and another save permentently removes the edits!

xCode ios How to write a mutable array to a file on the desktop?

I have a one-time need to create a file from an iOS mutable array. The array will be a short animated drawing.
That will be input to an app.
I write the array, and redraw it in the app, so I know that it's getting populated.
I've tried to do something as simple as this:
- (void) writeFile {
//CREATE FILE
NSLog(#"%s", __FUNCTION__);
[writeArray writeToURL:[NSURL fileURLWithPath:[#"~/Desktop/animation.data" stringByExpandingTildeInPath]] atomically:NO];
}
but I must be doing something wrong, as no file appears..
As the file is small (4-8K), maybe I should take a different approach?
Any help will be appreciated.
Of course this won't work on the device, but I assume you're just trying to work in the Simulator for some kind of testing. fileURLWithPath: doesn't expand ~. You need a full path here. None of the path searching routines is going to point into your user folder in any case, since that doesn't exist on iOS.

Drag-and-drop pasteboard data

I'm trying to write some simple code to drag-and-drop the contents of a text file onto a window. With some help from an earlier post and an Apple example I've now got the basics of drag-and-drop up and running.
Unfortunately however, Apple's sample code deals only with images. Can anyone please tell me how I'd modify their "pasteboard" method (shown below) to send the contents of a simple 'dot.txt' file?
- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type
{
//------------------------------------------------------
// method called by pasteboard to support promised drag types.
//--------------------------------------------------------
//sender has accepted the drag and now we need to send the data for the type we promised
if([type compare: NSTIFFPboardType]==NSOrderedSame)
{
//set data for TIFF type on the pasteboard as requested
[sender setData:[[self image] TIFFRepresentation] forType:NSTIFFPboardType];
}
else if([type compare: NSPDFPboardType]==NSOrderedSame)
{
[sender setData:[self dataWithPDFInsideRect:[self bounds]] forType:NSPDFPboardType];
}
}
Thanks :-)
Can anyone please tell me how I'd modify their "pasteboard" method (shown below) to send the contents of a simple 'dot.txt' file?
The caller is asking you to send data of a certain type. If you can provide data of that type, do so by putting it on the pasteboard. If you can't, do nothing.

Resources