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