xref: /haiku/src/libs/print/libprint/MarginView.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
1 /*
2 
3 MarginView.cpp
4 
5 Copyright (c) 2002 Haiku.
6 
7 Authors:
8 	Philippe Houdoin
9 	Simon Gauvin
10 	Michael Pfeiffer
11 
12 Permission is hereby granted, free of charge, to any person obtaining a copy of
13 this software and associated documentation files (the "Software"), to deal in
14 the Software without restriction, including without limitation the rights to
15 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16 of the Software, and to permit persons to whom the Software is furnished to do
17 so, subject to the following conditions:
18 
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21 
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 THE SOFTWARE.
29 
30 	Todo:
31 
32 	2 Make Strings constants or UI resources
33 
34 */
35 
36 #include "MarginView.h"
37 
38 
39 #include <AppKit.h>
40 #include <GridView.h>
41 #include <GridLayout.h>
42 #include <GroupLayout.h>
43 #include <GroupLayoutBuilder.h>
44 #include <SupportKit.h>
45 #include <TextControl.h>
46 
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 
51 
52 /*----------------- MarginView Private Constants --------------------*/
53 
54 const int kOffsetY = 20;
55 const int kOffsetX = 10;
56 const int kStringSize = 50;
57 const int kWidth = 50;
58 const int kNumCount = 10;
59 
60 const static float kPointUnits = 1; // 1 point = 1 point
61 const static float kInchUnits = 72; // 1" = 72 points
62 const static float kCMUnits = 28.346; // 72/2.54 1cm = 28.346 points
63 
64 const static float kMinFieldWidth = 100; // pixels
65 const static float kMinUnitHeight = 30; // pixels
66 
67 const static float kUnitFormat[] = { kInchUnits, kCMUnits, kPointUnits };
68 const static char *kUnitNames[] = { "inch", "cm", "points", NULL };
69 const static MarginUnit kUnitMsg[] = { kUnitInch, kUnitCM, kUnitPoint };
70 
71 const pattern kDots = {{ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }};
72 
73 const rgb_color kBlack 	= { 0,0,0,0 };
74 const rgb_color kRed 	= { 255,0,0,0 };
75 const rgb_color kWhite 	= { 255,255,255,0 };
76 const rgb_color kGray 	= { 220,220,220,0 };
77 
78 
PageView()79 PageView::PageView()
80 : BView("pageView", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
81 , fPageWidth(0)
82 , fPageHeight(0)
83 , fMargins(0, 0, 0, 0)
84 {
85 
86 }
87 
88 
89 void
SetPageSize(float pageWidth,float pageHeight)90 PageView::SetPageSize(float pageWidth, float pageHeight)
91 {
92 	fPageWidth = pageWidth;
93 	fPageHeight = pageHeight;
94 }
95 
96 
97 void
SetMargins(BRect margins)98 PageView::SetMargins(BRect margins)
99 {
100 	fMargins = margins;
101 }
102 
103 
104 void
Draw(BRect bounds)105 PageView::Draw(BRect bounds)
106 {
107 	BRect frame(Frame());
108 	float totalWidth = frame.Width();
109 	float totalHeight = frame.Height();
110 
111 	// fit page into available space
112 	// keeping the ratio fPageWidth : fPageHeight
113 	float pageWidth = totalWidth;
114 	float pageHeight = totalWidth * fPageHeight / fPageWidth;
115 	if (pageHeight > totalHeight) {
116 		pageHeight = totalHeight;
117 		pageWidth = totalHeight * fPageWidth / fPageHeight;
118 	}
119 
120 	// center page
121 	BPoint offset(0, 0);
122 	offset.x = static_cast<int>((totalWidth - pageWidth) / 2);
123 	offset.y = static_cast<int>((totalHeight - pageHeight) / 2);
124 
125 	// draw the page
126 	SetHighColor(kWhite);
127 	BRect r = BRect(0, 0, pageWidth, pageHeight);
128 	r.OffsetBy(offset);
129 	FillRect(r);
130 	SetHighColor(kBlack);
131 	StrokeRect(r);
132 
133 	// draw margin
134 	SetHighColor(kRed);
135 	SetLowColor(kWhite);
136 	r.top += (fMargins.top / fPageHeight) * pageHeight;
137 	r.right -= (fMargins.right / fPageWidth) * pageWidth;
138 	r.bottom -= (fMargins.bottom / fPageHeight) * pageHeight;
139 	r.left += (fMargins.left / fPageWidth) * pageWidth;
140 	StrokeRect(r, kDots);
141 }
142 
143 
144 /**
145  * Constructor
146  *
147  * @param pageWidth, float that is the points value of the page width
148  * @param pageHeight, float that is the points value of the page height
149  * @param margins, BRect values of margins
150  * @param units, unit32 enum for units used in view
151  * @return void
152  */
MarginView(int32 pageWidth,int32 pageHeight,BRect margins,MarginUnit units)153 MarginView::MarginView(int32 pageWidth, int32 pageHeight,
154 	BRect margins, MarginUnit units)
155 	: BBox("marginView")
156 {
157 	fMarginUnit = units;
158 	fUnitValue = kUnitFormat[units];
159 
160 	SetLabel("Margins");
161 
162 	fMargins = margins;
163 
164 	fPageWidth = pageWidth;
165 	fPageHeight = pageHeight;
166 }
167 
168 
169 /**
170  * Destructor
171  *
172  * @param none
173  * @return void
174  */
~MarginView()175 MarginView::~MarginView()
176 {
177 }
178 
179 
180 /**
181  * AttachToWindow
182  *
183  * @param none
184  * @return void
185  */
186 void
AttachedToWindow()187 MarginView::AttachedToWindow()
188 {
189 	if (Parent())
190 		SetViewColor(Parent()->ViewColor());
191 
192 	_ConstructGUI();
193 }
194 
195 
196 /**
197  * MesssageReceived()
198  *
199  * Receive messages for the view
200  *
201  * @param BMessage* , the message being received
202  * @return void
203  */
204 void
MessageReceived(BMessage * msg)205 MarginView::MessageReceived(BMessage *msg)
206 {
207 	switch (msg->what) {
208 		case CHANGE_PAGE_SIZE: {
209 				float w;
210 				float h;
211 				msg->FindFloat("width", &w);
212 				msg->FindFloat("height", &h);
213 				SetPageSize(w, h);
214 				UpdateView(MARGIN_CHANGED);
215 			}	break;
216 
217 		case FLIP_PAGE: {
218 				BPoint p = PageSize();
219 				SetPageSize(p.y, p.x);
220 				UpdateView(MARGIN_CHANGED);
221 			}	break;
222 
223 		case MARGIN_CHANGED:
224 			UpdateView(MARGIN_CHANGED);
225 			break;
226 
227 		case TOP_MARGIN_CHANGED:
228 			UpdateView(TOP_MARGIN_CHANGED);
229 			break;
230 
231 		case LEFT_MARGIN_CHANGED:
232 			UpdateView(LEFT_MARGIN_CHANGED);
233 			break;
234 
235 		case RIGHT_MARGIN_CHANGED:
236 			UpdateView(RIGHT_MARGIN_CHANGED);
237 			break;
238 
239 		case BOTTOM_MARGIN_CHANGED:
240 			UpdateView(BOTTOM_MARGIN_CHANGED);
241 			break;
242 
243 		case MARGIN_UNIT_CHANGED: {
244 		 		int32 marginUnit;
245 		 		if (msg->FindInt32("marginUnit", &marginUnit) == B_OK)
246 					_SetMarginUnit((MarginUnit)marginUnit);
247 			}	break;
248 
249 		default:
250 			BView::MessageReceived(msg);
251 			break;
252 	}
253 }
254 
255 
256 /*----------------- MarginView Public Methods --------------------*/
257 
258 /**
259  * PageSize
260  *
261  * @param none
262  * @return BPoint, contains actual point values of page in x, y of point
263  */
264 BPoint
PageSize() const265 MarginView::PageSize() const
266 {
267 	return BPoint(fPageWidth, fPageHeight);
268 }
269 
270 
271 /**
272  * SetPageSize
273  *
274  * @param pageWidth, float that is the unit value of the page width
275  * @param pageHeight, float that is the unit value of the page height
276  * @return void
277  */
278 void
SetPageSize(float pageWidth,float pageHeight)279 MarginView::SetPageSize(float pageWidth, float pageHeight)
280 {
281 	fPageWidth = pageWidth;
282 	fPageHeight = pageHeight;
283 }
284 
285 
286 /**
287  * Margin
288  *
289  * @param none
290  * @return rect, return margin values always in points
291  */
292 BRect
Margin() const293 MarginView::Margin() const
294 {
295 	BRect margin;
296 
297 	// convert the field text to values
298 	float top = atof(fTop->Text());
299 	float right = atof(fRight->Text());
300 	float left = atof(fLeft->Text());
301 	float bottom = atof(fBottom->Text());
302 
303 	// convert to units to points
304 	switch (fMarginUnit) {
305 		case kUnitInch:
306 			// convert to points
307 			top *= kInchUnits;
308 			right *= kInchUnits;
309 			left *= kInchUnits;
310 			bottom *= kInchUnits;
311 			break;
312 		case kUnitCM:
313 			// convert to points
314 			top *= kCMUnits;
315 			right *= kCMUnits;
316 			left *= kCMUnits;
317 			bottom *= kCMUnits;
318 			break;
319 		case kUnitPoint:
320 			break;
321 	}
322 
323 	margin.Set(left, top, right, bottom);
324 
325 	return margin;
326 }
327 
328 
329 
330 /**
331  * Unit
332  *
333  * @param none
334  * @return uint32 enum, units in inches, cm, points
335  */
336 MarginUnit
Unit() const337 MarginView::Unit() const
338 {
339 	return fMarginUnit;
340 }
341 
342 
343 /**
344  * UpdateView, recalculate and redraw the view
345  *
346  * @param msg is a message to the calculate size to tell which field caused
347  *		the update to occur, or it is a general update.
348  * @return void
349  */
350 void
UpdateView(uint32 msg)351 MarginView::UpdateView(uint32 msg)
352 {
353 	Window()->Lock();
354 
355 	{
356 		char pageSize[kStringSize];
357 		sprintf(pageSize, "%2.1f x %2.1f",
358 			fPageWidth / fUnitValue,
359 			fPageHeight / fUnitValue);
360 		fPageSize->SetText(pageSize);
361 
362 		fPage->SetPageSize(fPageWidth, fPageHeight);
363 		fPage->SetMargins(Margin());
364 		fPage->Invalidate();
365 	}
366 
367 	Invalidate();
368 	Window()->Unlock();
369 }
370 
371 
372 /*----------------- MarginView Private Methods --------------------*/
373 
374 /**
375  * _ConstructGUI()
376  *
377  * Creates the GUI for the View. MUST be called AFTER the View is attached to
378  *	the Window, or will crash and/or create strange behaviour
379  *
380  * @param none
381  * @return void
382  */
383 void
_ConstructGUI()384 MarginView::_ConstructGUI()
385 {
386 	fPage = new PageView();
387 	fPage->SetViewColor(ViewColor());
388 
389 	fPageSize = new BStringView("pageSize", "?x?");
390 
391 	BString str;
392 	// Create text fields
393 
394 	// top
395 	str << fMargins.top/fUnitValue;
396 	fTop = new BTextControl("top", "Top:", str.String(), NULL);
397 
398 	fTop->SetModificationMessage(new BMessage(TOP_MARGIN_CHANGED));
399 	fTop->SetTarget(this);
400 	_AllowOnlyNumbers(fTop, kNumCount);
401 
402 	//left
403     str = "";
404 	str << fMargins.left/fUnitValue;
405 	fLeft = new BTextControl("left", "Left:", str.String(), NULL);
406 
407 	fLeft->SetModificationMessage(new BMessage(LEFT_MARGIN_CHANGED));
408 	fLeft->SetTarget(this);
409 	_AllowOnlyNumbers(fLeft, kNumCount);
410 
411 	//bottom
412     str = "";
413 	str << fMargins.bottom/fUnitValue;
414 	fBottom = new BTextControl("bottom", "Bottom:", str.String(), NULL);
415 
416 	fBottom->SetModificationMessage(new BMessage(BOTTOM_MARGIN_CHANGED));
417 	fBottom->SetTarget(this);
418 	_AllowOnlyNumbers(fBottom, kNumCount);
419 
420 	//right
421     str = "";
422 	str << fMargins.right/fUnitValue;
423 	fRight = new BTextControl("right", "Right:", str.String(), NULL);
424 
425 	fRight->SetModificationMessage(new BMessage(RIGHT_MARGIN_CHANGED));
426 	fRight->SetTarget(this);
427 	_AllowOnlyNumbers(fRight, kNumCount);
428 
429 	// Create Units popup
430 
431 	BPopUpMenu *menu = new BPopUpMenu("units");
432 	BMenuField *units = new BMenuField("units", "Units:", menu);
433 
434 	BMenuItem *item;
435 	// Construct menu items
436 	for (int32 i = 0; kUnitNames[i] != NULL; i++) {
437 		BMessage *msg = new BMessage(MARGIN_UNIT_CHANGED);
438 		msg->AddInt32("marginUnit", kUnitMsg[i]);
439 		menu->AddItem(item = new BMenuItem(kUnitNames[i], msg));
440 		item->SetTarget(this);
441 		if (fMarginUnit == kUnitMsg[i])
442 			item->SetMarked(true);
443 	}
444 
445 	BGridView* settings = new BGridView();
446 	BGridLayout* settingsLayout = settings->GridLayout();
447 	settingsLayout->AddItem(fTop->CreateLabelLayoutItem(), 0, 0);
448 	settingsLayout->AddItem(fTop->CreateTextViewLayoutItem(), 1, 0);
449 	settingsLayout->AddItem(fLeft->CreateLabelLayoutItem(), 0, 1);
450 	settingsLayout->AddItem(fLeft->CreateTextViewLayoutItem(), 1, 1);
451 	settingsLayout->AddItem(fBottom->CreateLabelLayoutItem(), 0, 2);
452 	settingsLayout->AddItem(fBottom->CreateTextViewLayoutItem(), 1, 2);
453 	settingsLayout->AddItem(fRight->CreateLabelLayoutItem(), 0, 3);
454 	settingsLayout->AddItem(fRight->CreateTextViewLayoutItem(), 1, 3);
455 	settingsLayout->AddItem(units->CreateLabelLayoutItem(), 0, 4);
456 	settingsLayout->AddItem(units->CreateMenuBarLayoutItem(), 1, 4);
457 	settingsLayout->SetSpacing(0, 0);
458 
459 	BGroupView* groupView = new BGroupView(B_HORIZONTAL, 10);
460 	BGroupLayout* groupLayout = groupView->GroupLayout();
461 	groupLayout->AddView(BGroupLayoutBuilder(B_VERTICAL, 0)
462 		.Add(fPage)
463 		.Add(fPageSize)
464 		.SetInsets(0, 0, 0, 0)
465 		.TopView()
466 	);
467 	groupLayout->AddView(settings);
468 	groupLayout->SetInsets(5, 5, 5, 5);
469 
470 	AddChild(groupView);
471 
472 	UpdateView(MARGIN_CHANGED);
473 }
474 
475 
476 /**
477  * AllowOnlyNumbers()
478  *
479  * @param BTextControl, the control we want to only allow numbers
480  * @param maxNum, the maximun number of characters allowed
481  * @return void
482  */
483 void
_AllowOnlyNumbers(BTextControl * textControl,int32 maxNum)484 MarginView::_AllowOnlyNumbers(BTextControl *textControl, int32 maxNum)
485 {
486 	BTextView *tv = textControl->TextView();
487 
488 	for (int32 i = 0; i < 256; i++)
489 		tv->DisallowChar(i);
490 
491 	for (int32 i = '0'; i <= '9'; i++)
492 		tv->AllowChar(i);
493 
494 	tv->AllowChar(B_BACKSPACE);
495 	// TODO internationalization; e.g. "." or ","
496 	tv->AllowChar('.');
497 	tv->SetMaxBytes(maxNum);
498 }
499 
500 /**
501  * SetMargin
502  *
503  * @param brect, margin values in rect
504  * @return void
505  */
506 void
_SetMargin(BRect margin)507 MarginView::_SetMargin(BRect margin)
508 {
509 	fMargins = margin;
510 }
511 
512 
513 /**
514  * SetUnits, called by the MarginMgr when the units popup is selected
515  *
516  * @param uint32, the enum that identifies the units requested to change to.
517  * @return void
518  */
519 void
_SetMarginUnit(MarginUnit unit)520 MarginView::_SetMarginUnit(MarginUnit unit)
521 {
522 	// do nothing if the current units are the same as requested
523 	if (unit == fMarginUnit) {
524 		return;
525 	}
526 
527 	// set the units Format
528 	fUnitValue = kUnitFormat[unit];
529 
530 	// convert the field text to values
531 	float top = atof(fTop->Text());
532 	float right = atof(fRight->Text());
533 	float left = atof(fLeft->Text());
534 	float bottom = atof(fBottom->Text());
535 
536 	// convert to target units
537 	switch (fMarginUnit)
538 	{
539 		case kUnitInch:
540 			// convert to points
541 			top *= kInchUnits;
542 			right *= kInchUnits;
543 			left *= kInchUnits;
544 			bottom *= kInchUnits;
545 			// check for target unit is cm
546 			if (unit == kUnitCM) {
547 				top /= kCMUnits;
548 				right /= kCMUnits;
549 				left /= kCMUnits;
550 				bottom /= kCMUnits;
551 			}
552 			break;
553 		case kUnitCM:
554 			// convert to points
555 			top *= kCMUnits;
556 			right *= kCMUnits;
557 			left *= kCMUnits;
558 			bottom *= kCMUnits;
559 			// check for target unit is inches
560 			if (unit == kUnitInch) {
561 				top /= kInchUnits;
562 				right /= kInchUnits;
563 				left /= kInchUnits;
564 				bottom /= kInchUnits;
565 			}
566 			break;
567 		case kUnitPoint:
568 			// check for target unit is cm
569 			if (unit == kUnitCM) {
570 				top /= kCMUnits;
571 				right /= kCMUnits;
572 				left /= kCMUnits;
573 				bottom /= kCMUnits;
574 			}
575 			// check for target unit is inches
576 			if (unit == kUnitInch) {
577 				top /= kInchUnits;
578 				right /= kInchUnits;
579 				left /= kInchUnits;
580 				bottom /= kInchUnits;
581 			}
582 			break;
583 	}
584 	fMarginUnit = unit;
585 
586 	// lock Window since these changes are from another thread
587 	Window()->Lock();
588 
589 	// set the fields to new units
590 	BString str;
591 	str << top;
592 	fTop->SetText(str.String());
593 
594 	str = "";
595 	str << left;
596 	fLeft->SetText(str.String());
597 
598 	str = "";
599 	str << right;
600 	fRight->SetText(str.String());
601 
602 	str = "";
603 	str << bottom;
604 	fBottom->SetText(str.String());
605 
606 	// update UI
607 	Invalidate();
608 
609 	Window()->Unlock();
610 }
611 
612