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 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 90 PageView::SetPageSize(float pageWidth, float pageHeight) 91 { 92 fPageWidth = pageWidth; 93 fPageHeight = pageHeight; 94 } 95 96 97 void 98 PageView::SetMargins(BRect margins) 99 { 100 fMargins = margins; 101 } 102 103 104 void 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 */ 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 */ 175 MarginView::~MarginView() 176 { 177 } 178 179 180 /** 181 * AttachToWindow 182 * 183 * @param none 184 * @return void 185 */ 186 void 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 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 265 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 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 293 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 337 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 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 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 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 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 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