How do I Drag and Drop a '.txt' file in obj-c - cocoa

I'm trying to write some absolutely barebones code where I can drag a plain 'dot.txt' file onto an NSWindow and read in the data (and nothing fancier than that), but all the examples I've been able to find use images and NSViews etc.. Apple's 'Dragging File Contents' section in its "Drag and Drop Programming Topics for Cocoa" documentation confirms that dragging onto a plain NSWindow (rather than into an NSView etc.) is possible and seems to discuss exactly what I'm trying to do, but as a relative newbie I still find its reference to images and frames confusing.
Can anyone please help me get started by showing me where to 'registerForDraggedTypes' other than putting it in say, an 'initWithFrame' or 'initWithCoder' method, and what types to register for? Once I get the window to recognise my drag I can worry about the other 'performDragOperation' and 'draggingEntered' stuff later.
Thanks :-)

This is a part of code which I'm working on.
You can find this method when you created a new project.
-(void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
[window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}
I'm newbie too. Anyway, this worked for me.
And then, this is important.
We have to connect this object to window object to handle drop messages in this object
Open MainMenu.xib, and Interface Builder will pop up.
In Interface Builder, connect 'App Delegate'(this object) to 'delegate' outlet of 'Window'.
(You can find 'delegate' outlet in Inspector panel)
Now, 'App Delegate' receives event messages of 'Window'.
And then, adds more method at the same class file:
-(NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
{
return NSDragOperationGeneric;
}
-(BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
{
NSPasteboard* pbrd = [sender draggingPasteboard];
// Do something here.
return YES;
}

Related

NSWindow tracking

I would like to track each time a certain window appears (becomes visible to the user) in a OS X app. Where would be the most adequate place to call the tracker?
windowWillLoad, maybe?
I expected to find something like windowWillAppear but it seems I'm thinking too much iOS.
How about getting notification such as NSWindowDidBecomeMainNotification, By main I guess the one which is top most on screen directly visible by user.
see : Apple Documentation
Yes, one would expect that a window would notify its delegate or its controller with a windowWillAppear or windowDidAppear message, or post a documented notification like NSWindowDidAppearNotification. But alas, none of those exist. I filed a bug report with Apple and was given the advice to use a storyboard and a view controller instead. This is unhelpful in legacy apps that already use a bunch of window controllers and xibs.
You could subclass NSWindow and override orderWindow:relativeTo: to send a notification. Most, but not quite all, of the messages that make a window show itself ultimately go through this method, including orderBack:, orderFront:, makeKeyAndOrderFront:, and -[NSWindowController showWindow:]. But orderFrontRegardless does not go through orderWindow:relativeTo:, so you would also want to override that for completeness.
Another way to be notified is to make a subclass of NSViewController that controls some view that's always visible in the window. The view controller will receive viewWillAppear and viewDidAppear.
If you're subclassing NSWindow or NSViewController already for some other reason, either of these is a reasonable solution.
If you're not subclassing NSWindow already, and don't have an NSViewController subclass for a view that's always visible in the window, then another way is to use Cocoa bindings to connect the window's visible binding to a property one of your objects. For example, I have a custom NSWindowController subclass. I gave it a windowIsVisible property:
#interface MyWindowController ()
#property (nonatomic) BOOL windowIsVisible;
#end
and I implemented the accessors like this:
- (BOOL)windowIsVisible { return self.window.visible; }
- (void)setWindowIsVisible:(BOOL)windowIsVisible {
NSLog(#"window %# became %s", self.window, windowIsVisible ? "visible" : "hidden");
}
and in awakeFromNib, I bind the window's visible binding to the property like this:
- (void)awakeFromNib {
[super awakeFromNib];
[self.window bind:NSVisibleBinding toObject:self withKeyPath:NSStringFromSelector(#selector(windowIsVisible)) options:nil];
}
When the window becomes visible, the setWindowIsVisible: setter is called with an argument of YES. Note that if the whole app is hidden and reappears, the setter is called again, even though it wasn't called with argument NO when the app was hidden. So be careful not to assume the window was previously hidden.
Also, the binding might create a retain cycle, so you should probably unbind it when the window is closed, unless you want to keep the window and controller around. Note that the window does post NSWindowWillCloseNotification when it's closing, so you don't need any special magic to detect that.

Cocoa NSTextField Drag & Drop Requires Subclass... Really?

Until today, I've never had occasion to use anything other than an NSWindow itself as an NSDraggingDestination. When using a window as a one-size-fits-all drag destination, the NSWindow will pass those messages on to its delegate, allowing you to handle drops without subclassing NSWindow.
The docs say:
Although NSDraggingDestination is
declared as an informal protocol, the
NSWindow and NSView subclasses you
create to adopt the protocol need only
implement those methods that are
pertinent. (The NSWindow and NSView
classes provide private
implementations for all of the
methods.) Either a window object or
its delegate may implement these
methods; however, the delegate’s
implementation takes precedence if
there are implementations in both
places.
Today, I had a window with two NSTextFields on it, and I wanted them to have different drop behaviors, and I did not want to allow drops anywhere else in the window. The way I interpret the docs, it seems that I either have to subclass NSTextField, or make some giant spaghetti-conditional drop handlers on the window's delegate that hit-checks the draggingLocation against each view in order to select the different drop-area behaviors for each field.
The centralized NSWindow-delegate-based drop handler approach seems like it would be onerous in any case where you had more than a small handful of drop destination views. Likewise, the subclassing approach seems onerous regardless of the case, because now the drop handling code lives in a view class, so once you accept the drop you've got to come up with some way to marshal the dropped data back to the model. The bindings docs warn you off of trying to drive bindings by setting the UI value programmatically. So now you're stuck working your way back around that too.
So my question is: "Really!? Are those the only readily available options? Or am I missing something straightforward here?"
Thanks.
After a bit more research it appears that "Yes, really, your two options are to either subclass NSTextField or use your NSWindowDelegate to handle drops." I'll go further and make the claim that the better way of the two, for garden variety cases of, "I want multiple drop zones in a single window" is to use the NSWindowDelegate method with hit checks, since you avoid the issue of having your drop-handling code on the view side. I ended up with this draggingUpdated: method on my window delegate class:
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
if ([pboard.types containsObject: NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy))
{
NSView* hitView = [sender.draggingDestinationWindow.contentView hitTest: sender.draggingLocation];
if (hitView && (hitView == mSourceTextField || hitView == mDestTextField))
{
return NSDragOperationCopy;
}
}
return NSDragOperationNone;
}
Obviously there's more to the whole picture, but this hitTest:-based approach has worked for me so far. I suspect that this would be slightly more complex if one were working with a multi-NSCell based control like an NSTableView or NSOutlineView, but unsurprisingly, those have their own drag handling methods.
Hope this helps someone else.

NSWindow is not displaying

I am having a trouble try to display a NSWindow with out using Interface Builder. The initialization of the window was quite confusing since I am more familiar with iPhone (which does not have an NSWindow equivalent). So I searched Google for some code and I eventually found this:
NSRect windowRect = NSMakeRect(10.0f, 10.0f, 800.0f, 600.0f);
NSWindow *window = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:( NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask)
backing:NSBackingStoreBuffered defer:NO];
[window makeKeyAndOrderFront:nil];
So I copied that code and placed it in the applicationDidFinishLaunching and thought all would be good. But all is not good. Xcode did not display any errors (or warnings) in the Build Results. But, I do get this message in the display log:
2010-06-26 13:33:47.170 FooApp[283:a0f] Could not connect the action buttonPressed: to target of class NSApplication
I don't know how to interpret this as Google has failed me on searching for a solution on this display log error. And, as far as I can tell, I have no actions at the moment including a buttonPressed one. As a side note: I do not know if this is relevant or not, but I deleted the Main Window.xib and its accompanying property in the info.plist.
Any help would be greatly appreciated.
UPDATE: I tried doing some printf debugging (never really bothered learning NSLog) and the thing won't even printf if the thing is at the very beginning of the appliactionDidFinishLaunching or even worst, at the start of main (before the return if incase some of you are tempted to ask me if I put the printf before or after the return statement).
MainWindow.xib is part of the iphone App template, isn't it? What exactly did you delete? You still have the MainMenu.xib, right?
As you have discovered, having a nib file is not optional for a Cocoa app. You must have at least one nib (or xib, for you youngsters) and it must have a main menu in it.

resetting the image in an NSView

I think this is a very simple question, but I’m new to programming so I may be going about it in a wrong-headed way.
I have a basic understanding of Objective-C writing terminal applications and am teaching myself how to use the Cocoa GUI.
I understand how to use IBOutlet and IBAction to connect a simple button to a method that will repeatedly send random numbers to a textfield.
I understand how to add a NSView file, connect it to a custom view in interface builder and draw a path through random points in the view when the application launches.
(I’ve been putting this code inside the - (void)drawRect:(NSRect)dirtyRect method that is declared when the file is created).
What I can’t seem to figure out is how to connect a button to an action that will then ‘refresh’ the view – in this case repopulate it with another set of random points connected with a path. Looking at the documentation, I think I should somehow be using
– (void) setNeedsDisplay(BOOL)flag
but nothing I have tried so far had worked. Please tell me, what am I missing here?
Something like this:
- (IBAction)refreshButtonAction:(id)sender
{
[theView setNeedsDisplay:YES];
}
Connect your button to that action. "theView" is a reference to your custom NSView.

Document Based Application, preinitialize window (enter serial, buy, trial)

I need to create several windows before NSDocument is loaded, or create a window that blocks NSDocument window and top menu.
I tried several solutions - but they didn't work right.
modal window, one after another. there were some problems with Async URLConnection, and some other problems with my NSDocument content.
I created custom MainMenu.xib with no menu, that opens my preinitialize windows.
here i found some other problems, when a file(associated with my application) is opened - the Document Window initializes. Here i tried to subclass NSDocumentController, but i found no way to pause the "open document". (i want the document to be opened anyway, but only after the preinitalize windows would be closed).
So what is the right way to do this?
Implement applicationShouldOpenUntitledFile: in your app delegate to return NO if the user has to go through the not-registered-yet dialog first.
In the action methods for your “Trial” and “Confirm Registration” buttons, create the untitled document yourself (by sending the necessary message to the document controller).
So the right answer is to implement:
* application:openFiles:
* applicationShouldOpenUntitledFile:
and implement your own document creation. this is the way it worked for me.
MyDocument* document = [[MyDocument alloc]
initWithContentsOfURL:fileURL
ofType:[fileName pathExtension]
error:nil
];
if(document)
{
[[NSDocumentController sharedDocumentController] addDocument:document];
[document makeWindowControllers];
[document showWindows];
}
of course you need to write error handling code.

Resources