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