Limit allowed operations to only NSDragOperationCopy - cocoa

I have a custom view that is a drag source. I want to limit the allowed drag operations to only a copy, and hence I return NSDragOperationCopy in my draggingSession:sourceOperationMaskForDraggingContext: method.
For some reason this has no effect.
There doesn't seem to be anything wrong with my code as returning NSDragOperationNone works as expected and blocks drags.
Below is the code from my NSView that is the dragging source. Dragging from the view to the "Trash" you'll see a NSDragOperationMove or NSDragOperationDelete even though I only want to allow the NSDragOperationCopy operation.
I've also uploaded a small demo project to demonstrate what I'm talking about: https://dl.dropbox.com/u/368222/test.zip.
- (void)mouseDown:(NSEvent *)event {
NSString *path = [[NSBundle mainBundle] resourcePath];
NSString *imagePath = [path stringByAppendingString:#"/image.png"];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
NSArray *file = [NSArray arrayWithObject:imagePath];
NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
[pboard setPropertyList:file forType:NSFilenamesPboardType];
[self dragImage:image at:NSZeroPoint offset:NSMakeSize(0, 0) event:event pasteboard:pboard source:self slideBack:NO];
}
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
return NSDragOperationCopy;
}
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation {
NSLog(#"Dragging session ended with operation: %li", operation);
}
Anyone?
Thx!

Related

How to draw EPS data on NSView

I'm struggling with the problem to draw an eps file on a NSView.
When I first load the eps file from a file and draw it with drawInRect: the image is displayed correctly. However, the image will not be drawn when I load it from an archive file.
I've prepared a dirty small example that you can copy/paste and try out. Create a new Cocoa App project and add this to the delegate method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Just a sample eps file
NSURL *url = [NSURL URLWithString: #"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];
// Encode data
NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
[coder encodeObject: epsImage forKey: #"image"];
[coder finishEncoding];
NSString *dataFile = [#"~/Desktop/image.data" stringByExpandingTildeInPath];
[mutableData writeToFile: dataFile atomically: YES];
// Decode data
NSData *data = [NSData dataWithContentsOfFile: dataFile];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
NSImage *loadedImage = [decoder decodeObjectForKey: #"image"];
// Draw image
NSRect rect;
rect.origin = NSZeroPoint;
rect.size = loadedImage.size;
NSView *view = [[NSApp mainWindow] contentView];
[view lockFocus];
[loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
[view unlockFocus];
}
To prove that the first loaded image draws correctly just change the line [loadedImage drawInRect:...] to [epsImage drawInRect:...].
I'm using NSKeyedArchiver and NSKeyedUnarchiver here for simulating encodeWithCoder: and initWithCoder:. So please focus on the fact that NSImage with NSEPSImageRep representation, which does not contain a preview (from a resource fork?) and loaded purely as eps commands, is not drawn on a NSView correctly.
Any help is appreciated.
Due to the way that cacheing works on NSImage, I've often found it more effective to actually grab the NSImageRep if I know what the type is.
In our code, we found that the most reliable way to save off images is in their original format, but that requires either saving off the data in its original format somewhere else, or requesting the data from the NSImageRep. Unfortunately, there's not a generic -(NSData*)data method of NSImageRep, so we ended up specifically checking for various types of NSImageRep and saving them off depending on what we knew them to be.
Fortunately, loading is simple, as NSImage::initWithData: will figure out the type based on the data.
Here's our long-standing code for doing this. Basically, it prefers PDF then EPS then it makes a TIFF of anything it doesn't understand.
+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
if (!image)
return nil;
NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
NSString *kindString=nil;
NSArray *reps = [image representations];
for (NSImageRep *rep in reps) {
if ([rep isKindOfClass: [NSPDFImageRep class]]) {
// we have a PDF, so save that
pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
newData = [doc dataRepresentation];
if (newData && ([newData length]<[pdfData length])) {
pdfData = newData;
}
break;
}
if ([rep isKindOfClass: [NSEPSImageRep class]]) {
epsData = [(NSEPSImageRep*)rep EPSRepresentation];
break;
}
}
if (pdfData) {
imageData=pdfData;
kindString= #"pdfImage";
} else if (epsData) {
imageData=epsData;
kindString=#"epsImage";
} else {
// make a big copy
NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
[image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
}
imageData = [image TIFFRepresentation];
kindString=#"tiffImage";
}
if (kindStringPtr)
*kindStringPtr=kindString;
return imageData;
}
Once we have the NSData*, it can be saved in a keyed archive, written to disk or whatever.
On the way back in, load in the NSData* and then
NSImage *image = [[NSImage alloc] initWithData: savedData];
and you should be all set.

Drag and drop from finder to WebView

I'm using a WebView in edit mode. I have implemented this method from WebUIDelegate Procotol:
- (void)webView:(WebView *)sender willPerformDragDestinationAction:(WebDragDestinationAction)action forDraggingInfo:(id < NSDraggingInfo >)draggingInfo
and use it to catch the drops of elements on my WebView. When I detect a file being dragged from outside my app, and containing a picture, I build in this method the img DOM element and add it to my document.
This works fine, but as the method's name implies, I am only informed that the drag will happen, and I have no control over it.
As the Finder always does file drag operation, what normally happens when dropping a file on a WebView in editing mode is the webview displays the path of the file.
I end up having the file path string added to my webview, and the image too, but I would like to prevent the text from being added.
Is there any way to configure this without subclassing webview?
I tried it and while it works, it breaks plenty of other things like caret moving for the drop and such.
Answering this myself for a change!
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
if ([sender draggingSource] == nil)
{
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
NSURL* fileURL;
fileURL=[NSURL URLFromPasteboard: [sender draggingPasteboard]];
NSArray *dragTypes = [NSArray arrayWithObject:NSFileContentsPboardType];
[[sender draggingPasteboard] declareTypes:dragTypes owner:nil];
NSImage *content = [[NSImage alloc] initWithContentsOfURL:fileURL];
[[sender draggingPasteboard] setData:[content TIFFRepresentation] forType:NSPasteboardTypeTIFF];
}
}
return [super performDragOperation:sender];
}
Actually, what I did was indeed to subclass the WebView and intercept the performDragOperation to change the content of the dragging pasteboard, if the dragging source is outside of my app and doesn't contain already an image but only a filename.
I ran into the same issue.
What I found is that subclassing the view is the best place for this to insert the image data into the pasteboard. Here is how I am doing it for multiple files:
- (BOOL) performDragOperation:(id<NSDraggingInfo>)sender {
if ( [sender draggingSource] == nil ) {
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *classes = #[ [NSURL class] ];
NSDictionary *options = #{ NSPasteboardURLReadingFileURLsOnlyKey: [NSNumber numberWithBool:YES],
NSPasteboardURLReadingContentsConformToTypesKey: [NSImage imageTypes] };
NSArray *fileURLs = [pboard readObjectsForClasses:classes options:options];
if ( fileURLs ) {
NSMutableArray *images = [NSMutableArray arrayWithCapacity:[fileURLs count]];
for ( NSURL *fileURL in fileURLs )
[images addObject:[[NSImage alloc] initWithContentsOfURL:fileURL]];
[pboard declareTypes:[NSImage imageTypes] owner:nil];
[pboard clearContents]; [pboard writeObjects:images];
}
} return [super performDragOperation:sender];
}
What I noticed is the following sequence:
1. WebView captures drag operation.
2. Internal WebCore created document fragment
3. Node is inserted into a DOMRange
4. Editing Delegate is called
5. Lastly UI Delegate is called where it is too late to do anything
Also, I suggest setting the following via the UI Delegate:
- (NSUInteger) webView:(WebView *)webView dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo {
return WebDragDestinationActionEdit;
}
Ok, now the ISSUE I am running into and I really hope you might have an answer for me. When I select one file no problem. When I select multiple files, I get them and add all of them into the pasteboard properly. Even when I get to (5) for the UIDelegate and inspect the draggingPasteboard for its count I get what is expected. But unfortunately the document fragment is only being created once and likewise only one node gets inserted.
Any ideas how to get multiple fragments to be created so that they can all be inserted?
Thank you in advance.
Fixed version for the previous replies, this code works for multiple images dragged in the web view.
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
if ( [sender draggingSource] == nil )
{
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *classes = #[ [NSURL class] ];
NSDictionary *options = #{ NSPasteboardURLReadingFileURLsOnlyKey: [NSNumber numberWithBool:YES],
NSPasteboardURLReadingContentsConformToTypesKey: [NSImage imageTypes] };
NSArray *fileURLs = [pboard readObjectsForClasses:classes options:options];
if(fileURLs)
{
NSArray* filenames = [pboard propertyListForType: NSFilenamesPboardType];
NSMutableString* html = [NSMutableString string];
for(NSString* filename in filenames) {
[html appendFormat: #"<img src=\"%#\"/>", [[[NSURL alloc] initFileURLWithPath: filename] absoluteString]];
}
[pboard declareTypes: [NSArray arrayWithObject: NSHTMLPboardType] owner: self];
[pboard setString: html forType: NSHTMLPboardType];
}
} return [super performDragOperation:sender];
}

