btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / PrintJob.cpp
blobed7affbf634c4136102218cb1b5df1f00583be65
1 /*
2 * Copyright 2001-2009, Haiku.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * I.R. Adema
7 * Stefano Ceccherini (burton666@libero.it)
8 * Michael Pfeiffer
9 * julun <host.haiku@gmx.de>
13 #include <PrintJob.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include <Alert.h>
20 #include <Application.h>
21 #include <Button.h>
22 #include <Debug.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <FindDirectory.h>
26 #include <Messenger.h>
27 #include <NodeInfo.h>
28 #include <OS.h>
29 #include <Path.h>
30 #include <Region.h>
31 #include <Roster.h>
32 #include <SystemCatalog.h>
33 #include <View.h>
35 #include <AutoDeleter.h>
36 #include <pr_server.h>
37 #include <ViewPrivate.h>
39 using BPrivate::gSystemCatalog;
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "PrintJob"
44 #undef B_TRANSLATE
45 #define B_TRANSLATE(str) \
46 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "PrintJob")
49 /*! Summary of spool file:
51 |-----------------------------------|
52 | print_file_header |
53 |-----------------------------------|
54 | BMessage print_job_settings |
55 |-----------------------------------|
56 | |
57 | ********** (first page) ********* |
58 | * * |
59 | * _page_header_ * |
60 | * ----------------------------- * |
61 | * |---------------------------| * |
62 | * | BPoint where | * |
63 | * | BRect bounds | * |
64 | * | BPicture pic | * |
65 | * |---------------------------| * |
66 | * |---------------------------| * |
67 | * | BPoint where | * |
68 | * | BRect bounds | * |
69 | * | BPicture pic | * |
70 | * |---------------------------| * |
71 | ********************************* |
72 | |
73 | ********* (second page) ********* |
74 | * * |
75 | * _page_header_ * |
76 | * ----------------------------- * |
77 | * |---------------------------| * |
78 | * | BPoint where | * |
79 | * | BRect bounds | * |
80 | * | BPicture pic | * |
81 | * |---------------------------| * |
82 | ********************************* |
83 |-----------------------------------|
85 BeOS R5 print_file_header.version is 1 << 16
86 BeOS R5 print_file_header.first_page is -1
88 each page can consist of a collection of picture structures
89 remaining pages start at _page_header_.next_page of previous _page_header_
91 See also: "How to Write a BeOS R5 Printer Driver" for description of spool
92 file format: http://haiku-os.org/documents/dev/how_to_write_a_printer_driver
96 struct _page_header_ {
97 int32 number_of_pictures;
98 off_t next_page;
99 int32 reserved[10];
100 } _PACKED;
103 static void
104 ShowError(const char* message)
106 BAlert* alert = new BAlert(B_TRANSLATE("Error"), message, B_TRANSLATE("OK"));
107 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
108 alert->Go();
112 // #pragma mark -- PrintServerMessenger
115 namespace BPrivate {
118 class PrintServerMessenger {
119 public:
120 PrintServerMessenger(uint32 what, BMessage* input);
121 ~PrintServerMessenger();
123 BMessage* Request();
124 status_t SendRequest();
126 void SetResult(BMessage* result);
127 BMessage* Result() const { return fResult; }
129 static status_t GetPrintServerMessenger(BMessenger& messenger);
131 private:
132 void RejectUserInput();
133 void AllowUserInput();
134 void DeleteSemaphore();
135 static status_t MessengerThread(void* data);
137 uint32 fWhat;
138 BMessage* fInput;
139 BMessage* fRequest;
140 BMessage* fResult;
141 sem_id fThreadCompleted;
142 BAlert* fHiddenApplicationModalWindow;
146 } // namespace BPrivate
149 using namespace BPrivate;
152 // #pragma mark -- BPrintJob
155 BPrintJob::BPrintJob(const char* jobName)
157 fPrintJobName(NULL),
158 fSpoolFile(NULL),
159 fError(B_NO_INIT),
160 fSetupMessage(NULL),
161 fDefaultSetupMessage(NULL),
162 fAbort(0),
163 fCurrentPageHeader(NULL)
165 memset(&fSpoolFileHeader, 0, sizeof(print_file_header));
167 if (jobName != NULL && jobName[0])
168 fPrintJobName = strdup(jobName);
170 fCurrentPageHeader = new _page_header_;
171 if (fCurrentPageHeader != NULL)
172 memset(fCurrentPageHeader, 0, sizeof(_page_header_));
176 BPrintJob::~BPrintJob()
178 CancelJob();
180 free(fPrintJobName);
181 delete fSetupMessage;
182 delete fDefaultSetupMessage;
183 delete fCurrentPageHeader;
187 status_t
188 BPrintJob::ConfigPage()
190 PrintServerMessenger messenger(PSRV_SHOW_PAGE_SETUP, fSetupMessage);
191 status_t status = messenger.SendRequest();
192 if (status != B_OK)
193 return status;
195 delete fSetupMessage;
196 fSetupMessage = messenger.Result();
197 _HandlePageSetup(fSetupMessage);
199 return B_OK;
203 status_t
204 BPrintJob::ConfigJob()
206 PrintServerMessenger messenger(PSRV_SHOW_PRINT_SETUP, fSetupMessage);
207 status_t status = messenger.SendRequest();
208 if (status != B_OK)
209 return status;
211 delete fSetupMessage;
212 fSetupMessage = messenger.Result();
213 if (!_HandlePrintSetup(fSetupMessage))
214 return B_ERROR;
216 fError = B_OK;
217 return B_OK;
221 void
222 BPrintJob::BeginJob()
224 fError = B_ERROR;
226 // can not start a new job until it has been commited or cancelled
227 if (fSpoolFile != NULL || fCurrentPageHeader == NULL)
228 return;
230 // TODO show alert, setup message is required
231 if (fSetupMessage == NULL)
232 return;
234 // create spool file
235 BPath path;
236 status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
237 if (status != B_OK)
238 return;
240 char *printer = _GetCurrentPrinterName();
241 if (printer == NULL)
242 return;
243 MemoryDeleter _(printer);
245 path.Append(printer);
247 char mangledName[B_FILE_NAME_LENGTH];
248 _GetMangledName(mangledName, B_FILE_NAME_LENGTH);
250 path.Append(mangledName);
251 if (path.InitCheck() != B_OK)
252 return;
254 // TODO: fSpoolFileName should store the name only (not path which can be
255 // 1024 bytes long)
256 strlcpy(fSpoolFileName, path.Path(), sizeof(fSpoolFileName));
257 fSpoolFile = new BFile(fSpoolFileName, B_READ_WRITE | B_CREATE_FILE);
259 if (fSpoolFile->InitCheck() != B_OK) {
260 CancelJob();
261 return;
264 // add print_file_header
265 // page_count is updated in CommitJob()
266 // on BeOS R5 the offset to the first page was always -1
267 fSpoolFileHeader.version = 1 << 16;
268 fSpoolFileHeader.page_count = 0;
269 fSpoolFileHeader.first_page = (off_t)-1;
271 if (fSpoolFile->Write(&fSpoolFileHeader, sizeof(print_file_header))
272 != sizeof(print_file_header)) {
273 CancelJob();
274 return;
277 // add printer settings message
278 if (!fSetupMessage->HasString(PSRV_FIELD_CURRENT_PRINTER))
279 fSetupMessage->AddString(PSRV_FIELD_CURRENT_PRINTER, printer);
281 _AddSetupSpec();
282 _NewPage();
284 // state variables
285 fAbort = 0;
286 fError = B_OK;
290 void
291 BPrintJob::CommitJob()
293 if (fSpoolFile == NULL)
294 return;
296 if (fSpoolFileHeader.page_count == 0) {
297 ShowError(B_TRANSLATE("No pages to print!"));
298 CancelJob();
299 return;
302 // update spool file
303 _EndLastPage();
305 // write spool file header
306 fSpoolFile->Seek(0, SEEK_SET);
307 fSpoolFile->Write(&fSpoolFileHeader, sizeof(print_file_header));
309 // set file attributes
310 app_info appInfo;
311 be_app->GetAppInfo(&appInfo);
312 const char* printerName = "";
313 fSetupMessage->FindString(PSRV_FIELD_CURRENT_PRINTER, &printerName);
315 BNodeInfo info(fSpoolFile);
316 info.SetType(PSRV_SPOOL_FILETYPE);
318 fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PAGECOUNT, B_INT32_TYPE, 0,
319 &fSpoolFileHeader.page_count, sizeof(int32));
320 fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_DESCRIPTION, B_STRING_TYPE, 0,
321 fPrintJobName, strlen(fPrintJobName) + 1);
322 fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PRINTER, B_STRING_TYPE, 0,
323 printerName, strlen(printerName) + 1);
324 fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_STATUS, B_STRING_TYPE, 0,
325 PSRV_JOB_STATUS_WAITING, strlen(PSRV_JOB_STATUS_WAITING) + 1);
326 fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_MIMETYPE, B_STRING_TYPE, 0,
327 appInfo.signature, strlen(appInfo.signature) + 1);
329 delete fSpoolFile;
330 fSpoolFile = NULL;
331 fError = B_ERROR;
333 // notify print server
334 BMessenger printServer;
335 if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
336 return;
338 BMessage request(PSRV_PRINT_SPOOLED_JOB);
339 request.AddString("JobName", fPrintJobName);
340 request.AddString("Spool File", fSpoolFileName);
342 BMessage reply;
343 printServer.SendMessage(&request, &reply);
347 void
348 BPrintJob::CancelJob()
350 if (fSpoolFile == NULL)
351 return;
353 fAbort = 1;
354 BEntry(fSpoolFileName).Remove();
355 delete fSpoolFile;
356 fSpoolFile = NULL;
360 void
361 BPrintJob::SpoolPage()
363 if (fSpoolFile == NULL)
364 return;
366 if (fCurrentPageHeader->number_of_pictures == 0)
367 return;
369 fSpoolFileHeader.page_count++;
370 fSpoolFile->Seek(0, SEEK_END);
371 if (fCurrentPageHeaderOffset) {
372 // update last written page_header
373 fCurrentPageHeader->next_page = fSpoolFile->Position();
374 fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
375 fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
376 fSpoolFile->Seek(fCurrentPageHeader->next_page, SEEK_SET);
379 _NewPage();
383 bool
384 BPrintJob::CanContinue()
386 // Check if our local error storage is still B_OK
387 return fError == B_OK && !fAbort;
391 void
392 BPrintJob::DrawView(BView* view, BRect rect, BPoint where)
394 if (fSpoolFile == NULL)
395 return;
397 if (view == NULL)
398 return;
400 if (view->LockLooper()) {
401 BPicture picture;
402 _RecurseView(view, B_ORIGIN - rect.LeftTop(), &picture, rect);
403 _AddPicture(picture, rect, where);
404 view->UnlockLooper();
409 BMessage*
410 BPrintJob::Settings()
412 if (fSetupMessage == NULL)
413 return NULL;
415 return new BMessage(*fSetupMessage);
419 void
420 BPrintJob::SetSettings(BMessage* message)
422 if (message != NULL)
423 _HandlePrintSetup(message);
425 delete fSetupMessage;
426 fSetupMessage = message;
430 bool
431 BPrintJob::IsSettingsMessageValid(BMessage* message) const
433 char* printerName = _GetCurrentPrinterName();
434 if (printerName == NULL)
435 return false;
437 const char* name = NULL;
438 // The passed message is valid if it contains the right printer name.
439 bool valid = message != NULL
440 && message->FindString("printer_name", &name) == B_OK
441 && strcmp(printerName, name) == 0;
443 free(printerName);
444 return valid;
448 // Either SetSettings() or ConfigPage() has to be called prior
449 // to any of the getters otherwise they return undefined values.
450 BRect
451 BPrintJob::PaperRect()
453 if (fDefaultSetupMessage == NULL)
454 _LoadDefaultSettings();
456 return fPaperSize;
460 BRect
461 BPrintJob::PrintableRect()
463 if (fDefaultSetupMessage == NULL)
464 _LoadDefaultSettings();
466 return fUsableSize;
470 void
471 BPrintJob::GetResolution(int32* xdpi, int32* ydpi)
473 if (fDefaultSetupMessage == NULL)
474 _LoadDefaultSettings();
476 if (xdpi != NULL)
477 *xdpi = fXResolution;
479 if (ydpi != NULL)
480 *ydpi = fYResolution;
484 int32
485 BPrintJob::FirstPage()
487 return fFirstPage;
491 int32
492 BPrintJob::LastPage()
494 return fLastPage;
498 int32
499 BPrintJob::PrinterType(void*) const
501 BMessenger printServer;
502 if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
503 return B_COLOR_PRINTER; // default
505 BMessage reply;
506 BMessage message(PSRV_GET_ACTIVE_PRINTER);
507 printServer.SendMessage(&message, &reply);
509 int32 type;
510 if (reply.FindInt32("color", &type) != B_OK)
511 return B_COLOR_PRINTER; // default
513 return type;
517 // #pragma mark - private
520 void
521 BPrintJob::_RecurseView(BView* view, BPoint origin, BPicture* picture,
522 BRect rect)
524 ASSERT(picture != NULL);
526 BRegion region;
527 region.Set(BRect(rect.left, rect.top, rect.right, rect.bottom));
528 view->fState->print_rect = rect;
530 view->AppendToPicture(picture);
531 view->PushState();
532 view->SetOrigin(origin);
533 view->ConstrainClippingRegion(&region);
535 if (view->ViewColor() != B_TRANSPARENT_COLOR) {
536 rgb_color highColor = view->HighColor();
537 view->SetHighColor(view->ViewColor());
538 view->FillRect(rect);
539 view->SetHighColor(highColor);
542 if ((view->Flags() & B_WILL_DRAW) != 0) {
543 view->fIsPrinting = true;
544 view->Draw(rect);
545 view->fIsPrinting = false;
548 view->PopState();
549 view->EndPicture();
551 BView* child = view->ChildAt(0);
552 while (child != NULL) {
553 if (!child->IsHidden()) {
554 BPoint leftTop(view->Bounds().LeftTop() + child->Frame().LeftTop());
555 BRect printRect(rect.OffsetToCopy(rect.LeftTop() - leftTop)
556 & child->Bounds());
557 if (printRect.IsValid())
558 _RecurseView(child, origin + leftTop, picture, printRect);
560 child = child->NextSibling();
563 if ((view->Flags() & B_DRAW_ON_CHILDREN) != 0) {
564 view->AppendToPicture(picture);
565 view->PushState();
566 view->SetOrigin(origin);
567 view->ConstrainClippingRegion(&region);
568 view->fIsPrinting = true;
569 view->DrawAfterChildren(rect);
570 view->fIsPrinting = false;
571 view->PopState();
572 view->EndPicture();
577 void
578 BPrintJob::_GetMangledName(char* buffer, size_t bufferSize) const
580 snprintf(buffer, bufferSize, "%s@%" B_PRId64, fPrintJobName,
581 system_time() / 1000);
585 void
586 BPrintJob::_HandlePageSetup(BMessage* setup)
588 setup->FindRect(PSRV_FIELD_PRINTABLE_RECT, &fUsableSize);
589 setup->FindRect(PSRV_FIELD_PAPER_RECT, &fPaperSize);
591 // TODO verify data type (taken from libprint)
592 int64 valueInt64;
593 if (setup->FindInt64(PSRV_FIELD_XRES, &valueInt64) == B_OK)
594 fXResolution = (short)valueInt64;
596 if (setup->FindInt64(PSRV_FIELD_YRES, &valueInt64) == B_OK)
597 fYResolution = (short)valueInt64;
601 bool
602 BPrintJob::_HandlePrintSetup(BMessage* message)
604 _HandlePageSetup(message);
606 bool valid = true;
607 if (message->FindInt32(PSRV_FIELD_FIRST_PAGE, &fFirstPage) != B_OK)
608 valid = false;
610 if (message->FindInt32(PSRV_FIELD_LAST_PAGE, &fLastPage) != B_OK)
611 valid = false;
613 return valid;
617 void
618 BPrintJob::_NewPage()
620 // init, write new page_header
621 fCurrentPageHeader->next_page = 0;
622 fCurrentPageHeader->number_of_pictures = 0;
623 fCurrentPageHeaderOffset = fSpoolFile->Position();
624 fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
628 void
629 BPrintJob::_EndLastPage()
631 if (!fSpoolFile)
632 return;
634 if (fCurrentPageHeader->number_of_pictures == 0)
635 return;
637 fSpoolFileHeader.page_count++;
638 fSpoolFile->Seek(0, SEEK_END);
639 if (fCurrentPageHeaderOffset) {
640 fCurrentPageHeader->next_page = 0;
641 fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
642 fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
643 fSpoolFile->Seek(0, SEEK_END);
648 void
649 BPrintJob::_AddSetupSpec()
651 fSetupMessage->Flatten(fSpoolFile);
655 void
656 BPrintJob::_AddPicture(BPicture& picture, BRect& rect, BPoint& where)
658 ASSERT(fSpoolFile != NULL);
660 fCurrentPageHeader->number_of_pictures++;
661 fSpoolFile->Write(&where, sizeof(BRect));
662 fSpoolFile->Write(&rect, sizeof(BPoint));
663 picture.Flatten(fSpoolFile);
667 /*! Returns a copy of the applications default printer name or NULL if it
668 could not be obtained. Caller is responsible to free the string using
669 free().
671 char*
672 BPrintJob::_GetCurrentPrinterName() const
674 BMessenger printServer;
675 if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
676 return NULL;
678 const char* printerName = NULL;
680 BMessage reply;
681 BMessage message(PSRV_GET_ACTIVE_PRINTER);
682 if (printServer.SendMessage(&message, &reply) == B_OK)
683 reply.FindString("printer_name", &printerName);
685 if (printerName == NULL)
686 return NULL;
688 return strdup(printerName);
692 void
693 BPrintJob::_LoadDefaultSettings()
695 BMessenger printServer;
696 if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
697 return;
699 BMessage message(PSRV_GET_DEFAULT_SETTINGS);
700 BMessage* reply = new BMessage;
702 printServer.SendMessage(&message, reply);
704 // Only override our settings if we don't have any settings yet
705 if (fSetupMessage == NULL)
706 _HandlePrintSetup(reply);
708 delete fDefaultSetupMessage;
709 fDefaultSetupMessage = reply;
713 void BPrintJob::_ReservedPrintJob1() {}
714 void BPrintJob::_ReservedPrintJob2() {}
715 void BPrintJob::_ReservedPrintJob3() {}
716 void BPrintJob::_ReservedPrintJob4() {}
719 // #pragma mark -- PrintServerMessenger
722 namespace BPrivate {
725 PrintServerMessenger::PrintServerMessenger(uint32 what, BMessage *input)
727 fWhat(what),
728 fInput(input),
729 fRequest(NULL),
730 fResult(NULL),
731 fThreadCompleted(-1),
732 fHiddenApplicationModalWindow(NULL)
734 RejectUserInput();
738 PrintServerMessenger::~PrintServerMessenger()
740 DeleteSemaphore();
741 // in case SendRequest could not start the thread
742 delete fRequest; fRequest = NULL;
743 AllowUserInput();
747 void
748 PrintServerMessenger::RejectUserInput()
750 fHiddenApplicationModalWindow = new BAlert("bogus", "app_modal", "OK");
751 fHiddenApplicationModalWindow->DefaultButton()->SetEnabled(false);
752 fHiddenApplicationModalWindow->SetDefaultButton(NULL);
753 fHiddenApplicationModalWindow->SetFlags(fHiddenApplicationModalWindow->Flags() | B_CLOSE_ON_ESCAPE);
754 fHiddenApplicationModalWindow->MoveTo(-65000, -65000);
755 fHiddenApplicationModalWindow->Go(NULL);
759 void
760 PrintServerMessenger::AllowUserInput()
762 fHiddenApplicationModalWindow->Lock();
763 fHiddenApplicationModalWindow->Quit();
767 void
768 PrintServerMessenger::DeleteSemaphore()
770 if (fThreadCompleted >= B_OK) {
771 sem_id id = fThreadCompleted;
772 fThreadCompleted = -1;
773 delete_sem(id);
778 status_t
779 PrintServerMessenger::SendRequest()
781 fThreadCompleted = create_sem(0, "print_server_messenger_sem");
782 if (fThreadCompleted < B_OK)
783 return B_ERROR;
785 thread_id id = spawn_thread(MessengerThread, "async_request",
786 B_NORMAL_PRIORITY, this);
787 if (id <= 0 || resume_thread(id) != B_OK)
788 return B_ERROR;
790 // Get the originating window, if it exists
791 BWindow* window = dynamic_cast<BWindow*>(
792 BLooper::LooperForThread(find_thread(NULL)));
793 if (window != NULL) {
794 status_t err;
795 while (true) {
796 do {
797 err = acquire_sem_etc(fThreadCompleted, 1, B_RELATIVE_TIMEOUT,
798 50000);
799 // We've (probably) had our time slice taken away from us
800 } while (err == B_INTERRUPTED);
802 // Semaphore was finally nuked in SetResult(BMessage *)
803 if (err == B_BAD_SEM_ID)
804 break;
805 window->UpdateIfNeeded();
807 } else {
808 // No window to update, so just hang out until we're done.
809 while (acquire_sem(fThreadCompleted) == B_INTERRUPTED);
812 status_t status;
813 wait_for_thread(id, &status);
815 return Result() != NULL ? B_OK : B_ERROR;
819 BMessage*
820 PrintServerMessenger::Request()
822 if (fRequest != NULL)
823 return fRequest;
825 if (fInput != NULL) {
826 fRequest = new BMessage(*fInput);
827 fRequest->what = fWhat;
828 } else
829 fRequest = new BMessage(fWhat);
831 return fRequest;
835 void
836 PrintServerMessenger::SetResult(BMessage* result)
838 fResult = result;
839 DeleteSemaphore();
840 // terminate loop in thread spawned by SendRequest
844 status_t
845 PrintServerMessenger::GetPrintServerMessenger(BMessenger& messenger)
847 messenger = BMessenger(PSRV_SIGNATURE_TYPE);
848 return messenger.IsValid() ? B_OK : B_ERROR;
852 status_t
853 PrintServerMessenger::MessengerThread(void* data)
855 PrintServerMessenger* messenger = static_cast<PrintServerMessenger*>(data);
857 BMessenger printServer;
858 if (messenger->GetPrintServerMessenger(printServer) != B_OK) {
859 ShowError(B_TRANSLATE("Print Server is not responding."));
860 messenger->SetResult(NULL);
861 return B_ERROR;
864 BMessage* request = messenger->Request();
865 if (request == NULL) {
866 messenger->SetResult(NULL);
867 return B_ERROR;
871 BMessage reply;
872 if (printServer.SendMessage(request, &reply) != B_OK
873 || reply.what != 'okok' ) {
874 messenger->SetResult(NULL);
875 return B_ERROR;
878 messenger->SetResult(new BMessage(reply));
879 return B_OK;
883 } // namespace BPrivate