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