How to find out if print panel was closed successfully when printing from a PDFView - cocoa

I need to know if the PDFDocument of a PDFView has been printed successfully in order to do some housekeeping afterwards.
When printing a regular NSView from NSDocument, I can do
NSPrintOperation *op = [NSPrintOperation
printOperationWithView:myRegularPrintView
printInfo:self.printInfo];
[op setCanSpawnSeparateThread:NO]; // Because we want to clean up afterwards
[op setShowsPrintPanel:YES];
[self runModalPrintOperation:op
delegate:self
didRunSelector:#selector(documentDidRunModalPrintOperation:success:contextInfo:)
contextInfo:NULL];
In the documentDidRunModalPrintOperation callback I can do the housekeeping. But printing the content of a PDFView only works correctly if I call
[myPDFView printWithInfo:[NSPrintInfo sharedPrintInfo] autoRotate:YES];
So I see no way to run the print operation with a callback function that is going to be called when the print panel is closed.

Since macOS 10.7 there is a function in PDFDocument which returns a NSPrintOperation, so one can simply do
NSPrintOperation *op = [myPDFView.document printOperationForPrintInfo:self.printInfo scalingMode:kPDFPrintPageScaleToFit autoRotate:YES];
and then continue just like with a normal NSView and add a callback when calling runModalPrintOperation.

Related

Cocoa osx PDFView NSPrintOperation PrintPanel not showing page preview

In my app for Mac I have a webview that shows some html content. I create a PDFDocument from that webview and then I want to print that document. So I create a PDFView from the document and then I call the NSPrintOperation printOperationWithView. When showing the print panel all appears correct except the page preview which appears blank, but if I press the details button the panel is refreshed and the page preview appears correctly.
How can I solve this??
Need help please. Thanks in advance.
This is an example of my problem:
1- The print panel shows with a blank page preview. Next I press show details.
2- After refreshing the panel the page preview appears correctly.
This is my code:
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
[printInfo setTopMargin:0.0];
[printInfo setBottomMargin:0.0];
[printInfo setLeftMargin:0.0];
[printInfo setRightMargin:0.0];
[printInfo setHorizontalPagination:NSFitPagination];
[printInfo setVerticalPagination:NSAutoPagination];
[printInfo setVerticallyCentered:NO];
[printInfo setHorizontallyCentered:YES];
NSData *pdfFinal = [[[[webView mainFrame] frameView] documentView] dataWithPDFInsideRect:[[[webView mainFrame] frameView] documentView].frame];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfFinal];
PDFView *pdfView = [[PDFView alloc] init];
[pdfView setDocument:doc];
NSPrintOperation *op;
op = [NSPrintOperation printOperationWithView:pdfView.documentView printInfo:printInfo];
[op setShowsProgressPanel:YES];
[op setShowsPrintPanel:YES];
[op runOperation];
From this link:
PDFDocument *doc = ...;
// Invoke private method.
// NOTE: Use NSInvocation because one argument is a BOOL type. Alternately, you could declare the method in a category and just call it.
BOOL autoRotate = NO; // Set accordingly.
NSMethodSignature *signature = [PDFDocument instanceMethodSignatureForSelector:#selector(getPrintOperationForPrintInfo:autoRotate:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:#selector(getPrintOperationForPrintInfo:autoRotate:)];
[invocation setArgument:&printInfo atIndex:2];
[invocation setArgument:&autoRotate atIndex:3];
[invocation invokeWithTarget:doc];
// Grab the returned print operation.
void *result;
[invocation getReturnValue:&result];
NSPrintOperation *op = (__bridge NSPrintOperation *)result;
[op setShowsPrintPanel:YES];
[op setShowsProgressPanel:YES];
[op runOperation];
This works on OSX from 10.4 to 10.10 (Yosemite).
EDIT: You can also see this answer which is similar, but with less lines of code.
At a guess, since PDFView is a subclass of NSView, whose designated initializer is -initWithFrame:, not -init, your call to PDFView *pdfView = [[PDFView alloc] init] may not be allowing the PDFView to set up its initial state, though subsequent calls to its machinery may be magically resolving this state for you but NSView and subclasses tend to behave strangely (particularly with respect to drawing) when you don't use the proper designated initializer (which means its frame and bounds are equal to NSZeroRect).
Try using -initWithFrame: with some reasonable, non-zero rectangle.
Update
Alright, just a wild shot in the dark, but the documentation for NSPrintOperation says that -runOperation blocks the main thread and recommends using -runOperationModalForWindow:delegate:didRunSelector:contextInfo: to avoid blocking the main thread entirely. Is it possible that by blocking the main thread something else is being prevented from doing its initial work (I'd call this either undocumented behavior or an API bug, but...)? Try implementing the modal version instead and see if that helps. If not, I'd actually file a bug report with Apple.

How update image from URL in OS X app?

i have some problem. So i have code which update song name and picture from php. Song name work and also updated but picture not work, in php file all work but in my project - no. How make update picture from url after 10 sec for example. Thanks.
-(void)viewWillDraw {
NSURL *artistImageURL = [NSURL URLWithString:#"http://site.ru/ParseDataField/kiss.php?image"];
NSImage *artistImage = [[NSImage alloc] initWithContentsOfURL:artistImageURL];
[dj setImage:artistImage];
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
NSError* error = nil;
NSString* text = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://site.ru/ParseDataField/kiss.php?artist"]
encoding:NSASCIIStringEncoding
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[labelName setStringValue:text];
});
});
}
You should really consider placing this code someplace other than -viewWillDraw. This routine can be called multiple times for the same NSView under some circumstances and, more importantly, you need to call [super viewWillDraw] to make sure that things will actually draw correctly (if anything is drawn in the view itself).
For periodic updates (such as every 10 seconds), you should consider using NSTimer to trigger the retrieval of the next object.
As for the general question of why your image isn't being drawn correctly, you should probably consider putting the image retrieval and drawing code into the same structure as your label retrieval and drawing code. This will get the [dj setImage: artistImage] method call outside of the viewWillDraw chain which is likely causing some difficulty here.