copy and paste text + image from NSPasteboard

we are working on a C++ Qt applciation that copies selected text and/or images from external applications, modifies it and then paste it back. Since we are on Mac, we are doing this part with Objective-C.
We are having problems trying to get an image from the Pasteboard. It works fine for text, but we are not sure about how to handle images or combination of text+image.
Since we dont know what the user might select, we should be able to perform a generic retrieval of content of the pasteboard to modify it and putting it back in the pasteboard.
We've try this:
//we thought about retrieving some generic item from pasteboard, using NSPasteboardItem
NSArray *classes = [[NSArray alloc] initWithObjects:[NSPasteboardItem class], nil];
NSDictionary *options = [NSDictionary dictionary];
NSArray *auxArray = [[NSPasteboard generalPasteboard] readObjectsForClasses:classes options:options];
NSData *archived_data = [auxArray objectAtIndex:0];
Our solution for handling text was:
NSString *text = [[NSPasteoard generalPasteboard] stringForType:NSStringPboardType];
string text_str = string([text UTF8String]);
It didnt work, so, How can we get the user selection from the pasteboard?
We need to get the raw bytes or rtf content in order to modify it as we need, and then putting it back in the pasteboard and paste it back replacing the original user selection.
Thanks!
I think this function will help you
- (IBAction)paste:sender
{
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classArray = [NSArray arrayWithObject:[NSImage class]];
NSDictionary *options = [NSDictionary dictionary];
BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
if (ok)
{
NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
NSImage *image = [objectsToPaste objectAtIndex:0];
[imageView setImage:image];
}
}

