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
35PreviewPage::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
57PreviewPage::~PreviewPage()
58 {
59	delete [] fRects;
60	delete [] fPoints;
61	delete [] fPictures;
62}
63
64
65status_t
66PreviewPage::InitCheck() const
67{
68	return fStatus;
69}
70
71
72void
73PreviewPage::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
84namespace {
85
86const float kPreviewTopMargin		= 10.0;
87const float kPreviewBottomMargin	= 30.0;
88const float kPreviewLeftMargin		= 10.0;
89const float kPreviewRightMargin		= 30.0;
90
91
92// TODO share constant with JobData
93const char *kJDOrientation		= "orientation";
94const char *kJDNup				= "JJJJ_nup";
95const char *kJDReverse			= "JJJJ_reverse";
96const char* kJDPageSelection	= "JJJJ_page_selection";
97
98
99const 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
105const 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
111BRect
112RotateRect(const BRect& rect)
113{
114	return BRect(rect.top, rect.left, rect.bottom, rect.right);
115}
116
117
118BRect
119ScaleDown(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
129BPoint
130CalulateOffset(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
191PreviewView::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
244PreviewView::~PreviewView()
245{
246	delete fCachedPage;
247}
248
249
250void
251PreviewView::Show()
252{
253	BView::Show();
254	be_app->SetCursor(ZOOM_IN);
255}
256
257
258void
259PreviewView::Hide()
260{
261	be_app->SetCursor(B_HAND_CURSOR);
262	BView::Hide();
263}
264
265
266void
267PreviewView::Draw(BRect rect)
268{
269	if (fReader.InitCheck() == B_OK) {
270		_DrawPageFrame(rect);
271		_DrawPage(rect);
272		_DrawMarginFrame(rect);
273	}
274}
275
276
277void
278PreviewView::FrameResized(float width, float height)
279{
280	Invalidate();
281	FixScrollbars();
282}
283
284
285void
286PreviewView::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
314void
315PreviewView::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
359void
360PreviewView::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
370void
371PreviewView::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
392void
393PreviewView::ShowFirstPage()
394{
395	if (!ShowsFirstPage()) {
396		fPage = 0;
397		Invalidate();
398	}
399}
400
401
402void
403PreviewView::ShowPrevPage()
404{
405	if (!ShowsFirstPage()) {
406		fPage--;
407		Invalidate();
408	}
409}
410
411
412void
413PreviewView::ShowNextPage()
414{
415	if (!ShowsLastPage()) {
416		fPage++;
417		Invalidate();
418	}
419}
420
421
422void
423PreviewView::ShowLastPage()
424{
425	if (!ShowsLastPage()) {
426		fPage = NumberOfPages()-1;
427		Invalidate();
428	}
429}
430
431
432bool
433PreviewView::ShowsFirstPage() const
434{
435	return fPage == 0;
436}
437
438
439bool
440PreviewView::ShowsLastPage() const
441{
442	return fPage == NumberOfPages() - 1;
443}
444
445
446void
447PreviewView::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
462void
463PreviewView::ZoomIn()
464{
465	if (CanZoomIn()) {
466		fZoom++;
467		FixScrollbars();
468		Invalidate();
469	}
470}
471
472
473bool
474PreviewView::CanZoomIn() const
475{
476	return fZoom < 4;
477}
478
479
480void
481PreviewView::ZoomOut()
482{
483	if (CanZoomOut()) {
484		fZoom--;
485		FixScrollbars();
486		Invalidate();
487	}
488}
489
490
491bool
492PreviewView::CanZoomOut() const
493{
494	return fZoom > -2;
495}
496
497
498void
499PreviewView::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
529BRect
530PreviewView::ViewRect() const
531{
532	BRect r(_PaperRect());
533	r.right += kPreviewLeftMargin + kPreviewRightMargin;
534	r.bottom += kPreviewTopMargin + kPreviewBottomMargin;
535	return r;
536}
537
538
539status_t
540PreviewView::InitCheck() const
541{
542	return fReader.InitCheck();
543}
544
545
546int32
547PreviewView::NumberOfPages() const
548{
549	return fNumberOfPages;
550}
551
552
553BRect
554PreviewView::_PaperRect() const
555{
556	return ScaleRect(fPaperRect, _ZoomFactor());
557}
558
559
560float
561PreviewView::_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
575BRect
576PreviewView::_PrintableRect() const
577{
578	return ScaleRect(fPrintableRect, _ZoomFactor());
579}
580
581
582bool
583PreviewView::_IsPageValid() const
584{
585	return fCachedPage && fCachedPage->InitCheck() == B_OK;
586}
587
588
589void
590PreviewView::_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
601bool
602PreviewView::_IsPageLoaded(int32 page) const
603{
604	return fCachedPage != NULL && fCachedPage->Page() == page;
605}
606
607
608BRect
609PreviewView::_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
635void
636PreviewView::_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
670void 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
723void
724PreviewView::_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
763int32 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
781PreviewWindow::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->SetViewUIColor(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
897void
898PreviewWindow::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
949status_t
950PreviewWindow::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
962void
963PreviewWindow::_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
992void
993PreviewWindow::_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