xref: /haiku/src/libs/print/libprint/Preview.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
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 	bounds.OffsetBy(10.0, 10.0);
788 
789 	fFirst = new BButton(bounds, "first", "First Page", new BMessage(MSG_FIRST_PAGE));
790 	AddChild(fFirst);
791 	fFirst->ResizeToPreferred();
792 
793 	bounds.OffsetBy(fFirst->Bounds().Width() + 10.0, 0.0);
794 	fPrev = new BButton(bounds, "previous", "Previous Page", new BMessage(MSG_PREV_PAGE));
795 	AddChild(fPrev);
796 	fPrev->ResizeToPreferred();
797 
798 	bounds.OffsetBy(fPrev->Bounds().Width() + 10.0, 0.0);
799 	fNext = new BButton(bounds, "next", "Next Page", new BMessage(MSG_NEXT_PAGE));
800 	AddChild(fNext);
801 	fNext->ResizeToPreferred();
802 
803 	bounds.OffsetBy(fNext->Bounds().Width() + 10.0, 0.0);
804 	fLast = new BButton(bounds, "last", "Last Page", new BMessage(MSG_LAST_PAGE));
805 	AddChild(fLast);
806 	fLast->ResizeToPreferred();
807 
808 	bounds = fLast->Frame();
809 	bounds.OffsetBy(fLast->Bounds().Width() + 10.0, 0.0);
810 	fPageNumber = new BTextControl(bounds, "numOfPage", "99", "",
811 		new BMessage(MSG_FIND_PAGE));
812 	AddChild(fPageNumber);
813 	fPageNumber->ResizeToPreferred();
814 	fPageNumber->SetDivider(0.0);
815 	fPageNumber->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_RIGHT);
816 	fPageNumber->MoveBy(0.0, bounds.Height() - fPageNumber->Bounds().Height());
817 
818 	uint32 num;
819 	for (num = 0; num <= 255; num++)
820 		fPageNumber->TextView()->DisallowChar(num);
821 
822 	for (num = 0; num <= 9; num++)
823 		fPageNumber->TextView()->AllowChar('0' + num);
824 	fPageNumber->TextView()-> SetMaxBytes(5);
825 
826 	bounds.OffsetBy(fPageNumber->Bounds().Width() + 5.0, 0.0);
827 	fPageText = new BStringView(bounds, "pageText", "");
828 	AddChild(fPageText);
829 	fPageText->ResizeTo(fPageText->StringWidth("of 99999 Pages"),
830 		fFirst->Bounds().Height());
831 
832 	bounds.OffsetBy(fPageText->Bounds().Width() + 10.0, 0.0);
833 	fZoomIn = new BButton(bounds, "zoomIn", "Zoom In", new BMessage(MSG_ZOOM_IN));
834 	AddChild(fZoomIn);
835 	fZoomIn->ResizeToPreferred();
836 
837 	bounds.OffsetBy(fZoomIn->Bounds().Width() + 10.0, 0.0);
838 	fZoomOut = new BButton(bounds, "ZoomOut", "Zoom Out", new BMessage(MSG_ZOOM_OUT));
839 	AddChild(fZoomOut);
840 	fZoomOut->ResizeToPreferred();
841 
842 	fButtonBarHeight = fZoomOut->Frame().bottom + 10.0;
843 
844 	bounds = Bounds();
845 	bounds.top = fButtonBarHeight;
846 
847 	if (showOkAndCancelButtons) {
848 		// adjust preview height
849 		bounds.bottom -= fButtonBarHeight;
850 		// update the total height of both bars
851 		fButtonBarHeight *= 2;
852 
853 		// cancel printing if user closes the preview window
854 		SetUserQuitResult(B_ERROR);
855 
856 		BButton *printJob = new BButton(BRect(), "printJob", "Print",
857 			new BMessage(MSG_PRINT_JOB), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
858 		AddChild(printJob);
859 		printJob->ResizeToPreferred();
860 		printJob->MoveTo(bounds.right - (printJob->Bounds().Width() + 10.0),
861 			bounds.bottom + 10.0);
862 
863 		BButton *cancelJob = new BButton(BRect(), "cancelJob", "Cancel",
864 			new BMessage(MSG_CANCEL_JOB), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
865 		AddChild(cancelJob);
866 		cancelJob->ResizeToPreferred();
867 		cancelJob->MoveTo(printJob->Frame().left - (10.0 + cancelJob->Bounds().Width()),
868 			bounds.bottom + 10.0);
869 
870 		printJob->MakeDefault(true);
871 	}
872 
873 	bounds.right -= B_V_SCROLL_BAR_WIDTH;
874 	bounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
875 
876 	fPreview = new PreviewView(jobFile, bounds);
877 	fPreviewScroller = new BScrollView("PreviewScroller", fPreview, B_FOLLOW_ALL,
878 		B_FRAME_EVENTS, true, true, B_FANCY_BORDER);
879 	fPreviewScroller->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
880 	AddChild(fPreviewScroller);
881 
882 	if (fPreview->InitCheck() == B_OK) {
883 		_ResizeToPage();
884 		fPreview->FixScrollbars();
885 		_UpdateControls();
886 		fPreview->MakeFocus(true);
887 	}
888 }
889 
890 
891 void
892 PreviewWindow::MessageReceived(BMessage* m)
893 {
894 	switch (m->what) {
895 		case MSG_FIRST_PAGE:
896 			fPreview->ShowFirstPage();
897 			break;
898 
899 		case MSG_NEXT_PAGE:
900 			fPreview->ShowNextPage();
901 			break;
902 
903 		case MSG_PREV_PAGE:
904 			fPreview->ShowPrevPage();
905 			break;
906 
907 		case MSG_LAST_PAGE:
908 			fPreview->ShowLastPage();
909 			break;
910 
911 		case MSG_FIND_PAGE:
912 			fPreview->ShowFindPage(atoi(fPageNumber->Text())) ;
913 			break;
914 
915 		case MSG_ZOOM_IN:
916 			fPreview->ZoomIn();
917 			break;
918 
919 		case MSG_ZOOM_OUT:
920 			fPreview->ZoomOut();
921 			break;
922 
923 		case B_MODIFIERS_CHANGED:
924 			fPreview->MouseMoved(BPoint(), B_INSIDE_VIEW, m);
925 			break;
926 
927 		case MSG_CANCEL_JOB:
928 			Quit(B_ERROR);
929 			break;
930 
931 		case MSG_PRINT_JOB:
932 			Quit(B_OK);
933 			break;
934 
935 		default:
936 			BlockingWindow::MessageReceived(m);
937 			return;
938 	}
939 	_UpdateControls();
940 }
941 
942 
943 status_t
944 PreviewWindow::Go()
945 {
946 	status_t st = InitCheck();
947 	if (st == B_OK)
948 		return BlockingWindow::Go();
949 
950 	be_app->SetCursor(B_HAND_CURSOR);
951 	Quit();
952 	return st;
953 }
954 
955 
956 void
957 PreviewWindow::_ResizeToPage()
958  {
959 	BScreen screen;
960 	if (screen.Frame().right == 0.0)
961 		return;
962 
963 	const float windowBorderWidth = 5;
964 	const float windowBorderHeight = 5;
965 
966 	BRect rect(fPreview->ViewRect());
967 	float width = rect.Width() + 1 + B_V_SCROLL_BAR_WIDTH;
968 	float height = rect.Height() + 1 + fButtonBarHeight + B_H_SCROLL_BAR_HEIGHT;
969 
970 	rect = screen.Frame();
971 	// dimensions so that window does not reach outside of screen
972 	float maxWidth = rect.Width() + 1 - windowBorderWidth - Frame().left;
973 	float maxHeight = rect.Height() + 1 - windowBorderHeight - Frame().top;
974 
975 	// width so that all buttons are visible
976 	float minWidth = fZoomOut->Frame().right + 10;
977 
978 	if (width < minWidth) width = minWidth;
979 	if (width > maxWidth) width = maxWidth;
980 	if (height > maxHeight) height = maxHeight;
981 
982 	ResizeTo(width, height);
983 }
984 
985 
986 void
987 PreviewWindow::_UpdateControls()
988 {
989 	fFirst->SetEnabled(!fPreview->ShowsFirstPage());
990 	fPrev->SetEnabled(!fPreview->ShowsFirstPage());
991 	fNext->SetEnabled(!fPreview->ShowsLastPage());
992 	fLast->SetEnabled(!fPreview->ShowsLastPage());
993 	fZoomIn->SetEnabled(fPreview->CanZoomIn());
994 	fZoomOut->SetEnabled(fPreview->CanZoomOut());
995 
996 	BString text;
997 	text << fPreview->CurrentPage();
998 	fPageNumber->SetText(text.String());
999 
1000 	text.SetTo("of ");
1001 	text << fPreview->NumberOfPages() << " Page";
1002 	if (fPreview->NumberOfPages() > 1)
1003 		text.Append("s");
1004 	fPageText->SetText(text.String());
1005 }
1006