Force plaintext copy from a Cocoa WebView

I have a Cocoa Webview subclass, and I need to make all text copied from it be plaintext only. I have tried overriding -copy and -pasteboardTypesForSelection, but no luck, and debugging code seems to indicate that those methods are never called. I've also tried setting -webkit-user-modify to read-write-plaintext-only in the css (this would also work in this situation) but that seemed to have no effect.
Any ideas?
Okay this seems to work (with the subclass instance as its own editing delegate):
- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
{
if (command == #selector(copy:)) {
NSString *markup = [[self selectedDOMRange] markupString];
NSData *data = [markup dataUsingEncoding: NSUTF8StringEncoding];
NSNumber *n = [NSNumber numberWithUnsignedInteger: NSUTF8StringEncoding];
NSDictionary *options = [NSDictionary dictionaryWithObject:n forKey: NSCharacterEncodingDocumentOption];
NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data options:options documentAttributes: NULL];
NSString *selectedString = [as string];
[as autorelease];
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
NSArray *objectsToCopy = [NSArray arrayWithObject: selectedString];
[pasteboard writeObjects:objectsToCopy];
return YES;
}
return NO;
}
Not sure if this is the best way.

Can't use two "audioPlayerDidFinishPlaying"

Can you please tell me how to fix this? I want to release two different AVAudioPlayer when they finish playing, but separately.
Here's my code:
.h File
#interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
NSString *path;
}
- (IBAction)Short:(id)sender;
- (IBAction)BeatLong:(id)sender;
.m File
AVAudioPlayer *media;
AVAudioPlayer *media2;
- (IBAction)Short:(id)sender
{
path = [[NSBundle mainBundle] pathForResource:#"Short" ofType:#"wav"];
media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[media setDelegate:self];
[media play];
}
- (IBAction)Beat:(id)sender
{
path = [[NSBundle mainBundle] pathForResource:#"Beat" ofType:#"mp3"];
media2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[media2 setDelegate:self];
[media2 play];
}
(Open the image in a new tab to see it better ^^,)
Nay, that's illegal on the grounds of the language.
You have to distinguish the different players according to the AVAudioPlayer * pointer
submitted with the message.
If you just want to release it, just write
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)aPlayer successfully:(BOOL)flag
{
[aPlayer release];
}
and you're done and illegal too, since you don't own aPlayer.
But a better solution would be to detect which audio player you own and release it.
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)aPlayer successfully:(BOOL)flag
{
if ( aPlayer == self.media )
[self.media release];
else if ( aPlayer == self.media2 )
[self.media2 release];
// other players cannot be released, since we don't know anything about their owner.
}

Resources