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