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];
}
}
Related
How would one do this? I know just merging NSData doesn't work.
Playing with NSAttributedString should work, something like the code shown below
This is a very quick and dirty way to merge many RTF files together
- (void)mergeRTF:(NSURL*)rtf1 :(NSURL*)rtf2 :(NSURL*)merged {
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithUnsignedInteger:NSUTF8StringEncoding]
forKey:NSCharacterEncodingDocumentOption];
NSDictionary *docAttrs = nil;
NSError* error = nil;
NSAttributedString *rtfText1 = [[NSAttributedString alloc] initWithURL:rtf1
options:options
documentAttributes:&docAttrs
error:&error];
NSAttributedString *rtfText2 = [[NSAttributedString alloc] initWithURL:rtf2
options:options
documentAttributes:&docAttrs
error:&error];
NSMutableAttributedString* whole = [[NSMutableAttributedString alloc] initWithAttributedString:rtfText1];
[whole appendAttributedString:rtfText2];
NSData* data = [whole RTFFromRange:NSMakeRange(0, whole.length) documentAttributes:nil];
[data writeToURL:merged atomically:YES];
}
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!
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];
}
I have a UIPickerViewDelegate that allows the user to select a term from the picker and see the term's definition by using NSDictionary to access data stored in a plist file. I also have a MFMailComposeViewController to allow the user to email the term and it's definition elsewhere.
But I can't seem to get the term and definition formatted properly for entry into the email. I looked at various "convert NSDictionary to NSString" solutions, but none seem to offer what I need, including variations of Term = [dict objectForKey:#"Term"].
Under viewDidLoad, I have the following:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Glossary" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.termArray = [dict objectForKey:#"Term"];
self.definitionArray = [dict objectForKey:#"Definition"];
My pickerView code includes this:
NSString *resultString = [[NSString alloc] initWithFormat:
#"%#",
[definitionArray objectAtIndex:row]];
definitionLabel.text = resultString;
NSString *termString = [[NSString alloc] initWithFormat:
#"%#",
[termArray objectAtIndex:row]];
The code for showEmail contains:
int definitionIndex = [self.termPicker selectedRowInComponent:0];
NSString *emailTitle = [NSString stringWithFormat:#"Definition of %#", termLabel];
NSString *emailDefinition = [definitionArray objectAtIndex:definitionIndex];
NSString *messageBody = [NSString stringWithFormat:#"The definition of <B>%#</B> is:<P> <B>%#</B>", termLabel, emailDefinition];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:YES];
I would like the email title and message body to pull from the Term selected in the picker and the Definition associated with it. I'm sure it must be a simple formatting issue, but I can't figure it out.
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.