1 /* 2 * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "SudokuView.h" 8 9 #include "SudokuField.h" 10 #include "SudokuSolver.h" 11 12 #include <ctype.h> 13 #include <errno.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 #include <Application.h> 18 #include <Beep.h> 19 #include <File.h> 20 #include <Path.h> 21 22 23 const uint32 kMsgCheckSolved = 'chks'; 24 25 const uint32 kStrongLineSize = 2; 26 27 28 SudokuView::SudokuView(BRect frame, const char* name, 29 const BMessage& settings, uint32 resizingMode) 30 : BView(frame, name, resizingMode, 31 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), 32 fField(NULL), 33 fShowHintX(~0UL), 34 fLastHintValue(~0UL), 35 fLastField(~0UL), 36 fKeyboardX(0), 37 fKeyboardY(0), 38 fShowKeyboardFocus(false), 39 fEditable(true) 40 { 41 BMessage field; 42 if (settings.FindMessage("field", &field) == B_OK) { 43 fField = new SudokuField(&field); 44 if (fField->InitCheck() != B_OK) { 45 delete fField; 46 fField = NULL; 47 } else if (fField->IsSolved()) 48 ClearAll(); 49 } 50 if (fField == NULL) 51 fField = new SudokuField(3); 52 53 fBlockSize = fField->BlockSize(); 54 55 if (settings.FindInt32("hint flags", (int32*)&fHintFlags) != B_OK) 56 fHintFlags = kMarkInvalid; 57 if (settings.FindBool("show cursor", &fShowCursor) != B_OK) 58 fShowCursor = false; 59 60 SetViewColor(B_TRANSPARENT_COLOR); 61 // to avoid flickering 62 rgb_color color = { 255, 255, 240 }; 63 fBackgroundColor = color; 64 SetLowColor(color); 65 FrameResized(0, 0); 66 } 67 68 69 SudokuView::~SudokuView() 70 { 71 delete fField; 72 } 73 74 75 status_t 76 SudokuView::SaveState(BMessage& state) 77 { 78 BMessage field; 79 status_t status = fField->Archive(&field, true); 80 if (status == B_OK) 81 status = state.AddMessage("field", &field); 82 if (status == B_OK) 83 status = state.AddInt32("hint flags", fHintFlags); 84 if (status == B_OK) 85 status = state.AddBool("show cursor", fShowCursor); 86 87 return status; 88 } 89 90 91 status_t 92 SudokuView::_FilterString(const char* data, size_t dataLength, char* buffer, 93 uint32& out, bool& ignore) 94 { 95 uint32 maxOut = fField->Size() * fField->Size(); 96 97 for (uint32 i = 0; data[i] && i < dataLength; i++) { 98 if (data[i] == '#') 99 ignore = true; 100 else if (data[i] == '\n') 101 ignore = false; 102 103 if (ignore || isspace(data[i])) 104 continue; 105 106 if (!_ValidCharacter(data[i])) { 107 return B_BAD_VALUE; 108 } 109 110 buffer[out++] = data[i]; 111 if (out == maxOut) 112 break; 113 } 114 115 buffer[out] = '\0'; 116 return B_OK; 117 } 118 119 120 status_t 121 SudokuView::SetTo(entry_ref& ref) 122 { 123 BPath path; 124 status_t status = path.SetTo(&ref); 125 if (status < B_OK) 126 return status; 127 128 FILE* file = fopen(path.Path(), "r"); 129 if (file == NULL) 130 return errno; 131 132 uint32 maxOut = fField->Size() * fField->Size(); 133 char buffer[1024]; 134 char line[1024]; 135 bool ignore = false; 136 uint32 out = 0; 137 138 while (fgets(line, sizeof(line), file) != NULL 139 && out < maxOut) { 140 status = _FilterString(line, sizeof(line), buffer, out, ignore); 141 if (status < B_OK) { 142 fclose(file); 143 return status; 144 } 145 } 146 147 _PushUndo(); 148 status = fField->SetTo(_BaseCharacter(), buffer); 149 Invalidate(); 150 return status; 151 } 152 153 154 status_t 155 SudokuView::SetTo(const char* data) 156 { 157 if (data == NULL) 158 return B_BAD_VALUE; 159 160 char buffer[1024]; 161 bool ignore = false; 162 uint32 out = 0; 163 164 status_t status = _FilterString(data, 65536, buffer, out, ignore); 165 if (status < B_OK) 166 return B_BAD_VALUE; 167 168 _PushUndo(); 169 status = fField->SetTo(_BaseCharacter(), buffer); 170 Invalidate(); 171 return status; 172 } 173 174 175 status_t 176 SudokuView::SetTo(SudokuField* field) 177 { 178 if (field == NULL || field == fField) 179 return B_BAD_VALUE; 180 181 _PushUndo(); 182 delete fField; 183 fField = field; 184 185 fBlockSize = fField->BlockSize(); 186 FrameResized(0, 0); 187 Invalidate(); 188 return B_OK; 189 } 190 191 192 status_t 193 SudokuView::SaveTo(entry_ref& ref, bool asText) 194 { 195 BFile file; 196 status_t status = file.SetTo(&ref, B_WRITE_ONLY | B_CREATE_FILE 197 | B_ERASE_FILE); 198 if (status < B_OK) 199 return status; 200 201 if (asText) { 202 char line[1024]; 203 strcpy(line, "# Written by Sudoku\n\n"); 204 file.Write(line, strlen(line)); 205 206 uint32 i = 0; 207 208 for (uint32 y = 0; y < fField->Size(); y++) { 209 for (uint32 x = 0; x < fField->Size(); x++) { 210 if (x != 0 && x % fBlockSize == 0) 211 line[i++] = ' '; 212 _SetText(&line[i++], fField->ValueAt(x, y)); 213 } 214 line[i++] = '\n'; 215 } 216 217 file.Write(line, i); 218 } else { 219 } 220 221 return status; 222 } 223 224 225 void 226 SudokuView::ClearChanged() 227 { 228 _PushUndo(); 229 230 for (uint32 y = 0; y < fField->Size(); y++) { 231 for (uint32 x = 0; x < fField->Size(); x++) { 232 if ((fField->FlagsAt(x, y) & kInitialValue) == 0) 233 fField->SetValueAt(x, y, 0); 234 } 235 } 236 237 Invalidate(); 238 } 239 240 241 void 242 SudokuView::ClearAll() 243 { 244 _PushUndo(); 245 fField->Reset(); 246 Invalidate(); 247 } 248 249 250 void 251 SudokuView::SetHintFlags(uint32 flags) 252 { 253 if (flags == fHintFlags) 254 return; 255 256 if ((flags & kMarkInvalid) ^ (fHintFlags & kMarkInvalid)) 257 Invalidate(); 258 259 fHintFlags = flags; 260 } 261 262 263 void 264 SudokuView::SetEditable(bool editable) 265 { 266 fEditable = editable; 267 } 268 269 270 void 271 SudokuView::AttachedToWindow() 272 { 273 MakeFocus(true); 274 } 275 276 277 void 278 SudokuView::_FitFont(BFont& font, float fieldWidth, float fieldHeight) 279 { 280 font.SetSize(100); 281 282 font_height fontHeight; 283 font.GetHeight(&fontHeight); 284 285 float width = font.StringWidth("W"); 286 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 287 288 float factor = fieldWidth != fHintWidth ? 4.f / 5.f : 1.f; 289 float widthFactor = fieldWidth / (width / factor); 290 float heightFactor = fieldHeight / (height / factor); 291 font.SetSize(100 * min_c(widthFactor, heightFactor)); 292 } 293 294 295 void 296 SudokuView::FrameResized(float /*width*/, float /*height*/) 297 { 298 // font for numbers 299 300 uint32 size = fField->Size(); 301 fWidth = (Bounds().Width() - kStrongLineSize * (fBlockSize - 1)) / size; 302 fHeight = (Bounds().Height() - kStrongLineSize * (fBlockSize - 1)) / size; 303 _FitFont(fFieldFont, fWidth - 2, fHeight - 2); 304 305 font_height fontHeight; 306 fFieldFont.GetHeight(&fontHeight); 307 fBaseline = ceilf(fontHeight.ascent) / 2 308 + (fHeight - ceilf(fontHeight.descent)) / 2; 309 310 // font for hint 311 312 fHintWidth = (fWidth - 2) / fBlockSize; 313 fHintHeight = (fHeight - 2) / fBlockSize; 314 _FitFont(fHintFont, fHintWidth, fHintHeight); 315 316 fHintFont.GetHeight(&fontHeight); 317 fHintBaseline = ceilf(fontHeight.ascent) / 2 318 + (fHintHeight - ceilf(fontHeight.descent)) / 2; 319 } 320 321 322 BPoint 323 SudokuView::_LeftTop(uint32 x, uint32 y) 324 { 325 return BPoint(x * fWidth + x / fBlockSize * kStrongLineSize + 1, 326 y * fHeight + y / fBlockSize * kStrongLineSize + 1); 327 } 328 329 330 BRect 331 SudokuView::_Frame(uint32 x, uint32 y) 332 { 333 BPoint leftTop = _LeftTop(x, y); 334 BPoint rightBottom = leftTop + BPoint(fWidth - 2, fHeight - 2); 335 336 return BRect(leftTop, rightBottom); 337 } 338 339 340 void 341 SudokuView::_InvalidateHintField(uint32 x, uint32 y, uint32 hintX, 342 uint32 hintY) 343 { 344 BPoint leftTop = _LeftTop(x, y); 345 leftTop.x += hintX * fHintWidth; 346 leftTop.y += hintY * fHintHeight; 347 BPoint rightBottom = leftTop; 348 rightBottom.x += fHintWidth; 349 rightBottom.y += fHintHeight; 350 351 Invalidate(BRect(leftTop, rightBottom)); 352 } 353 354 355 void 356 SudokuView::_InvalidateField(uint32 x, uint32 y) 357 { 358 Invalidate(_Frame(x, y)); 359 } 360 361 362 void 363 SudokuView::_InvalidateKeyboardFocus(uint32 x, uint32 y) 364 { 365 BRect frame = _Frame(x, y); 366 frame.InsetBy(-1, -1); 367 Invalidate(frame); 368 } 369 370 371 bool 372 SudokuView::_GetHintFieldFor(BPoint where, uint32 x, uint32 y, 373 uint32& hintX, uint32& hintY) 374 { 375 BPoint leftTop = _LeftTop(x, y); 376 hintX = (uint32)floor((where.x - leftTop.x) / fHintWidth); 377 hintY = (uint32)floor((where.y - leftTop.y) / fHintHeight); 378 379 if (hintX >= fBlockSize || hintY >= fBlockSize) 380 return false; 381 382 return true; 383 } 384 385 386 bool 387 SudokuView::_GetFieldFor(BPoint where, uint32& x, uint32& y) 388 { 389 float block = fWidth * fBlockSize + kStrongLineSize; 390 x = (uint32)floor(where.x / block); 391 uint32 offsetX = (uint32)floor((where.x - x * block) / fWidth); 392 x = x * fBlockSize + offsetX; 393 394 block = fHeight * fBlockSize + kStrongLineSize; 395 y = (uint32)floor(where.y / block); 396 uint32 offsetY = (uint32)floor((where.y - y * block) / fHeight); 397 y = y * fBlockSize + offsetY; 398 399 if (offsetX >= fBlockSize || offsetY >= fBlockSize 400 || x >= fField->Size() || y >= fField->Size()) 401 return false; 402 403 return true; 404 } 405 406 407 void 408 SudokuView::_RemoveHint() 409 { 410 if (fShowHintX == ~0UL) 411 return; 412 413 uint32 x = fShowHintX; 414 uint32 y = fShowHintY; 415 fShowHintX = ~0; 416 fShowHintY = ~0; 417 418 _InvalidateField(x, y); 419 } 420 421 422 void 423 SudokuView::_UndoRedo(BObjectList<BMessage>& undos, 424 BObjectList<BMessage>& redos) 425 { 426 if (undos.IsEmpty()) 427 return; 428 429 BMessage* undo = undos.RemoveItemAt(undos.CountItems() - 1); 430 431 BMessage* redo = new BMessage; 432 if (fField->Archive(redo, true) == B_OK) 433 redos.AddItem(redo); 434 435 SudokuField field(undo); 436 delete undo; 437 438 fField->SetTo(&field); 439 440 SendNotices(kUndoRedoChanged); 441 Invalidate(); 442 } 443 444 445 void 446 SudokuView::Undo() 447 { 448 _UndoRedo(fUndos, fRedos); 449 } 450 451 452 void 453 SudokuView::Redo() 454 { 455 _UndoRedo(fRedos, fUndos); 456 } 457 458 459 void 460 SudokuView::_PushUndo() 461 { 462 fRedos.MakeEmpty(); 463 464 BMessage* undo = new BMessage; 465 if (fField->Archive(undo, true) == B_OK 466 && fUndos.AddItem(undo)) 467 SendNotices(kUndoRedoChanged); 468 } 469 470 471 void 472 SudokuView::MouseDown(BPoint where) 473 { 474 uint32 x, y; 475 if (!fEditable || !_GetFieldFor(where, x, y)) 476 return; 477 478 int32 buttons = B_PRIMARY_MOUSE_BUTTON; 479 int32 clicks = 1; 480 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) { 481 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 482 Looper()->CurrentMessage()->FindInt32("clicks", &clicks); 483 } 484 485 uint32 hintX, hintY; 486 if (!_GetHintFieldFor(where, x, y, hintX, hintY)) 487 return; 488 489 uint32 value = hintX + hintY * fBlockSize; 490 uint32 field = x + y * fField->Size(); 491 _PushUndo(); 492 493 if (clicks == 2 && fLastHintValue == value && fLastField == field 494 || (buttons & (B_SECONDARY_MOUSE_BUTTON 495 | B_TERTIARY_MOUSE_BUTTON)) != 0) { 496 // double click or other buttons set a value 497 if ((fField->FlagsAt(x, y) & kInitialValue) == 0) { 498 if (fField->ValueAt(x, y) > 0) { 499 fField->SetValueAt(x, y, 0); 500 fShowHintX = x; 501 fShowHintY = y; 502 } else { 503 fField->SetValueAt(x, y, value + 1); 504 BMessenger(this).SendMessage(kMsgCheckSolved); 505 } 506 507 _InvalidateField(x, y); 508 509 // allow dragging to remove the hint from other fields 510 fLastHintValueSet = false; 511 fLastHintValue = value; 512 fLastField = field; 513 } 514 return; 515 } 516 517 uint32 hintMask = fField->HintMaskAt(x, y); 518 uint32 valueMask = 1UL << value; 519 fLastHintValueSet = (hintMask & valueMask) == 0; 520 521 if (fLastHintValueSet) 522 hintMask |= valueMask; 523 else 524 hintMask &= ~valueMask; 525 526 fField->SetHintMaskAt(x, y, hintMask); 527 _InvalidateHintField(x, y, hintX, hintY); 528 529 fLastHintValue = value; 530 fLastField = field; 531 } 532 533 534 void 535 SudokuView::MouseMoved(BPoint where, uint32 transit, 536 const BMessage* dragMessage) 537 { 538 if (transit == B_EXITED_VIEW || dragMessage != NULL) { 539 _RemoveHint(); 540 return; 541 } 542 543 if (fShowKeyboardFocus) { 544 fShowKeyboardFocus = false; 545 _InvalidateKeyboardFocus(fKeyboardX, fKeyboardY); 546 } 547 548 uint32 x, y; 549 if (!_GetFieldFor(where, x, y) 550 || (fField->FlagsAt(x, y) & kInitialValue) != 0 551 || !fShowCursor && fField->ValueAt(x, y) != 0) { 552 _RemoveHint(); 553 return; 554 } 555 556 if (fShowHintX == x && fShowHintY == y) 557 return; 558 559 int32 buttons = 0; 560 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 561 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 562 563 uint32 field = x + y * fField->Size(); 564 565 if (buttons != 0 && field != fLastField) { 566 // if a button is pressed, we drag the last hint selection 567 // (either set or removal) to the field under the mouse 568 uint32 hintMask = fField->HintMaskAt(x, y); 569 uint32 valueMask = 1UL << fLastHintValue; 570 if (fLastHintValueSet) 571 hintMask |= valueMask; 572 else 573 hintMask &= ~valueMask; 574 575 fField->SetHintMaskAt(x, y, hintMask); 576 } 577 578 _RemoveHint(); 579 fShowHintX = x; 580 fShowHintY = y; 581 _InvalidateField(x, y); 582 } 583 584 585 void 586 SudokuView::_InsertKey(char rawKey, int32 modifiers) 587 { 588 if (!fEditable || !_ValidCharacter(rawKey) 589 || (fField->FlagsAt(fKeyboardX, fKeyboardY) & kInitialValue) != 0) 590 return; 591 592 uint32 value = rawKey - _BaseCharacter(); 593 594 if (modifiers & (B_SHIFT_KEY | B_OPTION_KEY)) { 595 // set or remove hint 596 if (value == 0) 597 return; 598 599 _PushUndo(); 600 uint32 hintMask = fField->HintMaskAt(fKeyboardX, fKeyboardY); 601 uint32 valueMask = 1UL << (value - 1); 602 if (modifiers & B_OPTION_KEY) 603 hintMask &= ~valueMask; 604 else 605 hintMask |= valueMask; 606 607 fField->SetValueAt(fKeyboardX, fKeyboardY, 0); 608 fField->SetHintMaskAt(fKeyboardX, fKeyboardY, hintMask); 609 } else { 610 _PushUndo(); 611 fField->SetValueAt(fKeyboardX, fKeyboardY, value); 612 if (value) 613 BMessenger(this).SendMessage(kMsgCheckSolved); 614 } 615 } 616 617 618 void 619 SudokuView::KeyDown(const char *bytes, int32 /*numBytes*/) 620 { 621 be_app->ObscureCursor(); 622 623 uint32 x = fKeyboardX, y = fKeyboardY; 624 625 switch (bytes[0]) { 626 case B_UP_ARROW: 627 if (fKeyboardY == 0) 628 fKeyboardY = fField->Size() - 1; 629 else 630 fKeyboardY--; 631 break; 632 case B_DOWN_ARROW: 633 if (fKeyboardY == fField->Size() - 1) 634 fKeyboardY = 0; 635 else 636 fKeyboardY++; 637 break; 638 639 case B_LEFT_ARROW: 640 if (fKeyboardX == 0) 641 fKeyboardX = fField->Size() - 1; 642 else 643 fKeyboardX--; 644 break; 645 case B_RIGHT_ARROW: 646 if (fKeyboardX == fField->Size() - 1) 647 fKeyboardX = 0; 648 else 649 fKeyboardX++; 650 break; 651 652 case B_BACKSPACE: 653 case B_DELETE: 654 case B_SPACE: 655 // clear value 656 _InsertKey(_BaseCharacter(), 0); 657 break; 658 659 default: 660 int32 rawKey = bytes[0]; 661 int32 modifiers = 0; 662 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) { 663 Looper()->CurrentMessage()->FindInt32("raw_char", &rawKey); 664 Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers); 665 } 666 667 _InsertKey(rawKey, modifiers); 668 break; 669 } 670 671 if (!fShowKeyboardFocus && fShowHintX != ~0UL) { 672 // always start at last mouse position, if any 673 fKeyboardX = fShowHintX; 674 fKeyboardY = fShowHintY; 675 } 676 677 _RemoveHint(); 678 679 // remove old focus, if any 680 if (fShowKeyboardFocus && (x != fKeyboardX || y != fKeyboardY)) 681 _InvalidateKeyboardFocus(x, y); 682 683 fShowKeyboardFocus = true; 684 _InvalidateKeyboardFocus(fKeyboardX, fKeyboardY); 685 } 686 687 688 void 689 SudokuView::MessageReceived(BMessage* message) 690 { 691 switch (message->what) { 692 case kMsgCheckSolved: 693 if (fField->IsSolved()) { 694 // notify window 695 Looper()->PostMessage(kMsgSudokuSolved); 696 } 697 break; 698 699 case B_UNDO: 700 Undo(); 701 break; 702 703 case B_REDO: 704 Redo(); 705 break; 706 707 case kMsgSolveSudoku: 708 { 709 SudokuSolver solver; 710 solver.SetTo(fField); 711 bigtime_t start = system_time(); 712 solver.ComputeSolutions(); 713 printf("found %ld solutions in %g msecs\n", 714 solver.CountSolutions(), (system_time() - start) / 1000.0); 715 if (solver.CountSolutions() > 0) { 716 _PushUndo(); 717 fField->SetTo(solver.SolutionAt(0)); 718 Invalidate(); 719 } else 720 beep(); 721 break; 722 } 723 724 case kMsgSolveSingle: 725 { 726 if (fField->IsSolved()) { 727 beep(); 728 break; 729 } 730 731 SudokuSolver solver; 732 solver.SetTo(fField); 733 bigtime_t start = system_time(); 734 solver.ComputeSolutions(); 735 printf("found %ld solutions in %g msecs\n", 736 solver.CountSolutions(), (system_time() - start) / 1000.0); 737 if (solver.CountSolutions() > 0) { 738 _PushUndo(); 739 740 // find free spot 741 uint32 x, y; 742 do { 743 x = rand() % fField->Size(); 744 y = rand() % fField->Size(); 745 } while (fField->ValueAt(x, y)); 746 747 fField->SetValueAt(x, y, 748 solver.SolutionAt(0)->ValueAt(x, y)); 749 _InvalidateField(x, y); 750 } else 751 beep(); 752 break; 753 } 754 755 default: 756 BView::MessageReceived(message); 757 break; 758 } 759 } 760 761 762 char 763 SudokuView::_BaseCharacter() 764 { 765 return fField->Size() > 9 ? '@' : '0'; 766 } 767 768 769 bool 770 SudokuView::_ValidCharacter(char c) 771 { 772 char min = _BaseCharacter(); 773 char max = min + fField->Size(); 774 return c >= min && c <= max; 775 } 776 777 778 void 779 SudokuView::_SetText(char* text, uint32 value) 780 { 781 text[0] = value + _BaseCharacter(); 782 text[1] = '\0'; 783 } 784 785 786 void 787 SudokuView::_DrawKeyboardFocus() 788 { 789 BRect frame = _Frame(fKeyboardX, fKeyboardY); 790 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 791 StrokeRect(frame); 792 frame.InsetBy(-1, -1); 793 StrokeRect(frame); 794 frame.InsetBy(2, 2); 795 StrokeRect(frame); 796 } 797 798 799 void 800 SudokuView::_DrawHints(uint32 x, uint32 y) 801 { 802 bool showAll = fShowHintX == x && fShowHintY == y; 803 uint32 hintMask = fField->HintMaskAt(x, y); 804 if (hintMask == 0 && !showAll) 805 return; 806 807 uint32 validMask = fField->ValidMaskAt(x, y); 808 BPoint leftTop = _LeftTop(x, y); 809 SetFont(&fHintFont); 810 811 for (uint32 j = 0; j < fBlockSize; j++) { 812 for (uint32 i = 0; i < fBlockSize; i++) { 813 uint32 value = j * fBlockSize + i; 814 if (hintMask & (1UL << value)) 815 SetHighColor(200, 0, 0); 816 else { 817 if (!showAll) 818 continue; 819 820 if ((fHintFlags & kMarkValidHints) == 0 821 || validMask & (1UL << value)) 822 SetHighColor(110, 110, 80); 823 else 824 SetHighColor(180, 180, 120); 825 } 826 827 char text[2]; 828 _SetText(text, value + 1); 829 DrawString(text, leftTop + BPoint((i + 0.5f) * fHintWidth 830 - StringWidth(text) / 2, floorf(j * fHintHeight) 831 + fHintBaseline)); 832 } 833 } 834 } 835 836 837 void 838 SudokuView::Draw(BRect /*updateRect*/) 839 { 840 // draw one pixel border otherwise not covered 841 // by lines and fields 842 SetLowColor(fBackgroundColor); 843 StrokeRect(Bounds(), B_SOLID_LOW); 844 845 // draw lines 846 847 uint32 size = fField->Size(); 848 849 SetHighColor(0, 0, 0); 850 851 float width = fWidth; 852 for (uint32 x = 1; x < size; x++) { 853 if (x % fBlockSize == 0) { 854 FillRect(BRect(width, 0, width + kStrongLineSize, 855 Bounds().Height())); 856 width += kStrongLineSize; 857 } else { 858 StrokeLine(BPoint(width, 0), BPoint(width, Bounds().Height())); 859 } 860 width += fWidth; 861 } 862 863 float height = fHeight; 864 for (uint32 y = 1; y < size; y++) { 865 if (y % fBlockSize == 0) { 866 FillRect(BRect(0, height, Bounds().Width(), 867 height + kStrongLineSize)); 868 height += kStrongLineSize; 869 } else { 870 StrokeLine(BPoint(0, height), BPoint(Bounds().Width(), height)); 871 } 872 height += fHeight; 873 } 874 875 // draw text 876 877 for (uint32 y = 0; y < size; y++) { 878 for (uint32 x = 0; x < size; x++) { 879 if ((fShowCursor && x == fShowHintX && y == fShowHintY 880 || fShowKeyboardFocus && x == fKeyboardX 881 && y == fKeyboardY) 882 && (fField->FlagsAt(x, y) & kInitialValue) == 0) { 883 //SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 884 SetLowColor(255, 255, 210); 885 FillRect(_Frame(x, y), B_SOLID_LOW); 886 } else { 887 SetLowColor(fBackgroundColor); 888 FillRect(_Frame(x, y), B_SOLID_LOW); 889 } 890 891 if (fShowKeyboardFocus && x == fKeyboardX && y == fKeyboardY) 892 _DrawKeyboardFocus(); 893 894 uint32 value = fField->ValueAt(x, y); 895 if (value == 0) { 896 _DrawHints(x, y); 897 continue; 898 } 899 900 SetFont(&fFieldFont); 901 if (fField->FlagsAt(x, y) & kInitialValue) 902 SetHighColor(0, 0, 0); 903 else { 904 if ((fHintFlags & kMarkInvalid) == 0 905 || fField->ValidMaskAt(x, y) & (1UL << (value - 1))) 906 SetHighColor(0, 0, 200); 907 else 908 SetHighColor(200, 0, 0); 909 } 910 911 char text[2]; 912 _SetText(text, value); 913 DrawString(text, _LeftTop(x, y) 914 + BPoint((fWidth - StringWidth(text)) / 2, fBaseline)); 915 } 916 } 917 } 918 919 920