xref: /haiku/src/kits/interface/PrintJob.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
1 /*
2  * Copyright 2001-2006, Haiku.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  				I.R. Adema
7  				Stefano Ceccherini (burton666@libero.it)
8  				Michael Pfeiffer
9  */
10 
11 // TODO refactor (avoid code duplications, decrease method sizes)
12 
13 #include <Alert.h>
14 #include <Application.h>
15 #include <Button.h>
16 #include <Debug.h>
17 #include <Entry.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <Messenger.h>
21 #include <NodeInfo.h>
22 #include <OS.h>
23 #include <Path.h>
24 #include <PrintJob.h>
25 #include <Roster.h>
26 #include <View.h>
27 
28 #include <pr_server.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 static const int kSemTimeOut = 50000;
35 
36 static const char *kPrintServerNotRespondingText = "Print Server is not responding.";
37 static const char *kNoPagesToPrintText = "No Pages to print!";
38 
39 // Summery of spool file format:
40 // See articel "How to Write a BeOS R5 Printer Driver" for description
41 // of spool file format:
42 // http://haiku-os.org/documents/dev/how_to_write_a_printer_driver
43 
44 // print_file_header header
45 //   On BeOS R5 header.version is 1 << 16 and
46 //   header.first_page is -1.
47 // BMessage          job_settings
48 // _page_header_     page_header
49 // followed by number_of_pictures:
50 //   BPoint   where
51 //   BRect    bounds
52 //   BPicture picture
53 // remaining pages start at page_header.next_page of previous page_header
54 
55 struct _page_header_ {
56 	int32 number_of_pictures;
57 	off_t next_page;
58 	int32 reserved[10];
59 };
60 
61 
62 static status_t
63 GetPrinterServerMessenger(BMessenger& messenger)
64 {
65 	messenger = BMessenger(PSRV_SIGNATURE_TYPE);
66 	return messenger.IsValid() ? B_OK : B_ERROR;
67 }
68 
69 
70 static void
71 ShowError(const char *message)
72 {
73 	BAlert* alert = new BAlert("Error", message, "OK");
74 	alert->Go();
75 }
76 
77 
78 namespace BPrivate {
79 
80 	class Configuration {
81 		public:
82 			Configuration(uint32 what, BMessage *input);
83 			~Configuration();
84 
85 			status_t SendRequest(thread_func function);
86 
87 			BMessage* Request();
88 
89 			void SetResult(BMessage* result);
90 			BMessage* Result() const { return fResult; }
91 
92 		private:
93 			void RejectUserInput();
94 			void AllowUserInput();
95 			void DeleteSemaphore();
96 
97 			uint32 fWhat;
98 			BMessage *fInput;
99 			BMessage *fRequest;
100 			BMessage *fResult;
101 			sem_id fThreadCompleted;
102 			BAlert *fHiddenApplicationModalWindow;
103 	};
104 
105 
106 	Configuration::Configuration(uint32 what, BMessage *input)
107 		: fWhat(what),
108 		fInput(input),
109 		fRequest(NULL),
110 		fResult(NULL),
111 		fThreadCompleted(-1),
112 		fHiddenApplicationModalWindow(NULL)
113 	{
114 		RejectUserInput();
115 	}
116 
117 
118 	Configuration::~Configuration()
119 	{
120 		DeleteSemaphore();
121 			// in case SendRequest could not start the thread
122 		delete fRequest; fRequest = NULL;
123 		AllowUserInput();
124 	}
125 
126 
127 	void
128 	Configuration::RejectUserInput()
129 	{
130 		BAlert* alert = new BAlert("bogus", "app_modal_dialog", "OK");
131 		fHiddenApplicationModalWindow = alert;
132 		alert->DefaultButton()->SetEnabled(false);
133 		alert->SetDefaultButton(NULL);
134 		alert->MoveTo(-65000, -65000);
135 		alert->Go(NULL);
136 	}
137 
138 
139 	void
140 	Configuration::AllowUserInput()
141 	{
142 		fHiddenApplicationModalWindow->Lock();
143 		fHiddenApplicationModalWindow->Quit();
144 	}
145 
146 
147 	void
148 	Configuration::DeleteSemaphore()
149 	{
150 		if (fThreadCompleted >= B_OK) {
151 			sem_id id = fThreadCompleted;
152 			fThreadCompleted = -1;
153 			delete_sem(id);
154 		}
155 	}
156 
157 
158 	status_t
159 	Configuration::SendRequest(thread_func function)
160 	{
161 		fThreadCompleted = create_sem(0, "Configuration");
162 		if (fThreadCompleted < B_OK) {
163 			return B_ERROR;
164 		}
165 
166 		thread_id id = spawn_thread(function, "async_request", B_NORMAL_PRIORITY, this);
167 		if (id <= 0 || resume_thread(id) != B_OK) {
168 			return B_ERROR;
169 		}
170 
171 		// Code copied from BAlert::Go()
172 		BWindow* window = dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
173 			// Get the originating window, if it exists
174 
175 		// Heavily modified from TextEntryAlert code; the original didn't let the
176 		// blocked window ever draw.
177 		if (window != NULL) {
178 			status_t err;
179 			for (;;) {
180 				do {
181 					err = acquire_sem_etc(fThreadCompleted, 1, B_RELATIVE_TIMEOUT,
182 										  kSemTimeOut);
183 						// We've (probably) had our time slice taken away from us
184 				} while (err == B_INTERRUPTED);
185 
186 				if (err == B_BAD_SEM_ID) {
187 					// Semaphore was finally nuked in SetResult(BMessage *)
188 					break;
189 				}
190 				window->UpdateIfNeeded();
191 			}
192 		} else {
193 			// No window to update, so just hang out until we're done.
194 			while (acquire_sem(fThreadCompleted) == B_INTERRUPTED);
195 		}
196 
197 		status_t status;
198 		wait_for_thread(id, &status);
199 
200 		return Result() != NULL ? B_OK : B_ERROR;
201 	}
202 
203 
204 	BMessage *
205 	Configuration::Request()
206 	{
207 		if (fRequest != NULL)
208 			return fRequest;
209 
210 		if (fInput != NULL) {
211 			fRequest = new BMessage(*fInput);
212 			fRequest->what = fWhat;
213 		} else
214 			fRequest = new BMessage(fWhat);
215 		return fRequest;
216 	}
217 
218 
219 	void
220 	Configuration::SetResult(BMessage *result)
221 	{
222 		fResult = result;
223 		DeleteSemaphore();
224 			// terminate loop in thread spawned by SendRequest
225 	}
226 
227 } // BPrivate
228 
229 
230 BPrintJob::BPrintJob(const char *job_name)
231 	:
232 	fPrintJobName(NULL),
233 	fPageNumber(0),
234 	fSpoolFile(NULL),
235 	fError(B_ERROR),
236 	fSetupMessage(NULL),
237 	fDefaultSetupMessage(NULL),
238 	fCurrentPageHeader(NULL)
239 {
240 	memset(&fCurrentHeader, 0, sizeof(fCurrentHeader));
241 
242 	if (job_name != NULL) {
243 		fPrintJobName = strdup(job_name);
244 	}
245 
246 	fCurrentPageHeader = new _page_header_;
247 	if (fCurrentPageHeader != NULL) {
248 		memset(fCurrentPageHeader, 0, sizeof(*fCurrentPageHeader));
249 	}
250 }
251 
252 
253 BPrintJob::~BPrintJob()
254 {
255 	CancelJob();
256 
257 	free(fPrintJobName);
258 	delete fSetupMessage;
259 	delete fDefaultSetupMessage;
260 	delete fCurrentPageHeader;
261 }
262 
263 
264 static status_t
265 ConfigPageThread(void *data)
266 {
267 	BPrivate::Configuration* configuration = static_cast<BPrivate::Configuration*>(data);
268 
269 	BMessenger printServer;
270 	if (GetPrinterServerMessenger(printServer) != B_OK) {
271 		ShowError(kPrintServerNotRespondingText);
272 		configuration->SetResult(NULL);
273 		return B_ERROR;
274 	}
275 
276 	BMessage *request = configuration->Request();
277 	if (request == NULL) {
278 		configuration->SetResult(NULL);
279 		return B_ERROR;
280 	}
281 
282 
283 	BMessage reply;
284 	if (printServer.SendMessage(request, &reply) != B_OK
285 		|| reply.what != 'okok') {
286 		configuration->SetResult(NULL);
287 		return B_ERROR;
288 	}
289 
290 	configuration->SetResult(new BMessage(reply));
291 	return B_OK;
292 }
293 
294 
295 status_t
296 BPrintJob::ConfigPage()
297 {
298 	BPrivate::Configuration configuration(PSRV_SHOW_PAGE_SETUP, fSetupMessage);
299 	status_t status = configuration.SendRequest(ConfigPageThread);
300 	if (status != B_OK)
301 		return status;
302 	delete fSetupMessage;
303 	fSetupMessage = configuration.Result();
304 	_HandlePageSetup(fSetupMessage);
305 	return B_OK;
306 }
307 
308 
309 static status_t
310 ConfigJobThread(void *data)
311 {
312 	BPrivate::Configuration* configuration = static_cast<BPrivate::Configuration*>(data);
313 
314 	BMessenger printServer;
315 	if (GetPrinterServerMessenger(printServer) != B_OK) {
316 		ShowError(kPrintServerNotRespondingText);
317 		configuration->SetResult(NULL);
318 		return B_ERROR;
319 	}
320 
321 	BMessage *request = configuration->Request();
322 	if (request == NULL) {
323 		configuration->SetResult(NULL);
324 		return B_ERROR;
325 	}
326 
327 
328 	BMessage reply;
329 	if (printServer.SendMessage(request, &reply) != B_OK
330 		|| reply.what != 'okok') {
331 		configuration->SetResult(NULL);
332 		return B_ERROR;
333 	}
334 
335 	configuration->SetResult(new BMessage(reply));
336 	return B_OK;
337 }
338 
339 status_t
340 BPrintJob::ConfigJob()
341 {
342 	BPrivate::Configuration configuration(PSRV_SHOW_PRINT_SETUP, fSetupMessage);
343 	status_t status = configuration.SendRequest(ConfigJobThread);
344 	if (status != B_OK)
345 		return status;
346 	delete fSetupMessage;
347 	fSetupMessage = configuration.Result();
348 	_HandlePrintSetup(fSetupMessage);
349 	return B_OK;
350 }
351 
352 
353 void
354 BPrintJob::BeginJob()
355 {
356 	if (fSpoolFile != NULL) {
357 		// can not start a new job until it has been commited or cancelled
358 		return;
359 	}
360 	if (fCurrentPageHeader == NULL) {
361 		return;
362 	}
363 
364 	if (fSetupMessage == NULL) {
365 		// TODO show alert, setup message is required
366 		return;
367 	}
368 
369 	// create spool file
370 	BPath path;
371 	status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
372 	if (status != B_OK)
373 		return;
374 
375 	char *printer = _GetCurrentPrinterName();
376 	if (printer == NULL)
377 		return;
378 
379 	path.Append(printer);
380 	free(printer);
381 
382 	char mangledName[B_FILE_NAME_LENGTH];
383 	_GetMangledName(mangledName, B_FILE_NAME_LENGTH);
384 
385 	path.Append(mangledName);
386 
387 	if (path.InitCheck() != B_OK)
388 		return;
389 
390 	// TODO fSpoolFileName should store the name only (not path which can be 1024 bytes long)
391 	strncpy(fSpoolFileName, path.Path(), sizeof(fSpoolFileName));
392 	fSpoolFile = new BFile(fSpoolFileName, B_READ_WRITE | B_CREATE_FILE);
393 
394 	if (fSpoolFile->InitCheck() != B_OK) {
395 		CancelJob();
396 		return;
397 	}
398 
399 	// add print_file_header
400 	// page_count is updated in CommitJob()
401 	fCurrentHeader.version = 1 << 16;
402 	fCurrentHeader.page_count = 0;
403 	// on BeOS R5 the offset to the first page
404 	// was always -1.
405 	fCurrentHeader.first_page = (off_t)-1;
406 
407 	if (fSpoolFile->Write(&fCurrentHeader, sizeof(fCurrentHeader)) != sizeof(fCurrentHeader)) {
408 		CancelJob();
409 		return;
410 	}
411 
412 	// add printer settings message
413 	if (!fSetupMessage->HasString(PSRV_FIELD_CURRENT_PRINTER))
414 		fSetupMessage->AddString(PSRV_FIELD_CURRENT_PRINTER, printer);
415 	_AddSetupSpec();
416 
417 	// prepare page header
418 	// number_of_pictures is updated in DrawView()
419 	// next_page is updated in SpoolPage()
420 	fCurrentPageHeaderOffset = fSpoolFile->Position();
421 	fCurrentPageHeader->number_of_pictures = 0;
422 
423 	// state variables
424 	fAbort = 0;
425 	fPageNumber = 0;
426 	fError = B_OK;
427 	return;
428 }
429 
430 
431 void
432 BPrintJob::CommitJob()
433 {
434 	if (fSpoolFile == NULL) {
435 		return;
436 	}
437 
438 	if (fPageNumber <= 0) {
439 		ShowError(kNoPagesToPrintText);
440 		CancelJob();
441 		return;
442 	}
443 
444 	if (fCurrentPageHeader->number_of_pictures > 0) {
445 		SpoolPage();
446 	}
447 
448 	// update spool file
449 	_EndLastPage();
450 
451 	// set file attributes
452  	app_info appInfo;
453 	be_app->GetAppInfo(&appInfo);
454 	const char* printerName = "";
455 	fSetupMessage->FindString(PSRV_FIELD_CURRENT_PRINTER, &printerName);
456 
457 	BNodeInfo info(fSpoolFile);
458 	info.SetType(PSRV_SPOOL_FILETYPE);
459 
460 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PAGECOUNT, B_INT32_TYPE, 0, &fPageNumber, sizeof(int32));
461 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_DESCRIPTION, B_STRING_TYPE, 0, fPrintJobName, strlen(fPrintJobName) + 1);
462 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PRINTER, B_STRING_TYPE, 0, printerName, strlen(printerName) + 1);
463 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_STATUS, B_STRING_TYPE, 0, PSRV_JOB_STATUS_WAITING, strlen(PSRV_JOB_STATUS_WAITING) + 1);
464 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_MIMETYPE, B_STRING_TYPE, 0, appInfo.signature, strlen(appInfo.signature) + 1);
465 
466 	delete fSpoolFile;
467 	fSpoolFile = NULL;
468 	fError = B_ERROR;
469 
470 	// notify print server
471 	BMessenger printServer;
472 	if (GetPrinterServerMessenger(printServer) != B_OK) {
473 		return;
474 	}
475 
476 	BMessage request(PSRV_PRINT_SPOOLED_JOB);
477 	BMessage reply;
478 
479 	request.AddString("JobName", fPrintJobName);
480 	request.AddString("Spool File", fSpoolFileName);
481 	printServer.SendMessage(&request, &reply);
482 }
483 
484 void
485 BPrintJob::CancelJob()
486 {
487 	if (fSpoolFile == NULL) {
488 		return;
489 	}
490 
491 	fAbort = 1;
492 	BEntry(fSpoolFileName).Remove();
493 	delete fSpoolFile;
494 	fSpoolFile = NULL;
495 }
496 
497 
498 void
499 BPrintJob::SpoolPage()
500 {
501 	if (fSpoolFile == NULL) {
502 		return;
503 	}
504 
505 	// update page header
506 	fCurrentPageHeader->next_page = fSpoolFile->Position();
507 	fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
508 	fSpoolFile->Write(fCurrentPageHeader, sizeof(*fCurrentPageHeader));
509 	fSpoolFile->Seek(0, SEEK_END);
510 
511 	fCurrentPageHeader->number_of_pictures = 0;
512 }
513 
514 
515 bool
516 BPrintJob::CanContinue()
517 {
518 	// Check if our local error storage is still B_OK
519 	return fError == B_OK && !fAbort;
520 }
521 
522 
523 void
524 BPrintJob::DrawView(BView *view, BRect rect, BPoint where)
525 {
526 	if (fSpoolFile == NULL)
527 		return;
528 
529 	if (view == NULL)
530 		return;
531 
532 	if (view->LockLooper()) {
533 		BPicture picture;
534 		_RecurseView(view, where, &picture, rect);
535 		_AddPicture(picture, rect, where);
536 		view->UnlockLooper();
537 	}
538 }
539 
540 
541 BMessage *
542 BPrintJob::Settings()
543 {
544 	if (fSetupMessage == NULL) {
545 		return NULL;
546 	}
547 
548 	return new BMessage(*fSetupMessage);
549 }
550 
551 
552 void
553 BPrintJob::SetSettings(BMessage *message)
554 {
555 	if (message != NULL) {
556 		_HandlePrintSetup(message);
557 	}
558 	delete fSetupMessage;
559 	fSetupMessage = message;
560 }
561 
562 
563 bool
564 BPrintJob::IsSettingsMessageValid(BMessage *message) const
565 {
566 	char *printerName = _GetCurrentPrinterName();
567 	if (printerName == NULL) {
568 		return false;
569 	}
570 
571 	const char *name = NULL;
572 	// The passed message is valid if it contains the right printer name.
573 	bool valid = message != NULL
574 		&& message->FindString("printer_name", &name) == B_OK
575 		&& strcmp(printerName, name) == 0;
576 
577 	free(printerName);
578 
579 	return valid;
580 }
581 
582 
583 // Either SetSettings() or ConfigPage() has to be called prior
584 // to any of the getters otherwise they return undefined values.
585 BRect
586 BPrintJob::PaperRect()
587 {
588 	if (fDefaultSetupMessage == NULL)
589 		_LoadDefaultSettings();
590 	return fPaperSize;
591 }
592 
593 
594 BRect
595 BPrintJob::PrintableRect()
596 {
597 	if (fDefaultSetupMessage == NULL)
598 		_LoadDefaultSettings();
599 	return fUsableSize;
600 }
601 
602 
603 void
604 BPrintJob::GetResolution(int32 *xdpi, int32 *ydpi)
605 {
606 	if (fDefaultSetupMessage == NULL)
607 		_LoadDefaultSettings();
608 
609 	if (xdpi != NULL)
610 		*xdpi = fXResolution;
611 	if (ydpi != NULL)
612 		*ydpi = fYResolution;
613 }
614 
615 
616 int32
617 BPrintJob::FirstPage()
618 {
619     return fFirstPage;
620 }
621 
622 
623 int32
624 BPrintJob::LastPage()
625 {
626     return fLastPage;
627 }
628 
629 
630 int32
631 BPrintJob::PrinterType(void *) const
632 {
633 	BMessenger printServer;
634 	if (GetPrinterServerMessenger(printServer) != B_OK) {
635 		return B_COLOR_PRINTER; // default
636 	}
637 
638 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
639 	BMessage reply;
640 
641 	printServer.SendMessage(&message, &reply);
642 
643 	int32 type;
644 	if (reply.FindInt32("color", &type) != B_OK) {
645 		return B_COLOR_PRINTER; // default
646 	}
647 	return type;
648 }
649 
650 
651 #if 0
652 #pragma mark ----- PRIVATE -----
653 #endif
654 
655 
656 void
657 BPrintJob::_RecurseView(BView *view, BPoint origin,
658                        BPicture *picture, BRect rect)
659 {
660 	ASSERT(picture != NULL);
661 	// TODO: test what happens if views don't have
662 	// the B_WILL_DRAW flag or have B_DRAW_ON_CHILDREN
663 
664 	view->AppendToPicture(picture);
665 	view->f_is_printing = true;
666 	view->PushState();
667 	view->Draw(rect);
668 	view->PopState();
669 	view->f_is_printing = false;
670 	view->EndPicture();
671 
672 	BView *child = view->ChildAt(0);
673 	while (child != NULL) {
674 		// TODO: origin and rect should probably
675 		// be converted for children views in some way
676 		_RecurseView(child, origin, picture, rect);
677 		child = child->NextSibling();
678 	}
679 }
680 
681 
682 void
683 BPrintJob::_GetMangledName(char *buffer, size_t bufferSize) const
684 {
685 	snprintf(buffer, bufferSize, "%s@%lld", fPrintJobName, system_time() / 1000);
686 }
687 
688 
689 void
690 BPrintJob::_HandlePageSetup(BMessage *setup)
691 {
692 	setup->FindRect(PSRV_FIELD_PRINTABLE_RECT, &fUsableSize);
693 	setup->FindRect(PSRV_FIELD_PAPER_RECT, &fPaperSize);
694 
695 	// TODO verify data type (taken from libprint)
696 	int64 valueInt64;
697 	if (setup->FindInt64(PSRV_FIELD_XRES, &valueInt64) == B_OK) {
698 		fXResolution = (short)valueInt64;
699 	}
700 	if (setup->FindInt64(PSRV_FIELD_YRES, &valueInt64) == B_OK) {
701 		fYResolution = (short)valueInt64;
702 	}
703 }
704 
705 
706 bool
707 BPrintJob::_HandlePrintSetup(BMessage *message)
708 {
709 	_HandlePageSetup(message);
710 
711 	bool valid = true;
712 	if (message->FindInt32(PSRV_FIELD_FIRST_PAGE, &fFirstPage) != B_OK) {
713 		valid = false;
714 	}
715 	if (message->FindInt32(PSRV_FIELD_LAST_PAGE, &fLastPage) != B_OK) {
716 		valid = false;
717 	}
718 
719 	return valid;
720 }
721 
722 
723 void
724 BPrintJob::_NewPage()
725 {
726 	// write page header
727 	fCurrentPageHeaderOffset = fSpoolFile->Position();
728 	fSpoolFile->Write(fCurrentPageHeader, sizeof(*fCurrentPageHeader));
729 
730 	if (fPageNumber == 1)
731 		fCurrentHeader.first_page = fCurrentPageHeaderOffset;
732 
733 	fPageNumber ++;
734 }
735 
736 
737 void
738 BPrintJob::_EndLastPage()
739 {
740 	fSpoolFile->Seek(0, SEEK_SET);
741 	fCurrentHeader.page_count = fPageNumber;
742 	fSpoolFile->Write(&fCurrentHeader, sizeof(fCurrentHeader));}
743 
744 
745 void
746 BPrintJob::_AddSetupSpec()
747 {
748 	fSetupMessage->Flatten(fSpoolFile);
749 }
750 
751 
752 void
753 BPrintJob::_AddPicture(BPicture &picture, BRect &rect, BPoint &where)
754 {
755 	ASSERT(fSpoolFile != NULL);
756 
757 	if (fCurrentPageHeader->number_of_pictures == 0) {
758 		_NewPage();
759 	}
760 	fCurrentPageHeader->number_of_pictures ++;
761 
762 	fSpoolFile->Write(&where, sizeof(where));
763 	fSpoolFile->Write(&rect, sizeof(rect));
764 	picture.Flatten(fSpoolFile);
765 }
766 
767 
768 // Returns a copy of the current printer name
769 // or NULL if it ccould not be obtained.
770 // Caller is responsible to free the string using free().
771 char *
772 BPrintJob::_GetCurrentPrinterName() const
773 {
774 	BMessenger printServer;
775 	if (GetPrinterServerMessenger(printServer)) {
776 		return NULL;
777 	}
778 
779 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
780 	BMessage reply;
781 
782 	const char *printerName = NULL;
783 
784 	if (printServer.SendMessage(&message, &reply) == B_OK) {
785 		reply.FindString("printer_name", &printerName);
786 	}
787 	if (printerName == NULL) {
788 		return NULL;
789 	}
790 	return strdup(printerName);
791 }
792 
793 
794 void
795 BPrintJob::_LoadDefaultSettings()
796 {
797 	BMessenger printServer;
798 	if (GetPrinterServerMessenger(printServer) != B_OK) {
799 		return;
800 	}
801 
802 	BMessage message(PSRV_GET_DEFAULT_SETTINGS);
803 	BMessage *reply = new BMessage;
804 
805 	printServer.SendMessage(&message, reply);
806 
807 	// Only override our settings if we don't have any settings yet
808 	if (fSetupMessage == NULL)
809 		_HandlePrintSetup(reply);
810 
811 	delete fDefaultSetupMessage;
812 	fDefaultSetupMessage = reply;
813 }
814 
815 
816 void BPrintJob::_ReservedPrintJob1() {}
817 void BPrintJob::_ReservedPrintJob2() {}
818 void BPrintJob::_ReservedPrintJob3() {}
819 void BPrintJob::_ReservedPrintJob4() {}
820