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