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