1/*
2 * PageSetupDlg.cpp
3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4 */
5
6#include <string>
7#include <cstring>
8#include <cstdlib>
9#include <fcntl.h>
10#include <unistd.h>
11#include <sys/stat.h>
12
13#include <Alert.h>
14#include <Bitmap.h>
15#include <Box.h>
16#include <Button.h>
17#include <Debug.h>
18#include <Font.h>
19#include <GridView.h>
20#include <GroupLayout.h>
21#include <GroupLayoutBuilder.h>
22#include <Looper.h>
23#include <MessageFilter.h>
24#include <MenuField.h>
25#include <MenuItem.h>
26#include <Message.h>
27#include <Point.h>
28#include <PopUpMenu.h>
29#include <PrintJob.h>
30#include <RadioButton.h>
31#include <Rect.h>
32#include <String.h>
33#include <SupportDefs.h>
34#include <TextControl.h>
35#include <View.h>
36
37
38#include "DbgMsg.h"
39#include "JobData.h"
40#include "MarginView.h"
41#include "PageSetupDlg.h"
42#include "PrinterData.h"
43#include "PrinterCap.h"
44#include "PrintUtils.h"
45
46
47using namespace std;
48
49
50enum MSGS {
51	kMsgCancel = 1,
52	kMsgOK,
53	kMsgOrientationChanged,
54	kMsgPaperChanged,
55};
56
57PageSetupView::PageSetupView(JobData *job_data, PrinterData *printer_data,
58	const PrinterCap *printer_cap)
59	: BView("pageSetupView", B_WILL_DRAW)
60	, fJobData(job_data)
61	, fPrinterData(printer_data)
62	, fPrinterCap(printer_cap)
63{
64	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
65}
66
67PageSetupView::~PageSetupView()
68{
69}
70
71void
72PageSetupView::AddOrientationItem(const char* name, JobData::Orientation orientation)
73{
74	BMessage *msg = new BMessage(kMsgOrientationChanged);
75	msg->AddInt32("orientation", orientation);
76	BMenuItem *item = new BMenuItem(name, msg);
77
78	fOrientation->AddItem(item);
79	item->SetTarget(this);
80	if (fJobData->GetOrientation() == orientation) {
81		item->SetMarked(true);
82	} else if (fOrientation->CountItems() == 1) {
83		item->SetMarked(true);
84	}
85}
86
87void
88PageSetupView::AttachedToWindow()
89{
90	BMenuItem  *item = NULL;
91	bool       marked;
92	int        count;
93
94	// margin
95	MarginUnit units = fJobData->GetMarginUnit();
96	BRect paper = fJobData->GetPaperRect();
97	BRect margin = fJobData->GetPrintableRect();
98
99	// re-calculate the margin from the printable rect in points
100	margin.top -= paper.top;
101	margin.left -= paper.left;
102	margin.right = paper.right - margin.right;
103	margin.bottom = paper.bottom - margin.bottom;
104
105	fMarginView = new MarginView(
106		paper.IntegerWidth(),
107		paper.IntegerHeight(),
108			margin, units);
109
110	// paper selection
111	marked = false;
112	fPaper = new BPopUpMenu("paperSize");
113	fPaper->SetRadioMode(true);
114	count = fPrinterCap->CountCap(PrinterCap::kPaper);
115	PaperCap **paper_cap = (PaperCap **)fPrinterCap->GetCaps(PrinterCap::kPaper);
116	while (count--) {
117		BMessage *msg = new BMessage(kMsgPaperChanged);
118		msg->AddPointer("paperCap", *paper_cap);
119		item = new BMenuItem((*paper_cap)->fLabel.c_str(), msg);
120		fPaper->AddItem(item);
121		item->SetTarget(this);
122		if ((*paper_cap)->fPaper == fJobData->GetPaper()) {
123			item->SetMarked(true);
124			marked = true;
125		}
126		paper_cap++;
127	}
128	if (!marked)
129		item->SetMarked(true);
130	BMenuField* paperSize = new BMenuField("paperSize", "Paper size:", fPaper);
131
132	// orientation
133	fOrientation = new BPopUpMenu("orientation");
134	fOrientation->SetRadioMode(true);
135
136	BMenuField* orientation = new BMenuField("orientation", "Orientation:", fOrientation);
137
138	count = fPrinterCap->CountCap(PrinterCap::kOrientation);
139	if (count == 0) {
140		AddOrientationItem("Portrait", JobData::kPortrait);
141		AddOrientationItem("Landscape", JobData::kLandscape);
142	} else {
143		OrientationCap **orientation_cap = (OrientationCap **)fPrinterCap->GetCaps(PrinterCap::kOrientation);
144		while (count--) {
145			AddOrientationItem((*orientation_cap)->fLabel.c_str(),
146				(*orientation_cap)->fOrientation);
147			orientation_cap++;
148		}
149	}
150
151	// resolution
152	marked = false;
153	fResolution = new BPopUpMenu("resolution");
154	fResolution->SetRadioMode(true);
155	count = fPrinterCap->CountCap(PrinterCap::kResolution);
156	ResolutionCap **resolution_cap = (ResolutionCap **)fPrinterCap->GetCaps(PrinterCap::kResolution);
157	while (count--) {
158		item = new BMenuItem((*resolution_cap)->fLabel.c_str(), NULL);
159		fResolution->AddItem(item);
160		item->SetTarget(this);
161		if (((*resolution_cap)->fXResolution == fJobData->GetXres()) &&
162			((*resolution_cap)->fYResolution == fJobData->GetYres())) {
163			item->SetMarked(true);
164			marked = true;
165		}
166		resolution_cap++;
167	}
168	if (!marked)
169		item->SetMarked(true);
170	BMenuField* resolution = new BMenuField("resolution", "Resolution:", fResolution);
171
172	// scale
173	BString scale;
174	scale << (int)fJobData->GetScaling();
175	fScaling = new BTextControl("scale", "Scale [%]:",
176									scale.String(),
177	                                NULL);
178	int num;
179	for (num = 0; num <= 255; num++) {
180		fScaling->TextView()->DisallowChar(num);
181	}
182	for (num = 0; num <= 9; num++) {
183		fScaling->TextView()->AllowChar('0' + num);
184	}
185	fScaling->TextView()->SetMaxBytes(3);
186
187	// cancel and ok
188	BButton* cancel = new BButton("cancel", "Cancel", new BMessage(kMsgCancel));
189	BButton* ok = new BButton("ok", "OK", new BMessage(kMsgOK));
190
191	ok->MakeDefault(true);
192
193	BGridView* settings = new BGridView();
194	BGridLayout* settingsLayout = settings->GridLayout();
195	settingsLayout->AddItem(paperSize->CreateLabelLayoutItem(), 0, 0);
196	settingsLayout->AddItem(paperSize->CreateMenuBarLayoutItem(), 1, 0);
197	settingsLayout->AddItem(orientation->CreateLabelLayoutItem(), 0, 1);
198	settingsLayout->AddItem(orientation->CreateMenuBarLayoutItem(), 1, 1);
199	settingsLayout->AddItem(resolution->CreateLabelLayoutItem(), 0, 2);
200	settingsLayout->AddItem(resolution->CreateMenuBarLayoutItem(), 1, 2);
201	settingsLayout->AddItem(fScaling->CreateLabelLayoutItem(), 0, 3);
202	settingsLayout->AddItem(fScaling->CreateTextViewLayoutItem(), 1, 3);
203	settingsLayout->SetSpacing(0, 0);
204
205	SetLayout(new BGroupLayout(B_VERTICAL));
206	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
207		.AddGroup(B_HORIZONTAL, 5, 1.0f)
208			.AddGroup(B_VERTICAL, 0, 1.0f)
209				.Add(fMarginView)
210				.AddGlue()
211			.End()
212			.AddGroup(B_VERTICAL, 0, 1.0f)
213				.Add(settings)
214				.AddGlue()
215			.End()
216		.End()
217		.AddGroup(B_HORIZONTAL, 10, 1.0f)
218			.AddGlue()
219			.Add(cancel)
220			.Add(ok)
221		.End()
222		.SetInsets(10, 10, 10, 10)
223	);
224}
225
226inline void
227swap(float *e1, float *e2)
228{
229	float e = *e1;
230	*e1 = *e2;
231	*e2 = e;
232}
233
234JobData::Orientation
235PageSetupView::GetOrientation()
236{
237	BMenuItem *item = fOrientation->FindMarked();
238	int32 orientation;
239	if (item != NULL &&
240		item->Message()->FindInt32("orientation", &orientation) == B_OK) {
241		return (JobData::Orientation)orientation;
242	} else {
243		return JobData::kPortrait;
244	}
245}
246
247PaperCap *
248PageSetupView::GetPaperCap()
249{
250	BMenuItem *item = fPaper->FindMarked();
251	void *pointer;
252	if (item != NULL &&
253		item->Message()->FindPointer("paperCap", &pointer) == B_OK) {
254		return (PaperCap*)pointer;
255	} else {
256		return (PaperCap*)fPrinterCap->GetDefaultCap(PrinterCap::kPaper);
257	}
258}
259
260bool
261PageSetupView::UpdateJobData()
262{
263	fJobData->SetOrientation(GetOrientation());
264
265	PaperCap *paperCap = GetPaperCap();
266	BRect paper_rect = paperCap->fPaperRect;
267	BRect physical_rect = paperCap->fPhysicalRect;
268	fJobData->SetPaper(paperCap->fPaper);
269
270	const char *resolutionLabel = fResolution->FindMarked()->Label();
271	const ResolutionCap* resolution = static_cast<const ResolutionCap*>(
272		fPrinterCap->FindCap(PrinterCap::kResolution, resolutionLabel));
273	ASSERT(resolution != NULL);
274	if (resolution != NULL) {
275		fJobData->SetXres(resolution->fXResolution);
276		fJobData->SetYres(resolution->fYResolution);
277		fJobData->SetResolutionID(resolution->ID());
278	}
279
280	// rotate paper and physical rectangle if landscape orientation
281	if (JobData::kLandscape == fJobData->GetOrientation()) {
282		swap(&paper_rect.left, &paper_rect.top);
283		swap(&paper_rect.right, &paper_rect.bottom);
284		swap(&physical_rect.left, &physical_rect.top);
285		swap(&physical_rect.right, &physical_rect.bottom);
286	}
287
288	// adjust printable rect by margin
289	fJobData->SetMarginUnit(fMarginView->Unit());
290	BRect margin = fMarginView->Margin();
291	BRect printable_rect;
292	printable_rect.left = paper_rect.left + margin.left;
293	printable_rect.top = paper_rect.top + margin.top;
294	printable_rect.right = paper_rect.right - margin.right;
295	printable_rect.bottom = paper_rect.bottom - margin.bottom;
296
297	printable_rect.left = max_c(printable_rect.left, physical_rect.left);
298	printable_rect.top = max_c(printable_rect.top, physical_rect.top);
299	printable_rect.right = min_c(printable_rect.right, physical_rect.right);
300	printable_rect.bottom = min_c(printable_rect.bottom, physical_rect.bottom);
301
302	float scaling = atoi(fScaling->Text());
303	if (scaling <= 0.0) { // sanity check
304		scaling = 100.0;
305	}
306	if (scaling > 1000.0) {
307		scaling = 1000.0;
308	}
309
310	float scalingR = 100.0 / scaling;
311
312	fJobData->SetScaling(scaling);
313	fJobData->SetPaperRect(paper_rect);
314	fJobData->SetScaledPaperRect(ScaleRect(paper_rect, scalingR));
315	fJobData->SetPrintableRect(printable_rect);
316	fJobData->SetScaledPrintableRect(ScaleRect(printable_rect, scalingR));
317	fJobData->SetPhysicalRect(physical_rect);
318	fJobData->SetScaledPhysicalRect(ScaleRect(physical_rect, scalingR));
319
320	fJobData->Save();
321	return true;
322}
323
324void
325PageSetupView::MessageReceived(BMessage *msg)
326{
327	switch (msg->what) {
328		case kMsgPaperChanged:
329		case kMsgOrientationChanged:
330			{
331				JobData::Orientation orientation = GetOrientation();
332				PaperCap *paperCap = GetPaperCap();
333				float width = paperCap->fPaperRect.Width();
334				float height = paperCap->fPaperRect.Height();
335				if (orientation != JobData::kPortrait) {
336					swap(&width, &height);
337				}
338				fMarginView->SetPageSize(width, height);
339				fMarginView->UpdateView(MARGIN_CHANGED);
340
341			}
342			break;
343	}
344}
345
346//====================================================================
347
348// TODO center window on screen
349PageSetupDlg::PageSetupDlg(JobData *job_data, PrinterData *printer_data, const PrinterCap *printer_cap)
350	: DialogWindow(BRect(100, 100, 160, 160),
351		"Paper setup", B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
352		B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
353			| B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE)
354{
355	AddShortcut('W',B_COMMAND_KEY,new BMessage(B_QUIT_REQUESTED));
356
357	fPageSetupView = new PageSetupView(job_data, printer_data,
358		printer_cap);
359
360	SetLayout(new BGroupLayout(B_HORIZONTAL));
361	AddChild(BGroupLayoutBuilder(B_HORIZONTAL, 0)
362		.Add(fPageSetupView)
363	);
364
365	SetResult(B_ERROR);
366}
367
368void
369PageSetupDlg::MessageReceived(BMessage *msg)
370{
371	switch (msg->what) {
372	case kMsgOK:
373		Lock();
374		fPageSetupView->UpdateJobData();
375		Unlock();
376		SetResult(B_NO_ERROR);
377		PostMessage(B_QUIT_REQUESTED);
378		break;
379
380	case kMsgCancel:
381		PostMessage(B_QUIT_REQUESTED);
382		break;
383
384	default:
385		DialogWindow::MessageReceived(msg);
386	}
387}
388
389