1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2009, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "SourceView.h" 9 10 #include <algorithm> 11 #include <new> 12 13 #include <ctype.h> 14 #include <stdio.h> 15 16 #include <Clipboard.h> 17 #include <LayoutUtils.h> 18 #include <Looper.h> 19 #include <Message.h> 20 #include <MessageRunner.h> 21 #include <Polygon.h> 22 #include <Region.h> 23 #include <ScrollBar.h> 24 #include <ScrollView.h> 25 26 #include <AutoLocker.h> 27 #include <ObjectList.h> 28 29 #include "Breakpoint.h" 30 #include "DisassembledCode.h" 31 #include "Function.h" 32 #include "FileSourceCode.h" 33 #include "LocatableFile.h" 34 #include "MessageCodes.h" 35 #include "SourceLanguage.h" 36 #include "StackTrace.h" 37 #include "Statement.h" 38 #include "Team.h" 39 #include "Tracing.h" 40 41 42 static const int32 kLeftTextMargin = 3; 43 static const float kMinViewHeight = 80.0f; 44 static const int32 kSpacesPerTab = 4; 45 // TODO: Should be settable! 46 static const bigtime_t kScrollTimer = 10000LL; 47 48 49 class SourceView::BaseView : public BView { 50 public: 51 BaseView(const char* name, 52 SourceView* sourceView, FontInfo* fontInfo); 53 54 virtual void SetSourceCode(SourceCode* sourceCode); 55 56 virtual BSize PreferredSize(); 57 58 protected: 59 inline int32 LineCount() const; 60 61 inline float TotalHeight() const; 62 63 int32 LineAtOffset(float yOffset) const; 64 void GetLineRange(BRect rect, int32& minLine, 65 int32& maxLine) const; 66 BRect LineRect(uint32 line) const; 67 68 protected: 69 SourceView* fSourceView; 70 FontInfo* fFontInfo; 71 SourceCode* fSourceCode; 72 }; 73 74 75 class SourceView::MarkerManager { 76 public: 77 MarkerManager(SourceView* sourceView, 78 Team* team, Listener* listener); 79 80 void SetSourceCode(SourceCode* sourceCode); 81 82 void SetStackTrace(StackTrace* stackTrace); 83 void SetStackFrame(StackFrame* stackFrame); 84 85 void UserBreakpointChanged( 86 UserBreakpoint* breakpoint); 87 88 struct Marker; 89 struct InstructionPointerMarker; 90 struct BreakpointMarker; 91 92 template<typename MarkerType> struct MarkerByLinePredicate; 93 94 typedef BObjectList<Marker> MarkerList; 95 typedef BObjectList<BreakpointMarker> BreakpointMarkerList; 96 97 void GetMarkers(uint32 minLine, uint32 maxLine, 98 MarkerList& markers); 99 BreakpointMarker* BreakpointMarkerAtLine(uint32 line); 100 101 private: 102 void _InvalidateIPMarkers(); 103 void _InvalidateBreakpointMarkers(); 104 void _UpdateIPMarkers(); 105 void _UpdateBreakpointMarkers(); 106 107 // TODO: "public" to workaround a GCC2 problem: 108 public: 109 static int _CompareMarkers(const Marker* a, 110 const Marker* b); 111 static int _CompareBreakpointMarkers( 112 const BreakpointMarker* a, 113 const BreakpointMarker* b); 114 115 template<typename MarkerType> 116 static int _CompareLineMarkerTemplate(const uint32* line, 117 const MarkerType* marker); 118 static int _CompareLineMarker(const uint32* line, 119 const Marker* marker); 120 static int _CompareLineBreakpointMarker( 121 const uint32* line, 122 const BreakpointMarker* marker); 123 124 private: 125 Team* fTeam; 126 Listener* fListener; 127 SourceCode* fSourceCode; 128 StackTrace* fStackTrace; 129 StackFrame* fStackFrame; 130 MarkerList fIPMarkers; 131 BreakpointMarkerList fBreakpointMarkers; 132 bool fIPMarkersValid; 133 bool fBreakpointMarkersValid; 134 135 }; 136 137 138 class SourceView::MarkerView : public BaseView { 139 public: 140 MarkerView(SourceView* sourceView, Team* team, 141 Listener* listener, MarkerManager *manager, 142 FontInfo* fontInfo); 143 ~MarkerView(); 144 145 virtual void SetSourceCode(SourceCode* sourceCode); 146 147 void SetStackTrace(StackTrace* stackTrace); 148 void SetStackFrame(StackFrame* stackFrame); 149 150 void UserBreakpointChanged( 151 UserBreakpoint* breakpoint); 152 153 virtual BSize MinSize(); 154 virtual BSize MaxSize(); 155 156 virtual void Draw(BRect updateRect); 157 158 virtual void MouseDown(BPoint where); 159 160 161 private: 162 Team* fTeam; 163 Listener* fListener; 164 MarkerManager* fMarkerManager; 165 StackTrace* fStackTrace; 166 StackFrame* fStackFrame; 167 rgb_color fBreakpointOptionMarker; 168 }; 169 170 171 struct SourceView::MarkerManager::Marker { 172 Marker(uint32 line); 173 virtual ~Marker(); 174 175 inline uint32 Line() const; 176 177 virtual void Draw(BView* view, BRect rect) = 0; 178 179 private: 180 uint32 fLine; 181 }; 182 183 184 struct SourceView::MarkerManager::InstructionPointerMarker : Marker { 185 InstructionPointerMarker(uint32 line, 186 bool topIP, bool currentIP); 187 188 virtual void Draw(BView* view, BRect rect); 189 190 private: 191 void _DrawArrow(BView* view, BPoint tip, BSize size, 192 BSize base, const rgb_color& color, 193 bool fill); 194 195 private: 196 bool fIsTopIP; 197 bool fIsCurrentIP; 198 }; 199 200 201 struct SourceView::MarkerManager::BreakpointMarker : Marker { 202 BreakpointMarker(uint32 line, 203 target_addr_t address, bool enabled); 204 205 target_addr_t Address() const { return fAddress; } 206 bool IsEnabled() const { return fEnabled; } 207 208 virtual void Draw(BView* view, BRect rect); 209 210 private: 211 target_addr_t fAddress; 212 bool fEnabled; 213 }; 214 215 216 template<typename MarkerType> 217 struct SourceView::MarkerManager::MarkerByLinePredicate 218 : UnaryPredicate<MarkerType> { 219 MarkerByLinePredicate(uint32 line) 220 : 221 fLine(line) 222 { 223 } 224 225 virtual int operator()(const MarkerType* marker) const 226 { 227 return -_CompareLineMarkerTemplate<MarkerType>(&fLine, marker); 228 } 229 230 private: 231 uint32 fLine; 232 }; 233 234 235 class SourceView::TextView : public BaseView { 236 public: 237 TextView(SourceView* sourceView, 238 MarkerManager* manager, 239 FontInfo* fontInfo); 240 241 virtual void SetSourceCode(SourceCode* sourceCode); 242 void UserBreakpointChanged( 243 UserBreakpoint* breakpoint); 244 245 virtual BSize MinSize(); 246 virtual BSize MaxSize(); 247 248 virtual void Draw(BRect updateRect); 249 250 virtual void KeyDown(const char* bytes, int32 numBytes); 251 virtual void MakeFocus(bool isFocused); 252 virtual void MessageReceived(BMessage* message); 253 virtual void MouseDown(BPoint where); 254 virtual void MouseMoved(BPoint where, uint32 transit, 255 const BMessage* dragMessage); 256 virtual void MouseUp(BPoint where); 257 258 private: 259 struct SelectionPoint 260 { 261 SelectionPoint(int32 _line, int32 _offset) 262 { 263 line = _line; 264 offset = _offset; 265 } 266 267 bool operator==(const SelectionPoint& other) 268 { 269 return line == other.line && offset == other.offset; 270 } 271 272 int32 line; 273 int32 offset; 274 }; 275 276 enum TrackingState 277 { 278 kNotTracking = 0, 279 kTracking = 1, 280 kDragging = 2 281 }; 282 283 float _MaxLineWidth(); 284 void _FormatLine(const char* line, 285 BString& formattedLine); 286 inline int32 _NextTabStop(int32 column) const; 287 float _FormattedPosition(int32 line, 288 int32 offset) const; 289 SelectionPoint _SelectionPointAt(BPoint where) const; 290 void _GetSelectionRegion(BRegion& region) const; 291 void _GetSelectionText(BString& text) const; 292 void _CopySelectionToClipboard() const; 293 void _SelectWordAt(const SelectionPoint& point, 294 bool extend = false); 295 void _SelectLineAt(const SelectionPoint& point, 296 bool extend = false); 297 void _HandleAutoScroll(); 298 void _ScrollHorizontal(int32 charCount); 299 void _ScrollByLines(int32 lineCount); 300 void _ScrollByPages(int32 pageCount); 301 void _ScrollToTop(); 302 void _ScrollToBottom(); 303 304 private: 305 306 float fMaxLineWidth; 307 float fCharacterWidth; 308 SelectionPoint fSelectionStart; 309 SelectionPoint fSelectionEnd; 310 SelectionPoint fSelectionBase; 311 SelectionPoint fLastClickPoint; 312 bigtime_t fLastClickTime; 313 int16 fClickCount; 314 rgb_color fTextColor; 315 bool fSelectionMode; 316 TrackingState fTrackState; 317 BMessageRunner* fScrollRunner; 318 MarkerManager* fMarkerManager; 319 }; 320 321 322 // #pragma mark - BaseView 323 324 325 SourceView::BaseView::BaseView(const char* name, SourceView* sourceView, 326 FontInfo* fontInfo) 327 : 328 BView(name, B_WILL_DRAW | B_SUBPIXEL_PRECISE), 329 fSourceView(sourceView), 330 fFontInfo(fontInfo), 331 fSourceCode(NULL) 332 { 333 } 334 335 336 void 337 SourceView::BaseView::SetSourceCode(SourceCode* sourceCode) 338 { 339 fSourceCode = sourceCode; 340 341 InvalidateLayout(); 342 Invalidate(); 343 } 344 345 346 BSize 347 SourceView::BaseView::PreferredSize() 348 { 349 return MinSize(); 350 } 351 352 353 int32 354 SourceView::BaseView::LineCount() const 355 { 356 return fSourceCode != NULL ? fSourceCode->CountLines() : 0; 357 } 358 359 360 float 361 SourceView::BaseView::TotalHeight() const 362 { 363 float height = LineCount() * fFontInfo->lineHeight - 1; 364 return std::max(height, kMinViewHeight); 365 } 366 367 368 int32 369 SourceView::BaseView::LineAtOffset(float yOffset) const 370 { 371 int32 lineCount = LineCount(); 372 if (yOffset < 0 || lineCount == 0) 373 return -1; 374 375 int32 line = (int32)yOffset / (int32)fFontInfo->lineHeight; 376 return line < lineCount ? line : -1; 377 } 378 379 380 void 381 SourceView::BaseView::GetLineRange(BRect rect, int32& minLine, 382 int32& maxLine) const 383 { 384 int32 lineHeight = (int32)fFontInfo->lineHeight; 385 minLine = (int32)rect.top / lineHeight; 386 maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight; 387 minLine = std::max(minLine, (int32)0); 388 maxLine = std::min(maxLine, fSourceCode->CountLines() - 1); 389 } 390 391 392 BRect 393 SourceView::BaseView::LineRect(uint32 line) const 394 { 395 float y = (float)line * fFontInfo->lineHeight; 396 return BRect(0, y, Bounds().right, y + fFontInfo->lineHeight - 1); 397 } 398 399 400 // #pragma mark - MarkerView::Marker 401 402 403 SourceView::MarkerManager::Marker::Marker(uint32 line) 404 : 405 fLine(line) 406 { 407 } 408 409 410 SourceView::MarkerManager::Marker::~Marker() 411 { 412 } 413 414 415 uint32 416 SourceView::MarkerManager::Marker::Line() const 417 { 418 return fLine; 419 } 420 421 422 // #pragma mark - MarkerManager::InstructionPointerMarker 423 424 425 SourceView::MarkerManager::InstructionPointerMarker::InstructionPointerMarker( 426 uint32 line, bool topIP, bool currentIP) 427 : 428 Marker(line), 429 fIsTopIP(topIP), 430 fIsCurrentIP(currentIP) 431 { 432 } 433 434 435 void 436 SourceView::MarkerManager::InstructionPointerMarker::Draw(BView* view, 437 BRect rect) 438 { 439 // Get the arrow color -- for the top IP, if current, we use blue, 440 // otherwise a gray. 441 rgb_color color; 442 if (fIsCurrentIP && fIsTopIP) { 443 color.set_to(0, 0, 255, 255); 444 } else { 445 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 446 B_DARKEN_3_TINT); 447 } 448 449 // Draw a filled array for the current IP, otherwise just an 450 // outline. 451 BPoint tip(rect.right - 3.5f, floorf((rect.top + rect.bottom) / 2)); 452 if (fIsCurrentIP) { 453 _DrawArrow(view, tip, BSize(10, 10), BSize(5, 5), color, true); 454 } else { 455 _DrawArrow(view, tip + BPoint(-0.5f, 0), BSize(9, 8), 456 BSize(5, 4), color, false); 457 } 458 } 459 460 461 void 462 SourceView::MarkerManager::InstructionPointerMarker::_DrawArrow(BView* view, 463 BPoint tip, BSize size, BSize base, const rgb_color& color, bool fill) 464 { 465 view->SetHighColor(color); 466 467 float baseTop = tip.y - base.height / 2; 468 float baseBottom = tip.y + base.height / 2; 469 float top = tip.y - size.height / 2; 470 float bottom = tip.y + size.height / 2; 471 float left = tip.x - size.width; 472 float middle = left + base.width; 473 474 BPoint points[7]; 475 points[0].Set(tip.x, tip.y); 476 points[1].Set(middle, top); 477 points[2].Set(middle, baseTop); 478 points[3].Set(left, baseTop); 479 points[4].Set(left, baseBottom); 480 points[5].Set(middle, baseBottom); 481 points[6].Set(middle, bottom); 482 483 if (fill) 484 view->FillPolygon(points, 7); 485 else 486 view->StrokePolygon(points, 7); 487 } 488 489 490 // #pragma mark - MarkerManager::BreakpointMarker 491 492 493 SourceView::MarkerManager::BreakpointMarker::BreakpointMarker(uint32 line, 494 target_addr_t address, bool enabled) 495 : 496 Marker(line), 497 fAddress(address), 498 fEnabled(enabled) 499 { 500 } 501 502 503 void 504 SourceView::MarkerManager::BreakpointMarker::Draw(BView* view, BRect rect) 505 { 506 float y = (rect.top + rect.bottom) / 2; 507 view->SetHighColor((rgb_color){255, 0, 0, 255}); 508 if (fEnabled) 509 view->FillEllipse(BPoint(rect.right - 8, y), 4, 4); 510 else 511 view->StrokeEllipse(BPoint(rect.right - 8, y), 3.5f, 3.5f); 512 } 513 514 515 // #pragma mark - MarkerManager 516 517 518 SourceView::MarkerManager::MarkerManager(SourceView* sourceView, Team* team, 519 Listener* listener) 520 : 521 fTeam(team), 522 fListener(listener), 523 fStackTrace(NULL), 524 fStackFrame(NULL), 525 fIPMarkers(10, true), 526 fBreakpointMarkers(20, true), 527 fIPMarkersValid(false), 528 fBreakpointMarkersValid(false) 529 { 530 } 531 532 533 void 534 SourceView::MarkerManager::SetSourceCode(SourceCode* sourceCode) 535 { 536 fSourceCode = sourceCode; 537 _InvalidateIPMarkers(); 538 _InvalidateBreakpointMarkers(); 539 } 540 541 542 void 543 SourceView::MarkerManager::SetStackTrace(StackTrace* stackTrace) 544 { 545 fStackTrace = stackTrace; 546 _InvalidateIPMarkers(); 547 } 548 549 550 void 551 SourceView::MarkerManager::SetStackFrame(StackFrame* stackFrame) 552 { 553 fStackFrame = stackFrame; 554 _InvalidateIPMarkers(); 555 } 556 557 558 void 559 SourceView::MarkerManager::UserBreakpointChanged(UserBreakpoint* breakpoint) 560 { 561 _InvalidateBreakpointMarkers(); 562 } 563 564 565 void 566 SourceView::MarkerManager::_InvalidateIPMarkers() 567 { 568 fIPMarkersValid = false; 569 fIPMarkers.MakeEmpty(); 570 } 571 572 573 void 574 SourceView::MarkerManager::_InvalidateBreakpointMarkers() 575 { 576 fBreakpointMarkersValid = false; 577 fBreakpointMarkers.MakeEmpty(); 578 } 579 580 581 void 582 SourceView::MarkerManager::_UpdateIPMarkers() 583 { 584 if (fIPMarkersValid) 585 return; 586 587 fIPMarkers.MakeEmpty(); 588 589 if (fSourceCode != NULL && fStackTrace != NULL) { 590 LocatableFile* sourceFile = fSourceCode->GetSourceFile(); 591 592 AutoLocker<Team> locker(fTeam); 593 594 for (int32 i = 0; StackFrame* frame = fStackTrace->FrameAt(i); 595 i++) { 596 target_addr_t ip = frame->InstructionPointer(); 597 FunctionInstance* functionInstance; 598 Statement* statement; 599 if (fTeam->GetStatementAtAddress(ip, 600 functionInstance, statement) != B_OK) { 601 continue; 602 } 603 BReference<Statement> statementReference(statement, true); 604 605 int32 line = statement->StartSourceLocation().Line(); 606 if (line < 0 || line >= fSourceCode->CountLines()) 607 continue; 608 609 if (sourceFile != NULL) { 610 if (functionInstance->GetFunction()->SourceFile() != sourceFile) 611 continue; 612 } else { 613 if (functionInstance->GetSourceCode() != fSourceCode) 614 continue; 615 } 616 617 bool isTopFrame = i == 0 618 && frame->Type() != STACK_FRAME_TYPE_SYSCALL; 619 620 Marker* marker = new(std::nothrow) InstructionPointerMarker( 621 line, isTopFrame, frame == fStackFrame); 622 if (marker == NULL || !fIPMarkers.AddItem(marker)) { 623 delete marker; 624 break; 625 } 626 } 627 628 // sort by line 629 fIPMarkers.SortItems(&_CompareMarkers); 630 631 // TODO: Filter duplicate IP markers (recursive functions)! 632 } 633 634 fIPMarkersValid = true; 635 } 636 637 638 void 639 SourceView::MarkerManager::_UpdateBreakpointMarkers() 640 { 641 if (fBreakpointMarkersValid) 642 return; 643 644 fBreakpointMarkers.MakeEmpty(); 645 646 if (fSourceCode != NULL) { 647 LocatableFile* sourceFile = fSourceCode->GetSourceFile(); 648 649 AutoLocker<Team> locker(fTeam); 650 651 // get the breakpoints in our source code range 652 BObjectList<UserBreakpoint> breakpoints; 653 fTeam->GetBreakpointsForSourceCode(fSourceCode, breakpoints); 654 655 for (int32 i = 0; UserBreakpoint* breakpoint = breakpoints.ItemAt(i); 656 i++) { 657 UserBreakpointInstance* breakpointInstance 658 = breakpoint->InstanceAt(0); 659 FunctionInstance* functionInstance; 660 Statement* statement; 661 if (fTeam->GetStatementAtAddress( 662 breakpointInstance->Address(), functionInstance, 663 statement) != B_OK) { 664 continue; 665 } 666 BReference<Statement> statementReference(statement, true); 667 668 int32 line = statement->StartSourceLocation().Line(); 669 if (line < 0 || line >= fSourceCode->CountLines()) 670 continue; 671 672 if (sourceFile != NULL) { 673 if (functionInstance->GetFunction()->SourceFile() != sourceFile) 674 continue; 675 } else { 676 if (functionInstance->GetSourceCode() != fSourceCode) 677 continue; 678 } 679 680 BreakpointMarker* marker = new(std::nothrow) BreakpointMarker( 681 line, breakpointInstance->Address(), breakpoint->IsEnabled()); 682 if (marker == NULL || !fBreakpointMarkers.AddItem(marker)) { 683 delete marker; 684 break; 685 } 686 } 687 688 // sort by line 689 fBreakpointMarkers.SortItems(&_CompareBreakpointMarkers); 690 } 691 692 fBreakpointMarkersValid = true; 693 } 694 695 696 void 697 SourceView::MarkerManager::GetMarkers(uint32 minLine, uint32 maxLine, 698 MarkerList& markers) 699 { 700 _UpdateIPMarkers(); 701 _UpdateBreakpointMarkers(); 702 703 int32 ipIndex = fIPMarkers.FindBinaryInsertionIndex( 704 MarkerByLinePredicate<Marker>(minLine)); 705 int32 breakpointIndex = fBreakpointMarkers.FindBinaryInsertionIndex( 706 MarkerByLinePredicate<BreakpointMarker>(minLine)); 707 708 Marker* ipMarker = fIPMarkers.ItemAt(ipIndex); 709 Marker* breakpointMarker = fBreakpointMarkers.ItemAt(breakpointIndex); 710 711 while (ipMarker != NULL && breakpointMarker != NULL 712 && ipMarker->Line() <= maxLine && breakpointMarker->Line() <= maxLine) { 713 if (breakpointMarker->Line() <= ipMarker->Line()) { 714 markers.AddItem(breakpointMarker); 715 breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex); 716 } else { 717 markers.AddItem(ipMarker); 718 ipMarker = fIPMarkers.ItemAt(++ipIndex); 719 } 720 } 721 722 while (breakpointMarker != NULL && breakpointMarker->Line() <= maxLine) { 723 markers.AddItem(breakpointMarker); 724 breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex); 725 } 726 727 while (ipMarker != NULL && ipMarker->Line() <= maxLine) { 728 markers.AddItem(ipMarker); 729 ipMarker = fIPMarkers.ItemAt(++ipIndex); 730 } 731 } 732 733 734 SourceView::MarkerManager::BreakpointMarker* 735 SourceView::MarkerManager::BreakpointMarkerAtLine(uint32 line) 736 { 737 return fBreakpointMarkers.BinarySearchByKey(line, 738 &_CompareLineBreakpointMarker); 739 } 740 741 742 /*static*/ int 743 SourceView::MarkerManager::_CompareMarkers(const Marker* a, 744 const Marker* b) 745 { 746 if (a->Line() < b->Line()) 747 return -1; 748 return a->Line() == b->Line() ? 0 : 1; 749 } 750 751 752 /*static*/ int 753 SourceView::MarkerManager::_CompareBreakpointMarkers(const BreakpointMarker* a, 754 const BreakpointMarker* b) 755 { 756 if (a->Line() < b->Line()) 757 return -1; 758 return a->Line() == b->Line() ? 0 : 1; 759 } 760 761 762 template<typename MarkerType> 763 /*static*/ int 764 SourceView::MarkerManager::_CompareLineMarkerTemplate(const uint32* line, 765 const MarkerType* marker) 766 { 767 if (*line < marker->Line()) 768 return -1; 769 return *line == marker->Line() ? 0 : 1; 770 } 771 772 773 /*static*/ int 774 SourceView::MarkerManager::_CompareLineMarker(const uint32* line, 775 const Marker* marker) 776 { 777 return _CompareLineMarkerTemplate<Marker>(line, marker); 778 } 779 780 781 /*static*/ int 782 SourceView::MarkerManager::_CompareLineBreakpointMarker(const uint32* line, 783 const BreakpointMarker* marker) 784 { 785 return _CompareLineMarkerTemplate<BreakpointMarker>(line, marker); 786 } 787 788 789 // #pragma mark - MarkerView 790 791 792 SourceView::MarkerView::MarkerView(SourceView* sourceView, Team* team, 793 Listener* listener, MarkerManager* manager, FontInfo* fontInfo) 794 : 795 BaseView("source marker view", sourceView, fontInfo), 796 fTeam(team), 797 fListener(listener), 798 fMarkerManager(manager), 799 fStackTrace(NULL), 800 fStackFrame(NULL) 801 { 802 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 803 fBreakpointOptionMarker = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 804 B_DARKEN_1_TINT); 805 SetViewColor(tint_color(background, B_LIGHTEN_2_TINT)); 806 } 807 808 809 SourceView::MarkerView::~MarkerView() 810 { 811 } 812 813 814 void 815 SourceView::MarkerView::SetSourceCode(SourceCode* sourceCode) 816 { 817 BaseView::SetSourceCode(sourceCode); 818 } 819 820 821 void 822 SourceView::MarkerView::SetStackTrace(StackTrace* stackTrace) 823 { 824 Invalidate(); 825 } 826 827 828 void 829 SourceView::MarkerView::SetStackFrame(StackFrame* stackFrame) 830 { 831 Invalidate(); 832 } 833 834 835 void 836 SourceView::MarkerView::UserBreakpointChanged(UserBreakpoint* breakpoint) 837 { 838 Invalidate(); 839 } 840 841 842 BSize 843 SourceView::MarkerView::MinSize() 844 { 845 return BSize(40, TotalHeight()); 846 } 847 848 849 BSize 850 SourceView::MarkerView::MaxSize() 851 { 852 return BSize(MinSize().width, B_SIZE_UNLIMITED); 853 } 854 855 void 856 SourceView::MarkerView::Draw(BRect updateRect) 857 { 858 if (fSourceCode == NULL) 859 return; 860 861 // get the lines intersecting with the update rect 862 int32 minLine, maxLine; 863 GetLineRange(updateRect, minLine, maxLine); 864 if (minLine > maxLine) 865 return; 866 867 // get the markers in that range 868 SourceView::MarkerManager::MarkerList markers; 869 fMarkerManager->GetMarkers(minLine, maxLine, markers); 870 871 float width = Bounds().Width(); 872 873 AutoLocker<SourceCode> sourceLocker(fSourceCode); 874 875 int32 markerIndex = 0; 876 for (int32 line = minLine; line <= maxLine; line++) { 877 bool drawBreakpointOptionMarker = true; 878 879 SourceView::MarkerManager::Marker* marker; 880 while ((marker = markers.ItemAt(markerIndex)) != NULL 881 && marker->Line() == (uint32)line) { 882 marker->Draw(this, LineRect(line)); 883 drawBreakpointOptionMarker = false; 884 markerIndex++; 885 } 886 887 if (!drawBreakpointOptionMarker) 888 continue; 889 890 SourceLocation statementStart, statementEnd; 891 if (!fSourceCode->GetStatementLocationRange(SourceLocation(line), 892 statementStart, statementEnd) 893 || statementStart.Line() != line) { 894 continue; 895 } 896 897 float y = ((float)line + 0.5f) * fFontInfo->lineHeight; 898 SetHighColor(fBreakpointOptionMarker); 899 FillEllipse(BPoint(width - 8, y), 2, 2); 900 } 901 } 902 903 904 void 905 SourceView::MarkerView::MouseDown(BPoint where) 906 { 907 if (fSourceCode == NULL) 908 return; 909 910 int32 line = LineAtOffset(where.y); 911 if (line < 0) 912 return; 913 914 AutoLocker<Team> locker(fTeam); 915 Statement* statement; 916 if (fTeam->GetStatementAtSourceLocation(fSourceCode, 917 SourceLocation(line), statement) != B_OK) { 918 return; 919 } 920 BReference<Statement> statementReference(statement, true); 921 if (statement->StartSourceLocation().Line() != line) 922 return; 923 924 int32 modifiers; 925 if (Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers) != B_OK) 926 modifiers = 0; 927 928 SourceView::MarkerManager::BreakpointMarker* marker = 929 fMarkerManager->BreakpointMarkerAtLine(line); 930 target_addr_t address = marker != NULL 931 ? marker->Address() : statement->CoveringAddressRange().Start(); 932 933 if ((modifiers & B_SHIFT_KEY) != 0) { 934 if (marker != NULL && !marker->IsEnabled()) 935 fListener->ClearBreakpointRequested(address); 936 else 937 fListener->SetBreakpointRequested(address, false); 938 } else { 939 if (marker != NULL && marker->IsEnabled()) 940 fListener->ClearBreakpointRequested(address); 941 else 942 fListener->SetBreakpointRequested(address, true); 943 } 944 } 945 946 947 // #pragma mark - TextView 948 949 950 SourceView::TextView::TextView(SourceView* sourceView, MarkerManager* manager, 951 FontInfo* fontInfo) 952 : 953 BaseView("source text view", sourceView, fontInfo), 954 fMaxLineWidth(-1), 955 fCharacterWidth(fontInfo->font.StringWidth("Q")), 956 fSelectionStart(-1, -1), 957 fSelectionEnd(-1, -1), 958 fSelectionBase(-1, -1), 959 fLastClickPoint(-1, -1), 960 fLastClickTime(0), 961 fClickCount(0), 962 fSelectionMode(false), 963 fTrackState(kNotTracking), 964 fScrollRunner(NULL), 965 fMarkerManager(manager) 966 { 967 SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); 968 fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR); 969 SetFlags(Flags() | B_NAVIGABLE); 970 } 971 972 973 void 974 SourceView::TextView::SetSourceCode(SourceCode* sourceCode) 975 { 976 fMaxLineWidth = -1; 977 fSelectionStart = fSelectionBase = fSelectionEnd = SelectionPoint(-1, -1); 978 fClickCount = 0; 979 BaseView::SetSourceCode(sourceCode); 980 } 981 982 983 void 984 SourceView::TextView::UserBreakpointChanged(UserBreakpoint* breakpoint) 985 { 986 Invalidate(); 987 } 988 989 990 BSize 991 SourceView::TextView::MinSize() 992 { 993 return BSize(kLeftTextMargin + _MaxLineWidth() - 1, TotalHeight()); 994 } 995 996 997 BSize 998 SourceView::TextView::MaxSize() 999 { 1000 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 1001 } 1002 1003 1004 void 1005 SourceView::TextView::Draw(BRect updateRect) 1006 { 1007 if (fSourceCode == NULL) 1008 return; 1009 1010 // get the lines intersecting with the update rect 1011 int32 minLine, maxLine; 1012 GetLineRange(updateRect, minLine, maxLine); 1013 SourceView::MarkerManager::MarkerList markers; 1014 fMarkerManager->GetMarkers(minLine, maxLine, markers); 1015 1016 // draw the affected lines 1017 SetHighColor(fTextColor); 1018 SetFont(&fFontInfo->font); 1019 SourceView::MarkerManager::Marker* marker; 1020 int32 markerIndex = 0; 1021 for (int32 i = minLine; i <= maxLine; i++) { 1022 SetLowColor(ViewColor()); 1023 float y = i * fFontInfo->lineHeight; 1024 BString lineString; 1025 _FormatLine(fSourceCode->LineAt(i), lineString); 1026 1027 for (int32 j = markerIndex; j < markers.CountItems(); j++) { 1028 marker = markers.ItemAt(j); 1029 if (marker->Line() < (uint32)i) { 1030 ++markerIndex; 1031 continue; 1032 } else if (marker->Line() == (uint32)i) { 1033 ++markerIndex; 1034 if (dynamic_cast<SourceView::MarkerManager 1035 ::InstructionPointerMarker*>(marker) != NULL) 1036 SetLowColor(96, 216, 216, 255); 1037 else 1038 SetLowColor(255, 255, 0, 255); 1039 FillRect(BRect(kLeftTextMargin, y, Bounds().right, 1040 y + fFontInfo->lineHeight), B_SOLID_LOW); 1041 break; 1042 } else 1043 break; 1044 } 1045 1046 DrawString(lineString, 1047 BPoint(kLeftTextMargin, y + fFontInfo->fontHeight.ascent)); 1048 } 1049 1050 if (fSelectionStart.line != -1 && fSelectionEnd.line != -1) { 1051 PushState(); 1052 BRegion selectionRegion; 1053 _GetSelectionRegion(selectionRegion); 1054 SetDrawingMode(B_OP_INVERT); 1055 FillRegion(&selectionRegion, B_SOLID_HIGH); 1056 PopState(); 1057 } 1058 } 1059 1060 1061 void 1062 SourceView::TextView::KeyDown(const char* bytes, int32 numBytes) 1063 { 1064 switch(bytes[0]) { 1065 case B_UP_ARROW: 1066 _ScrollByLines(-1); 1067 break; 1068 1069 case B_DOWN_ARROW: 1070 _ScrollByLines(1); 1071 break; 1072 1073 case B_PAGE_UP: 1074 _ScrollByPages(-1); 1075 break; 1076 1077 case B_PAGE_DOWN: 1078 _ScrollByPages(1); 1079 break; 1080 1081 case B_HOME: 1082 _ScrollToTop(); 1083 break; 1084 1085 case B_END: 1086 _ScrollToBottom(); 1087 break; 1088 } 1089 1090 SourceView::BaseView::KeyDown(bytes, numBytes); 1091 } 1092 1093 1094 void 1095 SourceView::TextView::MakeFocus(bool isFocused) 1096 { 1097 fSourceView->HighlightBorder(isFocused); 1098 1099 SourceView::BaseView::MakeFocus(isFocused); 1100 } 1101 1102 1103 void 1104 SourceView::TextView::MessageReceived(BMessage* message) 1105 { 1106 switch (message->what) 1107 { 1108 case B_COPY: 1109 _CopySelectionToClipboard(); 1110 break; 1111 1112 case B_SELECT_ALL: 1113 fSelectionStart.line = 0; 1114 fSelectionStart.offset = 0; 1115 fSelectionEnd.line = fSourceCode->CountLines() - 1; 1116 fSelectionEnd.offset = fSourceCode->LineLengthAt( 1117 fSelectionEnd.line); 1118 Invalidate(); 1119 break; 1120 1121 case MSG_TEXTVIEW_AUTOSCROLL: 1122 _HandleAutoScroll(); 1123 break; 1124 1125 default: 1126 SourceView::BaseView::MessageReceived(message); 1127 break; 1128 } 1129 } 1130 1131 1132 void 1133 SourceView::TextView::MouseDown(BPoint where) 1134 { 1135 if (fSourceCode != NULL) { 1136 if (!IsFocus()) 1137 MakeFocus(true); 1138 fTrackState = kTracking; 1139 1140 // don't reset the selection if the user clicks within the 1141 // current selection range 1142 BRegion region; 1143 _GetSelectionRegion(region); 1144 bigtime_t clickTime = system_time(); 1145 SelectionPoint point = _SelectionPointAt(where); 1146 fLastClickPoint = point; 1147 bigtime_t clickSpeed = 0; 1148 get_click_speed(&clickSpeed); 1149 if (clickTime - fLastClickTime < clickSpeed 1150 && fSelectionBase == point) { 1151 if (fClickCount > 3) { 1152 fClickCount = 0; 1153 fLastClickTime = 0; 1154 } else { 1155 fClickCount++; 1156 fLastClickTime = clickTime; 1157 } 1158 } else { 1159 fClickCount = 1; 1160 fLastClickTime = clickTime; 1161 } 1162 1163 if (fClickCount == 2) { 1164 _SelectWordAt(point); 1165 fSelectionMode = true; 1166 } else if (fClickCount == 3) { 1167 _SelectLineAt(point); 1168 fSelectionMode = true; 1169 } else if (!region.Contains(where)) { 1170 fSelectionBase = fSelectionStart = fSelectionEnd = point; 1171 fSelectionMode = true; 1172 Invalidate(); 1173 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 1174 } 1175 } 1176 } 1177 1178 1179 void 1180 SourceView::TextView::MouseMoved(BPoint where, uint32 transit, 1181 const BMessage* dragMessage) 1182 { 1183 BRegion region; 1184 if (fSelectionMode) { 1185 BRegion oldRegion; 1186 _GetSelectionRegion(oldRegion); 1187 SelectionPoint point = _SelectionPointAt(where); 1188 if (point.line < 0) 1189 return; 1190 1191 switch (transit) { 1192 case B_INSIDE_VIEW: 1193 case B_OUTSIDE_VIEW: 1194 if (fClickCount == 2) 1195 _SelectWordAt(point, true); 1196 else if (fClickCount == 3) 1197 _SelectLineAt(point, true); 1198 else { 1199 if (point.line > fSelectionBase.line) { 1200 fSelectionStart = fSelectionBase; 1201 fSelectionEnd = point; 1202 } else if (point.line < fSelectionBase.line) { 1203 fSelectionEnd = fSelectionBase; 1204 fSelectionStart = point; 1205 } else if (point.offset > fSelectionBase.offset) { 1206 fSelectionStart = fSelectionBase; 1207 fSelectionEnd = point; 1208 } else { 1209 fSelectionEnd = fSelectionBase; 1210 fSelectionStart = point; 1211 } 1212 } 1213 break; 1214 1215 case B_EXITED_VIEW: 1216 fScrollRunner = new BMessageRunner(BMessenger(this), 1217 new BMessage(MSG_TEXTVIEW_AUTOSCROLL), kScrollTimer); 1218 break; 1219 1220 case B_ENTERED_VIEW: 1221 delete fScrollRunner; 1222 fScrollRunner = NULL; 1223 break; 1224 } 1225 _GetSelectionRegion(region); 1226 region.Include(&oldRegion); 1227 Invalidate(®ion); 1228 } else if (fTrackState == kTracking) { 1229 _GetSelectionRegion(region); 1230 if (region.CountRects() > 0) { 1231 BString text; 1232 _GetSelectionText(text); 1233 BMessage message; 1234 message.AddData ("text/plain", B_MIME_TYPE, text.String(), 1235 text.Length()); 1236 BString clipName; 1237 if (fSourceCode->GetSourceFile() != NULL) 1238 clipName = fSourceCode->GetSourceFile()->Name(); 1239 else if (fSourceCode->GetSourceLanguage() != NULL) 1240 clipName = fSourceCode->GetSourceLanguage()->Name(); 1241 else 1242 clipName = "Text"; 1243 clipName << " clipping"; 1244 message.AddString ("be:clip_name", clipName.String()); 1245 message.AddInt32 ("be:actions", B_COPY_TARGET); 1246 BRect dragRect = region.Frame(); 1247 BRect visibleRect = fSourceView->Bounds(); 1248 if (dragRect.Height() > visibleRect.Height()) { 1249 dragRect.top = 0; 1250 dragRect.bottom = visibleRect.Height(); 1251 } 1252 if (dragRect.Width() > visibleRect.Width()) { 1253 dragRect.left = 0; 1254 dragRect.right = visibleRect.Width(); 1255 } 1256 DragMessage(&message, dragRect); 1257 fTrackState = kDragging; 1258 } 1259 } 1260 } 1261 1262 1263 void 1264 SourceView::TextView::MouseUp(BPoint where) 1265 { 1266 fSelectionMode = false; 1267 if (fTrackState == kTracking && fClickCount < 2) { 1268 1269 // if we clicked without dragging or double/triple clicking, 1270 // clear the current selection (if any) 1271 SelectionPoint point = _SelectionPointAt(where); 1272 if (fLastClickPoint == point) { 1273 fSelectionBase = fSelectionStart = fSelectionEnd; 1274 Invalidate(); 1275 } 1276 } 1277 delete fScrollRunner; 1278 fScrollRunner = NULL; 1279 fTrackState = kNotTracking; 1280 } 1281 1282 1283 float 1284 SourceView::TextView::_MaxLineWidth() 1285 { 1286 if (fMaxLineWidth >= 0) 1287 return fMaxLineWidth; 1288 1289 fMaxLineWidth = 0; 1290 if (fSourceCode != NULL) { 1291 for (int32 i = 0; const char* line = fSourceCode->LineAt(i); i++) { 1292 fMaxLineWidth = std::max(fMaxLineWidth, 1293 fFontInfo->font.StringWidth(line)); 1294 } 1295 } 1296 1297 return fMaxLineWidth; 1298 } 1299 1300 1301 void 1302 SourceView::TextView::_FormatLine(const char* line, BString& formattedLine) 1303 { 1304 int32 column = 0; 1305 for (; *line != '\0'; line++) { 1306 // TODO: That's probably not very efficient! 1307 if (*line == '\t') { 1308 int32 nextTabStop = _NextTabStop(column); 1309 for (; column < nextTabStop; column++) 1310 formattedLine << ' '; 1311 } else { 1312 formattedLine << *line; 1313 column++; 1314 } 1315 } 1316 } 1317 1318 1319 int32 1320 SourceView::TextView::_NextTabStop(int32 column) const 1321 { 1322 return (column / kSpacesPerTab + 1) * kSpacesPerTab; 1323 } 1324 1325 1326 float 1327 SourceView::TextView::_FormattedPosition(int32 line, int32 offset) const 1328 { 1329 int32 column = 0; 1330 for (int32 i = 0; i < offset; i++) { 1331 if (fSourceCode->LineAt(line)[i] == '\t') 1332 column = _NextTabStop(column); 1333 else 1334 ++column; 1335 } 1336 1337 return column * fCharacterWidth; 1338 } 1339 1340 1341 SourceView::TextView::SelectionPoint 1342 SourceView::TextView::_SelectionPointAt(BPoint where) const 1343 { 1344 int32 line = LineAtOffset(where.y); 1345 int32 offset = -1; 1346 if (line >= 0) { 1347 int32 column = 0; 1348 int32 lineLength = fSourceCode->LineLengthAt(line); 1349 const char* sourceLine = fSourceCode->LineAt(line); 1350 1351 for (int32 i = 0; i < lineLength; i++) { 1352 if (sourceLine[i] == '\t') 1353 column = _NextTabStop(column); 1354 else 1355 ++column; 1356 1357 if (column * fCharacterWidth > where.x) { 1358 offset = i; 1359 break; 1360 } 1361 } 1362 1363 if (offset < 0) 1364 offset = lineLength; 1365 } 1366 1367 return SelectionPoint(line, offset); 1368 } 1369 1370 1371 void 1372 SourceView::TextView::_GetSelectionRegion(BRegion ®ion) const 1373 { 1374 if (fSelectionStart.line == -1 && fSelectionEnd.line == -1) 1375 return; 1376 1377 BRect selectionRect; 1378 1379 if (fSelectionStart.line == fSelectionEnd.line) { 1380 if (fSelectionStart.offset != fSelectionEnd.offset) { 1381 selectionRect.left = _FormattedPosition(fSelectionStart.line, 1382 fSelectionStart.offset); 1383 selectionRect.top = fSelectionStart.line * fFontInfo->lineHeight; 1384 selectionRect.right = _FormattedPosition(fSelectionEnd.line, 1385 fSelectionEnd.offset); 1386 selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight; 1387 region.Include(selectionRect); 1388 } 1389 } else { 1390 // add rect for starting line 1391 selectionRect.left = _FormattedPosition(fSelectionStart.line, 1392 fSelectionStart.offset); 1393 selectionRect.top = fSelectionStart.line * fFontInfo->lineHeight; 1394 selectionRect.right = Bounds().right; 1395 selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight; 1396 region.Include(selectionRect); 1397 1398 // compute rect for all lines in middle of selection 1399 if (fSelectionEnd.line - fSelectionStart.line > 1) { 1400 selectionRect.left = 0.0; 1401 selectionRect.top = (fSelectionStart.line + 1) 1402 * fFontInfo->lineHeight; 1403 selectionRect.right = Bounds().right; 1404 selectionRect.bottom = fSelectionEnd.line * fFontInfo->lineHeight; 1405 region.Include(selectionRect); 1406 } 1407 1408 // add rect for last line (if needed) 1409 if (fSelectionEnd.offset > 0) { 1410 selectionRect.left = 0.0; 1411 selectionRect.top = fSelectionEnd.line * fFontInfo->lineHeight; 1412 selectionRect.right = _FormattedPosition(fSelectionEnd.line, 1413 fSelectionEnd.offset); 1414 selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight; 1415 region.Include(selectionRect); 1416 } 1417 } 1418 region.OffsetBy(kLeftTextMargin, 0.0); 1419 } 1420 1421 1422 void 1423 SourceView::TextView::_GetSelectionText(BString& text) const 1424 { 1425 if (fSelectionStart.line == -1 || fSelectionEnd.line == -1) 1426 return; 1427 1428 if (fSelectionStart.line == fSelectionEnd.line) { 1429 text.SetTo(fSourceCode->LineAt(fSelectionStart.line) 1430 + fSelectionStart.offset, fSelectionEnd.offset 1431 - fSelectionStart.offset); 1432 } else { 1433 text.SetTo(fSourceCode->LineAt(fSelectionStart.line) 1434 + fSelectionStart.offset); 1435 text << "\n"; 1436 for (int32 i = fSelectionStart.line + 1; i < fSelectionEnd.line; i++) 1437 text << fSourceCode->LineAt(i) << "\n"; 1438 text.Append(fSourceCode->LineAt(fSelectionEnd.line), 1439 fSelectionEnd.offset); 1440 } 1441 } 1442 1443 1444 void 1445 SourceView::TextView::_CopySelectionToClipboard(void) const 1446 { 1447 BString text; 1448 _GetSelectionText(text); 1449 1450 if (text.Length() > 0) { 1451 be_clipboard->Lock(); 1452 be_clipboard->Data()->RemoveData("text/plain"); 1453 be_clipboard->Data()->AddData ("text/plain", 1454 B_MIME_TYPE, text.String(), text.Length()); 1455 be_clipboard->Commit(); 1456 be_clipboard->Unlock(); 1457 } 1458 } 1459 1460 1461 void 1462 SourceView::TextView::_SelectWordAt(const SelectionPoint& point, bool extend) 1463 { 1464 const char* line = fSourceCode->LineAt(point.line); 1465 int32 length = fSourceCode->LineLengthAt(point.line); 1466 int32 start = point.offset - 1; 1467 int32 end = point.offset + 1; 1468 while ((end) < length) { 1469 if (!isalpha(line[end]) && !isdigit(line[end])) 1470 break; 1471 ++end; 1472 } 1473 while ((start - 1) >= 0) { 1474 if (!isalpha(line[start - 1]) && !isdigit(line[start - 1])) 1475 break; 1476 --start; 1477 } 1478 1479 if (extend) { 1480 if (point.line >= fSelectionBase.line 1481 || (point.line == fSelectionBase.line 1482 && point.offset > fSelectionBase.offset)) { 1483 fSelectionStart.line = fSelectionBase.line; 1484 fSelectionStart.offset = fSelectionBase.offset; 1485 fSelectionEnd.line = point.line; 1486 fSelectionEnd.offset = end; 1487 } else if (point.line < fSelectionBase.line) { 1488 fSelectionStart.line = point.line; 1489 fSelectionStart.offset = start; 1490 fSelectionEnd.line = fSelectionBase.line; 1491 fSelectionEnd.offset = fSelectionBase.offset; 1492 } else if (point.line == fSelectionBase.line) { 1493 // if we hit here, our offset is before the actual start. 1494 fSelectionStart.line = point.line; 1495 fSelectionStart.offset = start; 1496 fSelectionEnd.line = point.line; 1497 fSelectionEnd.offset = fSelectionBase.offset; 1498 } 1499 } else { 1500 fSelectionBase.line = fSelectionStart.line = point.line; 1501 fSelectionBase.offset = fSelectionStart.offset = start; 1502 fSelectionEnd.line = point.line; 1503 fSelectionEnd.offset = end; 1504 } 1505 BRegion region; 1506 _GetSelectionRegion(region); 1507 Invalidate(®ion); 1508 } 1509 1510 1511 void 1512 SourceView::TextView::_SelectLineAt(const SelectionPoint& point, bool extend) 1513 { 1514 if (extend) { 1515 if (point.line >= fSelectionBase.line) { 1516 fSelectionStart.line = fSelectionBase.line; 1517 fSelectionStart.offset = 0; 1518 fSelectionEnd.line = point.line; 1519 fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line); 1520 } else { 1521 fSelectionStart.line = point.line; 1522 fSelectionStart.offset = 0; 1523 fSelectionEnd.line = fSelectionBase.line; 1524 fSelectionEnd.offset = fSourceCode->LineLengthAt( 1525 fSelectionBase.line); 1526 } 1527 } else { 1528 fSelectionStart.line = fSelectionEnd.line = point.line; 1529 fSelectionStart.offset = 0; 1530 fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line); 1531 } 1532 BRegion region; 1533 _GetSelectionRegion(region); 1534 Invalidate(®ion); 1535 } 1536 1537 1538 void 1539 SourceView::TextView::_HandleAutoScroll(void) 1540 { 1541 BPoint point; 1542 uint32 buttons; 1543 GetMouse(&point, &buttons); 1544 float difference = 0.0; 1545 int factor = 0; 1546 BRect visibleRect = Frame() & fSourceView->Bounds(); 1547 if (point.y < visibleRect.top) 1548 difference = point.y - visibleRect.top; 1549 else if (point.y > visibleRect.bottom) 1550 difference = point.y - visibleRect.bottom; 1551 if (difference != 0.0) { 1552 factor = (int)(ceilf(difference / fFontInfo->lineHeight)); 1553 _ScrollByLines(factor); 1554 } 1555 difference = 0.0; 1556 if (point.x < visibleRect.left) 1557 difference = point.x - visibleRect.left; 1558 else if (point.x > visibleRect.right) 1559 difference = point.x - visibleRect.right; 1560 if (difference != 0.0) { 1561 factor = (int)(ceilf(difference / fCharacterWidth)); 1562 _ScrollHorizontal(factor); 1563 } 1564 1565 MouseMoved(point, B_OUTSIDE_VIEW, NULL); 1566 } 1567 1568 1569 void 1570 SourceView::TextView::_ScrollHorizontal(int32 charCount) 1571 { 1572 BScrollBar* horizontal = fSourceView->ScrollBar(B_HORIZONTAL); 1573 if (horizontal == NULL) 1574 return; 1575 1576 float value = horizontal->Value(); 1577 horizontal->SetValue(value + fCharacterWidth * charCount); 1578 } 1579 1580 1581 void 1582 SourceView::TextView::_ScrollByLines(int32 lineCount) 1583 { 1584 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL); 1585 if (vertical == NULL) 1586 return; 1587 1588 float value = vertical->Value(); 1589 vertical->SetValue(value + fFontInfo->lineHeight * lineCount); 1590 } 1591 1592 1593 void 1594 SourceView::TextView::_ScrollByPages(int32 pageCount) 1595 { 1596 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL); 1597 if (vertical == NULL) 1598 return; 1599 1600 float value = vertical->Value(); 1601 vertical->SetValue(value 1602 + fSourceView->Frame().Size().height * pageCount); 1603 } 1604 1605 1606 void 1607 SourceView::TextView::_ScrollToTop(void) 1608 { 1609 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL); 1610 if (vertical == NULL) 1611 return; 1612 1613 vertical->SetValue(0.0); 1614 } 1615 1616 1617 void 1618 SourceView::TextView::_ScrollToBottom(void) 1619 { 1620 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL); 1621 if (vertical == NULL) 1622 return; 1623 1624 float min, max; 1625 vertical->GetRange(&min, &max); 1626 vertical->SetValue(max); 1627 } 1628 1629 1630 // #pragma mark - SourceView 1631 1632 1633 SourceView::SourceView(Team* team, Listener* listener) 1634 : 1635 BView("source view", 0), 1636 fTeam(team), 1637 fStackTrace(NULL), 1638 fStackFrame(NULL), 1639 fSourceCode(NULL), 1640 fMarkerView(NULL), 1641 fTextView(NULL), 1642 fListener(listener) 1643 { 1644 // init font info 1645 fFontInfo.font = *be_fixed_font; 1646 fFontInfo.font.GetHeight(&fFontInfo.fontHeight); 1647 fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent) 1648 + ceilf(fFontInfo.fontHeight.descent); 1649 } 1650 1651 1652 SourceView::~SourceView() 1653 { 1654 SetStackFrame(NULL); 1655 SetStackTrace(NULL); 1656 SetSourceCode(NULL); 1657 } 1658 1659 1660 /*static*/ SourceView* 1661 SourceView::Create(Team* team, Listener* listener) 1662 { 1663 SourceView* self = new SourceView(team, listener); 1664 1665 try { 1666 self->_Init(); 1667 } catch (...) { 1668 delete self; 1669 throw; 1670 } 1671 1672 return self; 1673 } 1674 1675 1676 void 1677 SourceView::UnsetListener() 1678 { 1679 fListener = NULL; 1680 } 1681 1682 1683 void 1684 SourceView::SetStackTrace(StackTrace* stackTrace) 1685 { 1686 TRACE_GUI("SourceView::SetStackTrace(%p)\n", stackTrace); 1687 1688 if (stackTrace == fStackTrace) 1689 return; 1690 1691 if (fStackTrace != NULL) { 1692 fMarkerManager->SetStackTrace(NULL); 1693 fMarkerView->SetStackTrace(NULL); 1694 fStackTrace->ReleaseReference(); 1695 } 1696 1697 fStackTrace = stackTrace; 1698 1699 if (fStackTrace != NULL) 1700 fStackTrace->AcquireReference(); 1701 1702 fMarkerManager->SetStackTrace(fStackTrace); 1703 fMarkerView->SetStackTrace(fStackTrace); 1704 fTextView->Invalidate(); 1705 } 1706 1707 1708 void 1709 SourceView::SetStackFrame(StackFrame* stackFrame) 1710 { 1711 if (stackFrame == fStackFrame) 1712 return; 1713 1714 if (fStackFrame != NULL) { 1715 fMarkerManager->SetStackFrame(NULL); 1716 fMarkerView->SetStackFrame(NULL); 1717 fStackFrame->ReleaseReference(); 1718 } 1719 1720 fStackFrame = stackFrame; 1721 1722 if (fStackFrame != NULL) 1723 fStackFrame->AcquireReference(); 1724 1725 fMarkerManager->SetStackFrame(fStackFrame); 1726 fMarkerView->SetStackFrame(fStackFrame); 1727 fTextView->Invalidate(); 1728 1729 if (fStackFrame != NULL) 1730 ScrollToAddress(fStackFrame->InstructionPointer()); 1731 } 1732 1733 1734 void 1735 SourceView::SetSourceCode(SourceCode* sourceCode) 1736 { 1737 // set the source code, if it changed 1738 if (sourceCode == fSourceCode) 1739 return; 1740 1741 if (fSourceCode != NULL) { 1742 fMarkerManager->SetSourceCode(NULL); 1743 fTextView->SetSourceCode(NULL); 1744 fMarkerView->SetSourceCode(NULL); 1745 fSourceCode->ReleaseReference(); 1746 } 1747 1748 fSourceCode = sourceCode; 1749 1750 if (fSourceCode != NULL) 1751 fSourceCode->AcquireReference(); 1752 1753 fMarkerManager->SetSourceCode(fSourceCode); 1754 fTextView->SetSourceCode(fSourceCode); 1755 fMarkerView->SetSourceCode(fSourceCode); 1756 _UpdateScrollBars(); 1757 1758 if (fStackFrame != NULL) 1759 ScrollToAddress(fStackFrame->InstructionPointer()); 1760 } 1761 1762 1763 void 1764 SourceView::UserBreakpointChanged(UserBreakpoint* breakpoint) 1765 { 1766 fMarkerManager->UserBreakpointChanged(breakpoint); 1767 fMarkerView->UserBreakpointChanged(breakpoint); 1768 fTextView->UserBreakpointChanged(breakpoint); 1769 } 1770 1771 1772 bool 1773 SourceView::ScrollToAddress(target_addr_t address) 1774 { 1775 TRACE_GUI("SourceView::ScrollToAddress(%#" B_PRIx64 ")\n", address); 1776 1777 if (fSourceCode == NULL) 1778 return false; 1779 1780 AutoLocker<Team> locker(fTeam); 1781 1782 FunctionInstance* functionInstance; 1783 Statement* statement; 1784 if (fTeam->GetStatementAtAddress(address, functionInstance, 1785 statement) != B_OK) { 1786 return false; 1787 } 1788 BReference<Statement> statementReference(statement, true); 1789 1790 return ScrollToLine(statement->StartSourceLocation().Line()); 1791 } 1792 1793 1794 bool 1795 SourceView::ScrollToLine(uint32 line) 1796 { 1797 TRACE_GUI("SourceView::ScrollToLine(%" B_PRIu32 ")\n", line); 1798 1799 if (fSourceCode == NULL || line >= (uint32)fSourceCode->CountLines()) 1800 return false; 1801 1802 float top = (float)line * fFontInfo.lineHeight; 1803 float bottom = top + fFontInfo.lineHeight - 1; 1804 1805 BRect visible = Bounds(); 1806 1807 TRACE_GUI("SourceView::ScrollToLine(%" B_PRId32 ")\n", line); 1808 TRACE_GUI(" visible: (%f, %f) - (%f, %f), line: %f - %f\n", visible.left, 1809 visible.top, visible.right, visible.bottom, top, bottom); 1810 1811 // If not visible at all, scroll to the center, otherwise scroll so that at 1812 // least one more line is visible. 1813 if (top >= visible.bottom || bottom <= visible.top) { 1814 TRACE_GUI(" -> scrolling to (%f, %f)\n", visible.left, 1815 top - (visible.Height() + 1) / 2); 1816 ScrollTo(visible.left, top - (visible.Height() + 1) / 2); 1817 } else if (top - fFontInfo.lineHeight < visible.top) 1818 ScrollBy(0, top - fFontInfo.lineHeight - visible.top); 1819 else if (bottom + fFontInfo.lineHeight > visible.bottom) 1820 ScrollBy(0, bottom + fFontInfo.lineHeight - visible.bottom); 1821 1822 return true; 1823 } 1824 1825 1826 void 1827 SourceView::HighlightBorder(bool state) 1828 { 1829 BScrollView* parent = dynamic_cast<BScrollView*>(Parent()); 1830 if (parent != NULL) 1831 parent->SetBorderHighlighted(state); 1832 } 1833 1834 1835 void 1836 SourceView::TargetedByScrollView(BScrollView* scrollView) 1837 { 1838 _UpdateScrollBars(); 1839 } 1840 1841 1842 BSize 1843 SourceView::MinSize() 1844 { 1845 // BSize markerSize(fMarkerView->MinSize()); 1846 // BSize textSize(fTextView->MinSize()); 1847 // return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width), 1848 // std::max(markerSize.height, textSize.height)); 1849 return BSize(10, 10); 1850 } 1851 1852 1853 BSize 1854 SourceView::MaxSize() 1855 { 1856 // BSize markerSize(fMarkerView->MaxSize()); 1857 // BSize textSize(fTextView->MaxSize()); 1858 // return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width), 1859 // std::min(markerSize.height, textSize.height)); 1860 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 1861 } 1862 1863 1864 BSize 1865 SourceView::PreferredSize() 1866 { 1867 BSize markerSize(fMarkerView->PreferredSize()); 1868 BSize textSize(fTextView->PreferredSize()); 1869 return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width), 1870 std::max(markerSize.height, textSize.height)); 1871 // return MinSize(); 1872 } 1873 1874 1875 void 1876 SourceView::DoLayout() 1877 { 1878 BSize size = _DataRectSize(); 1879 float markerWidth = fMarkerView->MinSize().width; 1880 1881 fMarkerView->MoveTo(0, 0); 1882 fMarkerView->ResizeTo(markerWidth, size.height); 1883 1884 fTextView->MoveTo(markerWidth + 1, 0); 1885 fTextView->ResizeTo(size.width - markerWidth - 1, size.height); 1886 1887 _UpdateScrollBars(); 1888 } 1889 1890 1891 void 1892 SourceView::_Init() 1893 { 1894 fMarkerManager = new MarkerManager(this, fTeam, fListener); 1895 AddChild(fMarkerView = new MarkerView(this, fTeam, fListener, 1896 fMarkerManager, &fFontInfo)); 1897 AddChild(fTextView = new TextView(this, fMarkerManager, &fFontInfo)); 1898 } 1899 1900 1901 void 1902 SourceView::_UpdateScrollBars() 1903 { 1904 BSize dataRectSize = _DataRectSize(); 1905 BSize size = Frame().Size(); 1906 1907 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) { 1908 float range = dataRectSize.width - size.width; 1909 if (range > 0) { 1910 scrollBar->SetRange(0, range); 1911 scrollBar->SetProportion( 1912 (size.width + 1) / (dataRectSize.width + 1)); 1913 scrollBar->SetSteps(fFontInfo.lineHeight, size.width + 1); 1914 } else { 1915 scrollBar->SetRange(0, 0); 1916 scrollBar->SetProportion(1); 1917 } 1918 } 1919 1920 if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { 1921 float range = dataRectSize.height - size.height; 1922 if (range > 0) { 1923 scrollBar->SetRange(0, range); 1924 scrollBar->SetProportion( 1925 (size.height + 1) / (dataRectSize.height + 1)); 1926 scrollBar->SetSteps(fFontInfo.lineHeight, size.height + 1); 1927 } else { 1928 scrollBar->SetRange(0, 0); 1929 scrollBar->SetProportion(1); 1930 } 1931 } 1932 } 1933 1934 1935 BSize 1936 SourceView::_DataRectSize() const 1937 { 1938 float width = fMarkerView->MinSize().width + fTextView->MinSize().width + 1; 1939 float height = std::max(fMarkerView->MinSize().height, 1940 fTextView->MinSize().height); 1941 1942 BSize size = Frame().Size(); 1943 return BSize(std::max(size.width, width), std::max(size.height, height)); 1944 } 1945 1946 1947 // #pragma mark - Listener 1948 1949 1950 SourceView::Listener::~Listener() 1951 { 1952 } 1953