Animating an NSStatusItemView

Struggling with this one. I have a custom NSStatusItemView that I'm trying to animate. I've added the following code to my status item view to kick off the animation:
- (void)setAnimated
{
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"opacity"];
anim.duration = 1.0;
anim.repeatCount = HUGE_VALF;
anim.autoreverses = YES;
anim.fromValue=[NSNumber numberWithFloat:1.0];
anim.toValue=[NSNumber numberWithFloat:0.0];
[self.layer addAnimation: anim forKey: #"animateOpacity"];
[self setWantsLayer:YES];
[self setNeedsDisplay:YES];
}
When I call this method, nothing happens. Yet if I move this code to my drawRect method, then the view properly animates at launch. Not entirely sure what I need to do to be able to tell it to start animated after the fact but the above method is not doing it and I have no idea why! Any ideas?
Ok answer myself so the googles have record of the answer!
The problem was, kinda of, a lack of understanding of drawRect. When my setAnimated method calls setNeedsDisplay, it calls drawRect again, effectively undoing what is done in the setAnimated method.
There were two things I did to properly fix this. First, I modified the setAnimated method to accept a BOOL argument and set a isAnimated property on the view to that value. I then, in drawRect, check this BOOL value and do the animation if it is set to YES.
Secondly, it seems, you need to call [self setWantsLayer: YES] the first time the view is drawn. So I call this in drawRect the very first time it is run, so that later animation will work.

How to get the frame (origin, Size) of every visible windows on the active space?

I'm trying to figure out how to get the frame of all visible windows.
I tried the following code, but it only works for the app itself other windows report {0,0,0,0}
NSArray *windowArray = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications | NSWindowNumberListAllSpaces];
for(NSNumber *number in windowArray){
NSLog(#"Window number: %#", number);
NSWindow *window = [[NSApplication sharedApplication] windowWithWindowNumber:[number intValue]];
NSLog(#"Window: %#", NSStringFromRect( [[window contentView] frame]));
}
Sample code is appreciated.
I figured it out:
NSMutableArray *windows = (__bridge NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
for (NSDictionary *window in windows) {
NSString *name = [window objectForKey:#"kCGWindowName" ];
CGRect bounds;
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:#"kCGWindowBounds"], &bounds);
NSLog(#"%#: %#",name,NSStringFromRect(bounds));
}
You can't create an NSWindow for a window of another application. In general, you can't access the objects of other applications except through an interface that they cooperate with, like scripting.
You can get what you're looking for using the Quartz Window Services (a.k.a. CGWindowList) API.
I'm not at all sure that the window numbers returned by Cocoa are the same as the window numbers used by that API. In fact, the docs for -[NSWindow windowNumber] specifically say "note that this isn’t the same as the global window number assigned by the window server". I'm note sure to what use you can put the window numbers returned by +[NSWindow windowNumbersWithOptions:] which are not for your application's windows.

UIProgressView not displaying progress accurately

I am using UIProgresView for displaying downloading and in label the percentage of content downloads.The function is executing every time. When I displaying the progress value it is showing properly in the console. But it is not displaying on the screen, It is display at only 0% and 100%. I am using ASIHTTP framework too.
Please help in that.
CODE from comment:
for (float i=0; i< [topicNew count]; i++)
{
NSDictionary *new= [topicNew objectAtIndex:i];
NSString *imageName = [[[NSString alloc] initWithFormat:#"%#.%#.%#.png", appDelegate.subject,topicNamed,[new objectForKey:kWordTitleKey]] autorelease];
NSString *imagePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:imageName];
NSData *imageData = [self ParsingImagePath:[new objectForKey:kWordImagePathKey]];
[progressView setProgress:i/[topicNew count]];
[lblpercent setText:[[NSString stringWithFormat:#"%.0f",i/[topicNew count]] stringByAppendingString:#"%"]];
... More code here...
It looks like you are threadlocking your main thread. Meaning, you are updating the progressView but the mainthread never gets out of your for loop to display the updated change until the for loop is finished, which by then looks like your progress is set to 100%.
You need to either use a timer like (NSTimer) or some kind of background thread that processes your image files. Then you need to call your instance of UIProgressView from the background thread when you want to update your progress (end of the for loop for example)
float p = i/[topicNew count];
[progressView performSelectorOnMainThread:#selector(setProgress:)
withObject:[NSNumber numberWithFloat:p]
waitUntilDone:NO];
and pass in your updated progress to the progressView. Make sure you are not calling the progressView from the background thread directly, as UIKit is not thread safe. Only call it from your main thread, or using the performSelectorOnMainThread:withObject:waitUntilDOne: method.
Hopefully that gets you in the right directly

Resources