xref: /haiku/src/libs/print/libprint/Preview.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
1 /*
2  * Copyright 2002-2008, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Pfeiffer
7  *		Hartmut Reh
8  *		julun <host.haiku@gmx.de>
9  */
10 
11 #include "Preview.h"
12 
13 #include "GraphicsDriver.h"
14 #include "PrintUtils.h"
15 
16 
17 #include <math.h>
18 #include <stdlib.h>
19 
20 
21 #include <Application.h>
22 #include <Button.h>
23 #include <Debug.h>
24 #include <Region.h>
25 #include <Screen.h>
26 #include <String.h>
27 #include <ScrollView.h>
28 #include <StringView.h>
29 #include <TextControl.h>
30 
31 
32 // #pragma mark - PreviewPage
33 
34 
35 PreviewPage::PreviewPage(int32 page, PrintJobPage* pjp)
36 	: fPage(page)
37 	, fStatus(B_ERROR)
38 	, fNumberOfPictures(0)
39 	, fRects(NULL)
40 	, fPoints(NULL)
41 	, fPictures(NULL)
42 {
43 	fNumberOfPictures = pjp->NumberOfPictures();
44 
45 	fRects = new BRect[fNumberOfPictures];
46 	fPoints = new BPoint[fNumberOfPictures];
47 	fPictures = new BPicture[fNumberOfPictures];
48 
49 	for (int32 i = 0; i < fNumberOfPictures; ++i) {
50 		fStatus = pjp->NextPicture(fPictures[i], fPoints[i], fRects[i]);
51 		if (fStatus != B_OK)
52 			break;
53 	}
54 }
55 
56 
57 PreviewPage::~PreviewPage()
58  {
59 	delete [] fRects;
60 	delete [] fPoints;
61 	delete [] fPictures;
62 }
63 
64 
65 status_t
66 PreviewPage::InitCheck() const
67 {
68 	return fStatus;
69 }
70 
71 
72 void
73 PreviewPage::Draw(BView* view, const BRect& printRect)
74 {
75 	ASSERT(fStatus == B_OK);
76 	for (int32 i = 0; i < fNumberOfPictures; i++)
77 		view->DrawPicture(&fPictures[i], printRect.LeftTop() + fPoints[i]);
78 }
79 
80 
81 // #pragma mark - PreviewView
82 
83 
84 namespace {
85 
86 const float kPreviewTopMargin		= 10.0;
87 const float kPreviewBottomMargin	= 30.0;
88 const float kPreviewLeftMargin		= 10.0;
89 const float kPreviewRightMargin		= 30.0;
90 
91 
92 // TODO share constant with JobData
93 const char *kJDOrientation		= "orientation";
94 const char *kJDNup				= "JJJJ_nup";
95 const char *kJDReverse			= "JJJJ_reverse";
96 const char* kJDPageSelection	= "JJJJ_page_selection";
97 
98 
99 const uint8 ZOOM_IN[] = { 16, 1, 6, 6, 0, 0, 15, 128, 48, 96, 32, 32, 66, 16,
100 	66, 16, 79, 144, 66, 16, 66, 16, 32, 32, 48, 112, 15, 184, 0, 28, 0, 14, 0,
101 	6, 0, 0, 15, 128, 63, 224, 127, 240, 127, 240, 255, 248, 255, 248, 255, 248,
102 	255, 248, 255, 248, 127, 248, 127, 248, 63, 252, 15, 254, 0, 31, 0, 15, 0, 7 };
103 
104 
105 const uint8 ZOOM_OUT[] = { 16, 1, 6, 6, 0, 0, 15, 128, 48, 96, 32, 32, 64, 16,
106 	64, 16, 79, 144, 64, 16, 64, 16, 32, 32, 48, 112, 15, 184, 0, 28, 0, 14, 0,
107 	6, 0, 0, 15, 128, 63, 224, 127, 240, 127, 240, 255, 248, 255, 248, 255, 248,
108 	255, 248, 255, 248, 127, 248, 127, 248, 63, 252, 15, 254, 0, 31, 0, 15, 0, 7 };
109 
110 
111 BRect
112 RotateRect(const BRect& rect)
113 {
114 	return BRect(rect.top, rect.left, rect.bottom, rect.right);
115 }
116 
117 
118 BRect
119 ScaleDown(BRect rect, float factor)
120 {
121 	rect.left /= factor;
122 	rect.top /= factor;
123 	rect.right /= factor;
124 	rect.bottom /= factor;
125 	return rect;
126 }
127 
128 
129 BPoint
130 CalulateOffset(int32 numberOfPagesPerPage, int32 index,
131 	JobData::Orientation orientation, BRect printableRect)
132 {
133 	BPoint offset(0.0, 0.0);
134 	if (numberOfPagesPerPage == 1)
135 		return offset;
136 
137 	float width  = printableRect.Width();
138 	float height = printableRect.Height();
139 
140 	switch (numberOfPagesPerPage) {
141 		case 2: {
142 			if (index == 1) {
143 				if (JobData::kPortrait == orientation)
144 					offset.x = width;
145 				else
146 					offset.y = height;
147 			}
148 		}	break;
149 
150 		case 8: {
151 			if (JobData::kPortrait == orientation) {
152 				offset.x = width  * (index / 2);
153 				offset.y = height * (index % 2);
154 			} else {
155 				offset.x = width  * (index % 2);
156 				offset.y = height * (index / 2);
157 			}
158 		}	break;
159 
160 		case 32: {
161 			if (JobData::kPortrait == orientation) {
162 				offset.x = width  * (index / 4);
163 				offset.y = height * (index % 4);
164 			} else {
165 				offset.x = width  * (index % 4);
166 				offset.y = height * (index / 4);
167 			}
168 		}	break;
169 
170 		case 4: {
171 		case 9:
172 		case 16:
173 		case 25:
174 		case 36:
175 		case 49:
176 		case 64:
177 		case 81:
178 		case 100:
179 		case 121:
180 			int32 value = int32(sqrt(double(numberOfPagesPerPage)));
181 			offset.x = width  * (index % value);
182 			offset.y = height * (index / value);
183 		}	break;
184 	}
185 	return offset;
186 }
187 
188 }
189 
190 
191 PreviewView::PreviewView(BFile* jobFile, BRect rect)
192 	: BView(rect, "PreviewView", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS)
193 	, fPage(0)
194 	, fZoom(0)
195 	, fReader(jobFile)
196 	, fReverse(false)
197 	, fPaperRect(BRect())
198 	, fPrintableRect(BRect())
199 	, fTracking(false)
200 	, fInsideView(true)
201 	, fScrollStart(0.0, 0.0)
202 	, fNumberOfPages(1)
203 	, fNumberOfPagesPerPage(1)
204 	, fCachedPage(NULL)
205 	, fOrientation(JobData::kPortrait)
206 	, fPageSelection(JobData::kAllPages)
207 {
208 	int32 value32;
209 	if (fReader.JobSettings()->FindInt32(kJDOrientation, &value32) == B_OK)
210 		fOrientation = (JobData::Orientation)value32;
211 
212 	if (fReader.JobSettings()->FindInt32(kJDPageSelection, &value32) == B_OK)
213 		fPageSelection = (JobData::PageSelection)value32;
214 
215 	bool value;
216 	if (fReader.JobSettings()->FindBool(kJDReverse, &value) == B_OK)
217 		fReverse = value;
218 
219 	if (fReader.JobSettings()->FindInt32(kJDNup, &value32) == B_OK)
220 		fNumberOfPagesPerPage = value32;
221 
222 	fNumberOfPages = (fReader.NumberOfPages() + fNumberOfPagesPerPage - 1)
223 		/ fNumberOfPagesPerPage;
224 
225 	if (fPageSelection == JobData::kOddNumberedPages)
226 		fNumberOfPages = (fNumberOfPages + 1) / 2;
227 	else if (fPageSelection == JobData::kEvenNumberedPages)
228 		fNumberOfPages /= 2;
229 
230 	fPaperRect = fReader.PaperRect();
231 	fPrintableRect = fReader.PrintableRect();
232 	switch (fNumberOfPagesPerPage) {
233 		case 2:
234 		case 8:
235 		case 32:
236 		case 128: {
237 			fPaperRect = RotateRect(fPaperRect);
238 			fPrintableRect = RotateRect(fPrintableRect);
239 		}	break;
240 	}
241 }
242 
243 
244 PreviewView::~PreviewView()
245 {
246 	delete fCachedPage;
247 }
248 
249 
250 void
251 PreviewView::Show()
252 {
253 	BView::Show();
254 	be_app->SetCursor(ZOOM_IN);
255 }
256 
257 
258 void
259 PreviewView::Hide()
260 {
261 	be_app->SetCursor(B_HAND_CURSOR);
262 	BView::Hide();
263 }
264 
265 
266 void
267 PreviewView::Draw(BRect rect)
268 {
269 	if (fReader.InitCheck() == B_OK) {
270 		_DrawPageFrame(rect);
271 		_DrawPage(rect);
272 		_DrawMarginFrame(rect);
273 	}
274 }
275 
276 
277 void
278 PreviewView::FrameResized(float width, float height)
279 {
280 	Invalidate();
281 	FixScrollbars();
282 }
283 
284 
285 void
286 PreviewView::MouseDown(BPoint point)
287 {
288 	MakeFocus(true);
289 	BMessage *message = Window()->CurrentMessage();
290 
291 	int32 button;
292 	if (message && message->FindInt32("buttons", &button) == B_OK) {
293 		if (button == B_PRIMARY_MOUSE_BUTTON) {
294 			int32 modifier;
295 			if (message->FindInt32("modifiers", &modifier) == B_OK) {
296 				if (modifier & B_SHIFT_KEY)
297 					ZoomOut();
298 				else
299 					ZoomIn();
300 			}
301 		}
302 
303 		if (button == B_SECONDARY_MOUSE_BUTTON) {
304 			fTracking = true;
305 			be_app->SetCursor(B_HAND_CURSOR);
306 			SetMouseEventMask(B_POINTER_EVENTS,
307 				B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
308 			fScrollStart = Bounds().LeftTop() + ConvertToScreen(point);
309 		}
310 	}
311 }
312 
313 
314 void
315 PreviewView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
316 {
317 	if (fTracking) {
318 		uint32 button;
319 		GetMouse(&point, &button, false);
320 		point = fScrollStart - ConvertToScreen(point);
321 
322 		float hMin, hMax;
323 		BScrollBar *hBar = ScrollBar(B_HORIZONTAL);
324 		hBar->GetRange(&hMin, &hMax);
325 
326 		float vMin, vMax;
327 		BScrollBar *vBar = ScrollBar(B_VERTICAL);
328 		vBar->GetRange(&vMin, &vMax);
329 
330 		if (point.x < 0.0) point.x = 0.0;
331 		if (point.y < 0.0) point.y = 0.0;
332 		if (point.x > hMax) point.x = hMax;
333 		if (point.y > vMax) point.y = vMax;
334 
335 		ScrollTo(point);
336 	} else {
337 		switch (transit) {
338 			case B_ENTERED_VIEW: {
339 				fInsideView = true;
340 				be_app->SetCursor(ZOOM_IN);
341 			}	break;
342 
343 			case B_EXITED_VIEW: {
344 				fInsideView = false;
345 				be_app->SetCursor(B_HAND_CURSOR);
346 			}	break;
347 
348 			default: {
349 				if (modifiers() & B_SHIFT_KEY)
350 					be_app->SetCursor(ZOOM_OUT);
351 				else
352 					be_app->SetCursor(ZOOM_IN);
353 			}	break;
354 		}
355 	}
356 }
357 
358 
359 void
360 PreviewView::MouseUp(BPoint point)
361 {
362 	(void)point;
363 	fTracking = false;
364 	fScrollStart.Set(0.0, 0.0);
365 	if (fInsideView && ((modifiers() & B_SHIFT_KEY) == 0))
366 		be_app->SetCursor(ZOOM_IN);
367 }
368 
369 
370 void
371 PreviewView::KeyDown(const char* bytes, int32 numBytes)
372 {
373 	MakeFocus(true);
374 	switch (bytes[0]) {
375 		case '-': {
376 			if (modifiers() & B_CONTROL_KEY)
377 				ZoomOut();
378 		}	break;
379 
380 		case '+' : {
381 			if (modifiers() & B_CONTROL_KEY)
382 				ZoomIn();
383 		}	break;
384 
385 		default: {
386 			BView::KeyDown(bytes, numBytes);
387 		}	break;
388 	}
389 }
390 
391 
392 void
393 PreviewView::ShowFirstPage()
394 {
395 	if (!ShowsFirstPage()) {
396 		fPage = 0;
397 		Invalidate();
398 	}
399 }
400 
401 
402 void
403 PreviewView::ShowPrevPage()
404 {
405 	if (!ShowsFirstPage()) {
406 		fPage--;
407 		Invalidate();
408 	}
409 }
410 
411 
412 void
413 PreviewView::ShowNextPage()
414 {
415 	if (!ShowsLastPage()) {
416 		fPage++;
417 		Invalidate();
418 	}
419 }
420 
421 
422 void
423 PreviewView::ShowLastPage()
424 {
425 	if (!ShowsLastPage()) {
426 		fPage = NumberOfPages()-1;
427 		Invalidate();
428 	}
429 }
430 
431 
432 bool
433 PreviewView::ShowsFirstPage() const
434 {
435 	return fPage == 0;
436 }
437 
438 
439 bool
440 PreviewView::ShowsLastPage() const
441 {
442 	return fPage == NumberOfPages() - 1;
443 }
444 
445 
446 void
447 PreviewView::ShowFindPage(int32 page)
448 {
449 	page--;
450 
451 	if (page < 0) {
452 		page = 0;
453 	} else if (page > (NumberOfPages()-1)) {
454 		page = NumberOfPages()-1;
455 	}
456 
457 	fPage = page;
458 	Invalidate();
459 }
460 
461 
462 void
463 PreviewView::ZoomIn()
464 {
465 	if (CanZoomIn()) {
466 		fZoom++;
467 		FixScrollbars();
468 		Invalidate();
469 	}
470 }
471 
472 
473 bool
474 PreviewView::CanZoomIn() const
475 {
476 	return fZoom < 4;
477 }
478 
479 
480 void
481 PreviewView::ZoomOut()
482 {
483 	if (CanZoomOut()) {
484 		fZoom--;
485 		FixScrollbars();
486 		Invalidate();
487 	}
488 }
489 
490 
491 bool
492 PreviewView::CanZoomOut() const
493 {
494 	return fZoom > -2;
495 }
496 
497 
498 void
499 PreviewView::FixScrollbars()
500 {
501 	float width = _PaperRect().Width() + kPreviewLeftMargin + kPreviewRightMargin;
502 	float height = _PaperRect().Height() + kPreviewTopMargin + kPreviewBottomMargin;
503 
504 	BRect frame(Bounds());
505 	float x = width - frame.Width();
506 	if (x < 0.0)
507 		x = 0.0;
508 
509 	float y = height - frame.Height();
510 	if (y < 0.0)
511 		y = 0.0;
512 
513 	BScrollBar * scroll = ScrollBar(B_HORIZONTAL);
514 	scroll->SetRange(0.0, x);
515 	scroll->SetProportion((width - x) / width);
516 	float bigStep = frame.Width() - 2;
517 	float smallStep = bigStep / 10.;
518 	scroll->SetSteps(smallStep, bigStep);
519 
520 	scroll = ScrollBar(B_VERTICAL);
521 	scroll->SetRange(0.0, y);
522 	scroll->SetProportion((height - y) / height);
523 	bigStep = frame.Height() - 2;
524 	smallStep = bigStep / 10.;
525 	scroll->SetSteps(smallStep, bigStep);
526 }
527 
528 
529 BRect
530 PreviewView::ViewRect() const
531 {
532 	BRect r(_PaperRect());
533 	r.right += kPreviewLeftMargin + kPreviewRightMargin;
534 	r.bottom += kPreviewTopMargin + kPreviewBottomMargin;
535 	return r;
536 }
537 
538 
539 status_t
540 PreviewView::InitCheck() const
541 {
542 	return fReader.InitCheck();
543 }
544 
545 
546 int32
547 PreviewView::NumberOfPages() const
548 {
549 	return fNumberOfPages;
550 }
551 
552 
553 BRect
554 PreviewView::_PaperRect() const
555 {
556 	return ScaleRect(fPaperRect, _ZoomFactor());
557 }
558 
559 
560 float
561 PreviewView::_ZoomFactor() const
562 {
563 	const int32 b = 4;
564 	int32 zoom;
565 	if (fZoom > 0) {
566 		zoom = (1 << b) << fZoom;
567 	} else {
568 		zoom = (1 << b) >> -fZoom;
569 	}
570 	float factor = zoom / (float)(1 << b);
571 	return factor * fReader.GetScale() / 100.0;
572 }
573 
574 
575 BRect
576 PreviewView::_PrintableRect() const
577 {
578 	return ScaleRect(fPrintableRect, _ZoomFactor());
579 }
580 
581 
582 bool
583 PreviewView::_IsPageValid() const
584 {
585 	return fCachedPage && fCachedPage->InitCheck() == B_OK;
586 }
587 
588 
589 void
590 PreviewView::_LoadPage(int32 page)
591 {
592 	delete fCachedPage;
593 	fCachedPage = NULL;
594 
595 	PrintJobPage pjp;
596 	if (fReader.GetPage(page, pjp) == B_OK)
597 		fCachedPage = new PreviewPage(page, &pjp);
598 }
599 
600 
601 bool
602 PreviewView::_IsPageLoaded(int32 page) const
603 {
604 	return fCachedPage != NULL && fCachedPage->Page() == page;
605 }
606 
607 
608 BRect
609 PreviewView::_ContentRect() const
610 {
611 	float offsetX = kPreviewLeftMargin;
612 	float offsetY = kPreviewTopMargin;
613 
614 	BRect rect = Bounds();
615 	BRect paperRect = _PaperRect();
616 
617 	float min, max;
618 	ScrollBar(B_HORIZONTAL)->GetRange(&min, &max);
619 	if (min == max) {
620 		if ((paperRect.right + 2 * offsetX) < rect.right)
621 			offsetX = (rect.right - (paperRect.right + 2 * offsetX)) / 2;
622 	}
623 
624 	ScrollBar(B_VERTICAL)->GetRange(&min, &max);
625 	if (min == max) {
626 		if ((paperRect.bottom + 2 * offsetY) < rect.bottom)
627 			offsetY = (rect.bottom - (paperRect.bottom + 2 * offsetY)) / 2;
628 	}
629 
630 	paperRect.OffsetTo(offsetX, offsetY);
631 	return paperRect;
632 }
633 
634 
635 void
636 PreviewView::_DrawPageFrame(BRect rect)
637 {
638 	const float kShadowIndent = 3;
639 	const float kShadowWidth = 3;
640 
641 	const rgb_color frameColor = { 0, 0, 0, 0 };
642 	const rgb_color shadowColor = { 90, 90, 90, 0 };
643 
644 	PushState();
645 
646 	// draw page border around page
647 	BRect r(_ContentRect().InsetByCopy(-1, -1));
648 
649 	SetHighColor(frameColor);
650 	StrokeRect(r);
651 
652 	// draw page shadow
653 	SetHighColor(shadowColor);
654 
655 	float x = r.right + 1;
656 	float right = x + kShadowWidth;
657 	float bottom = r.bottom + 1 + kShadowWidth;
658 	float y = r.top + kShadowIndent;
659 	FillRect(BRect(x, y, right, bottom));
660 
661 	x = r.left + kShadowIndent;
662 	y = r.bottom  + 1;
663 	FillRect(BRect(x, y, r.right, bottom));
664 
665 	PopState();
666 }
667 
668 
669 
670 void PreviewView::_DrawPage(BRect rect)
671 {
672 	BRect printRect(_PrintableRect());
673 	switch (fNumberOfPagesPerPage) {
674 		case 2:
675 		case 8:
676 		case 32:
677 		case 128: {
678 			printRect = RotateRect(printRect);
679 		}	break;
680 	}
681 	printRect.OffsetBy(_ContentRect().LeftTop());
682 
683 	BPoint scalingXY = GraphicsDriver::GetScale(fNumberOfPagesPerPage, printRect, 100.0);
684 	float scaling = min_c(scalingXY.x, scalingXY.y);
685 
686 	printRect = ScaleDown(printRect, _ZoomFactor() * scaling);
687 	BRect clipRect(ScaleRect(printRect, scaling));
688 
689 	for (int32 index = 0; index < fNumberOfPagesPerPage; ++index) {
690 		int32 pageNumber = _GetPageNumber(index);
691 		if (pageNumber < 0)
692 			continue;
693 
694 		if (!_IsPageLoaded(pageNumber))
695 			_LoadPage(pageNumber);
696 
697 		if (!_IsPageValid())
698 			continue;
699 
700 		BPoint offset(CalulateOffset(fNumberOfPagesPerPage, index, fOrientation,
701 			clipRect));
702 
703 		clipRect.OffsetTo(printRect.LeftTop());
704 		clipRect.OffsetBy(offset);
705 
706 		BRegion clip(clipRect);
707 		ConstrainClippingRegion(&clip);
708 
709 		SetScale(_ZoomFactor() * scaling);
710 
711 		fCachedPage->Draw(this, printRect.OffsetByCopy(offset));
712 
713 		if (fNumberOfPagesPerPage > 1)
714 			StrokeRect(clipRect.InsetByCopy(1.0, 1.0), B_MIXED_COLORS);
715 
716 		SetScale(1.0);
717 
718 		ConstrainClippingRegion(NULL);
719 	}
720 }
721 
722 
723 void
724 PreviewView::_DrawMarginFrame(BRect rect)
725 {
726 	BRect paperRect(_ContentRect());
727 	BRect printRect(_PrintableRect());
728 	printRect.OffsetBy(paperRect.LeftTop());
729 
730 	const rgb_color highColor = HighColor();
731 	const rgb_color white = { 255, 255, 255, 255 };
732 
733 	SetHighColor(white);
734 
735 	FillRect(BRect(paperRect.left, paperRect.top, printRect.left
736 		, paperRect.bottom));
737 	FillRect(BRect(paperRect.left, paperRect.top, paperRect.right
738 		, printRect.top));
739 	FillRect(BRect(printRect.right, paperRect.top, paperRect.right
740 		, paperRect.bottom));
741 	FillRect(BRect(paperRect.left, printRect.bottom, paperRect.right
742 		, paperRect.bottom));
743 
744 	SetHighColor(highColor);
745 
746 	BeginLineArray(4);
747 
748 	SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
749 	StrokeLine(BPoint(printRect.left, paperRect.top),
750 		BPoint(printRect.left, paperRect.bottom), B_MIXED_COLORS);
751 	StrokeLine(BPoint(printRect.right, paperRect.top),
752 		BPoint(printRect.right, paperRect.bottom), B_MIXED_COLORS);
753 	StrokeLine(BPoint(paperRect.left, printRect.top),
754 		BPoint(paperRect.right, printRect.top), B_MIXED_COLORS);
755 	StrokeLine(BPoint(paperRect.left, printRect.bottom),
756 		BPoint(paperRect.right, printRect.bottom), B_MIXED_COLORS);
757 
758 	EndLineArray();
759 }
760 
761 
762 
763 int32 PreviewView::_GetPageNumber(int32 index) const
764 {
765 	int32 page = fPage;
766 	if (fReverse)
767 		page = fNumberOfPages - fPage - 1;
768 
769 	if (fPageSelection == JobData::kOddNumberedPages)
770 		page *= 2; // 0, 2, 4, ...
771 	else if (fPageSelection == JobData::kEvenNumberedPages)
772 		page = 2 * page + 1; // 1, 3, 5, ...
773 
774 	return page * fNumberOfPagesPerPage + index;
775 }
776 
777 
778 // #pragma mark - PreviewWindow
779 
780 
781 PreviewWindow::PreviewWindow(BFile* jobFile, bool showOkAndCancelButtons)
782 	: BlockingWindow(BRect(20, 24, 400, 600), "Preview", B_TITLED_WINDOW,
783 		B_ASYNCHRONOUS_CONTROLS)
784 	, fButtonBarHeight(0.0)
785 {
786 	BRect bounds(Bounds());
787 
788 	BView* panel = new BBox(Bounds(), "top_panel", B_FOLLOW_ALL,
789 					B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP,
790 					B_PLAIN_BORDER);
791 	AddChild(panel);
792 
793 	bounds.OffsetBy(10.0, 10.0);
794 
795 	fFirst = new BButton(bounds, "first", "First Page", new BMessage(MSG_FIRST_PAGE));
796 	panel->AddChild(fFirst);
797 	fFirst->ResizeToPreferred();
798 
799 	bounds.OffsetBy(fFirst->Bounds().Width() + 10.0, 0.0);
800 	fPrev = new BButton(bounds, "previous", "Previous Page", new BMessage(MSG_PREV_PAGE));
801 	panel->AddChild(fPrev);
802 	fPrev->ResizeToPreferred();
803 
804 	bounds.OffsetBy(fPrev->Bounds().Width() + 10.0, 0.0);
805 	fNext = new BButton(bounds, "next", "Next Page", new BMessage(MSG_NEXT_PAGE));
806 	panel->AddChild(fNext);
807 	fNext->ResizeToPreferred();
808 
809 	bounds.OffsetBy(fNext->Bounds().Width() + 10.0, 0.0);
810 	fLast = new BButton(bounds, "last", "Last Page", new BMessage(MSG_LAST_PAGE));
811 	panel->AddChild(fLast);
812 	fLast->ResizeToPreferred();
813 
814 	bounds = fLast->Frame();
815 	bounds.OffsetBy(fLast->Bounds().Width() + 10.0, 0.0);
816 	fPageNumber = new BTextControl(bounds, "numOfPage", "99", "",
817 		new BMessage(MSG_FIND_PAGE));
818 	panel->AddChild(fPageNumber);
819 	fPageNumber->ResizeToPreferred();
820 	fPageNumber->SetDivider(0.0);
821 	fPageNumber->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_RIGHT);
822 	fPageNumber->MoveBy(0.0, bounds.Height() - fPageNumber->Bounds().Height());
823 
824 	uint32 num;
825 	for (num = 0; num <= 255; num++)
826 		fPageNumber->TextView()->DisallowChar(num);
827 
828 	for (num = 0; num <= 9; num++)
829 		fPageNumber->TextView()->AllowChar('0' + num);
830 	fPageNumber->TextView()-> SetMaxBytes(5);
831 
832 	bounds.OffsetBy(fPageNumber->Bounds().Width() + 5.0, 0.0);
833 	fPageText = new BStringView(bounds, "pageText", "");
834 	panel->AddChild(fPageText);
835 	fPageText->ResizeTo(fPageText->StringWidth("of 99999 Pages"),
836 		fFirst->Bounds().Height());
837 
838 	bounds.OffsetBy(fPageText->Bounds().Width() + 10.0, 0.0);
839 	fZoomIn = new BButton(bounds, "zoomIn", "Zoom In", new BMessage(MSG_ZOOM_IN));
840 	panel->AddChild(fZoomIn);
841 	fZoomIn->ResizeToPreferred();
842 
843 	bounds.OffsetBy(fZoomIn->Bounds().Width() + 10.0, 0.0);
844 	fZoomOut = new BButton(bounds, "ZoomOut", "Zoom Out", new BMessage(MSG_ZOOM_OUT));
845 	panel->AddChild(fZoomOut);
846 	fZoomOut->ResizeToPreferred();
847 
848 	fButtonBarHeight = fZoomOut->Frame().bottom + 10.0;
849 
850 	bounds = Bounds();
851 	bounds.top = fButtonBarHeight;
852 
853 	if (showOkAndCancelButtons) {
854 		// adjust preview height
855 		bounds.bottom -= fButtonBarHeight;
856 		// update the total height of both bars
857 		fButtonBarHeight *= 2;
858 
859 		// cancel printing if user closes the preview window
860 		SetUserQuitResult(B_ERROR);
861 
862 		BButton *printJob = new BButton(BRect(), "printJob", "Print",
863 			new BMessage(MSG_PRINT_JOB), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
864 		panel->AddChild(printJob);
865 		printJob->ResizeToPreferred();
866 		printJob->MoveTo(bounds.right - (printJob->Bounds().Width() + 10.0),
867 			bounds.bottom + 10.0);
868 
869 		BButton *cancelJob = new BButton(BRect(), "cancelJob", "Cancel",
870 			new BMessage(MSG_CANCEL_JOB), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
871 		panel->AddChild(cancelJob);
872 		cancelJob->ResizeToPreferred();
873 		cancelJob->MoveTo(printJob->Frame().left - (10.0 + cancelJob->Bounds().Width()),
874 			bounds.bottom + 10.0);
875 
876 		printJob->MakeDefault(true);
877 	}
878 
879 	bounds.right -= B_V_SCROLL_BAR_WIDTH;
880 	bounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
881 
882 	fPreview = new PreviewView(jobFile, bounds);
883 	fPreviewScroller = new BScrollView("PreviewScroller", fPreview, B_FOLLOW_ALL,
884 		B_FRAME_EVENTS, true, true, B_FANCY_BORDER);
885 	fPreviewScroller->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
886 	panel->AddChild(fPreviewScroller);
887 
888 	if (fPreview->InitCheck() == B_OK) {
889 		_ResizeToPage();
890 		fPreview->FixScrollbars();
891 		_UpdateControls();
892 		fPreview->MakeFocus(true);
893 	}
894 }
895 
896 
897 void
898 PreviewWindow::MessageReceived(BMessage* m)
899 {
900 	switch (m->what) {
901 		case MSG_FIRST_PAGE:
902 			fPreview->ShowFirstPage();
903 			break;
904 
905 		case MSG_NEXT_PAGE:
906 			fPreview->ShowNextPage();
907 			break;
908 
909 		case MSG_PREV_PAGE:
910 			fPreview->ShowPrevPage();
911 			break;
912 
913 		case MSG_LAST_PAGE:
914 			fPreview->ShowLastPage();
915 			break;
916 
917 		case MSG_FIND_PAGE:
918 			fPreview->ShowFindPage(atoi(fPageNumber->Text())) ;
919 			break;
920 
921 		case MSG_ZOOM_IN:
922 			fPreview->ZoomIn();
923 			break;
924 
925 		case MSG_ZOOM_OUT:
926 			fPreview->ZoomOut();
927 			break;
928 
929 		case B_MODIFIERS_CHANGED:
930 			fPreview->MouseMoved(BPoint(), B_INSIDE_VIEW, m);
931 			break;
932 
933 		case MSG_CANCEL_JOB:
934 			Quit(B_ERROR);
935 			break;
936 
937 		case MSG_PRINT_JOB:
938 			Quit(B_OK);
939 			break;
940 
941 		default:
942 			BlockingWindow::MessageReceived(m);
943 			return;
944 	}
945 	_UpdateControls();
946 }
947 
948 
949 status_t
950 PreviewWindow::Go()
951 {
952 	status_t st = InitCheck();
953 	if (st == B_OK)
954 		return BlockingWindow::Go();
955 
956 	be_app->SetCursor(B_HAND_CURSOR);
957 	Quit();
958 	return st;
959 }
960 
961 
962 void
963 PreviewWindow::_ResizeToPage()
964  {
965 	BScreen screen;
966 	if (screen.Frame().right == 0.0)
967 		return;
968 
969 	const float windowBorderWidth = 5;
970 	const float windowBorderHeight = 5;
971 
972 	BRect rect(fPreview->ViewRect());
973 	float width = rect.Width() + 1 + B_V_SCROLL_BAR_WIDTH;
974 	float height = rect.Height() + 1 + fButtonBarHeight + B_H_SCROLL_BAR_HEIGHT;
975 
976 	rect = screen.Frame();
977 	// dimensions so that window does not reach outside of screen
978 	float maxWidth = rect.Width() + 1 - windowBorderWidth - Frame().left;
979 	float maxHeight = rect.Height() + 1 - windowBorderHeight - Frame().top;
980 
981 	// width so that all buttons are visible
982 	float minWidth = fZoomOut->Frame().right + 10;
983 
984 	if (width < minWidth) width = minWidth;
985 	if (width > maxWidth) width = maxWidth;
986 	if (height > maxHeight) height = maxHeight;
987 
988 	ResizeTo(width, height);
989 }
990 
991 
992 void
993 PreviewWindow::_UpdateControls()
994 {
995 	fFirst->SetEnabled(!fPreview->ShowsFirstPage());
996 	fPrev->SetEnabled(!fPreview->ShowsFirstPage());
997 	fNext->SetEnabled(!fPreview->ShowsLastPage());
998 	fLast->SetEnabled(!fPreview->ShowsLastPage());
999 	fZoomIn->SetEnabled(fPreview->CanZoomIn());
1000 	fZoomOut->SetEnabled(fPreview->CanZoomOut());
1001 
1002 	BString text;
1003 	text << fPreview->CurrentPage();
1004 	fPageNumber->SetText(text.String());
1005 
1006 	text.SetTo("of ");
1007 	text << fPreview->NumberOfPages() << " Page";
1008 	if (fPreview->NumberOfPages() > 1)
1009 		text.Append("s");
1010 	fPageText->SetText(text.String());
1011 }
1012