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