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.
8 #include "SourceView.h"
16 #include <Clipboard.h>
18 #include <LayoutUtils.h>
22 #include <MessageRunner.h>
25 #include <PopUpMenu.h>
27 #include <ScrollBar.h>
28 #include <ScrollView.h>
31 #include <AutoLocker.h>
32 #include <ObjectList.h>
34 #include "AppMessageCodes.h"
35 #include "AutoDeleter.h"
36 #include "Breakpoint.h"
37 #include "DisassembledCode.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"
50 static const int32 kLeftTextMargin
= 3;
51 static const float kMinViewHeight
= 80.0f
;
52 static const int32 kSpacesPerTab
= 4;
53 // TODO: Should be settable!
55 static const int32 kMaxHighlightsPerLine
= 64;
57 static const bigtime_t kScrollTimer
= 10000LL;
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
".";
66 static const uint32 MSG_OPEN_SOURCE_FILE
= 'mosf';
67 static const uint32 MSG_SWITCH_DISASSEMBLY_STATE
= 'msds';
69 static const char* kTrackerSignature
= "application/x-vnd.Be-TRAK";
72 // TODO: make configurable.
73 // Current values taken from Pe's defaults.
74 static rgb_color kSyntaxColors
[] = {
75 {0, 0, 0, 255}, // SYNTAX_HIGHLIGHT_NONE
76 {0x39, 0x74, 0x79, 255}, // SYNTAX_HIGHLIGHT_KEYWORD
77 {0, 0x64, 0, 255}, // SYNTAX_HIGHLIGHT_PREPROCESSOR_KEYWORD
78 {0, 0, 0, 255}, // SYNTAX_HIGHLIGHT_IDENTIFIER
79 {0x44, 0x8a, 0, 255}, // SYNTAX_HIGHLIGHT_OPERATOR
80 {0x70, 0x70, 0x70, 255}, // SYNTAX_HIGHLIGHT_TYPE
81 {0x85, 0x19, 0x19, 255}, // SYNTAX_HIGHLIGHT_NUMERIC_LITERAL
82 {0x3f, 0x48, 0x84, 255}, // SYNTAX_HIGHLIGHT_STRING_LITERAL
83 {0xa1, 0x64, 0xe, 255}, // SYNTAX_HIGHLIGHT_COMMENT
87 class SourceView::BaseView
: public BView
{
89 BaseView(const char* name
,
90 SourceView
* sourceView
, FontInfo
* fontInfo
);
92 virtual void SetSourceCode(SourceCode
* sourceCode
);
94 virtual BSize
PreferredSize();
97 inline int32
LineCount() const;
99 inline float TotalHeight() const;
101 int32
LineAtOffset(float yOffset
) const;
102 void GetLineRange(BRect rect
, int32
& minLine
,
103 int32
& maxLine
) const;
104 BRect
LineRect(uint32 line
) const;
107 SourceView
* fSourceView
;
109 SourceCode
* fSourceCode
;
113 class SourceView::MarkerManager
{
115 MarkerManager(SourceView
* sourceView
,
116 Team
* team
, Listener
* listener
);
118 void SetSourceCode(SourceCode
* sourceCode
);
120 void SetStackTrace(StackTrace
* stackTrace
);
121 void SetStackFrame(StackFrame
* stackFrame
);
123 void UserBreakpointChanged(
124 UserBreakpoint
* breakpoint
);
127 struct InstructionPointerMarker
;
128 struct BreakpointMarker
;
130 template<typename MarkerType
> struct MarkerByLinePredicate
;
132 typedef BObjectList
<Marker
> MarkerList
;
133 typedef BObjectList
<BreakpointMarker
> BreakpointMarkerList
;
135 void GetMarkers(uint32 minLine
, uint32 maxLine
,
136 MarkerList
& markers
);
137 BreakpointMarker
* BreakpointMarkerAtLine(uint32 line
);
140 void _InvalidateIPMarkers();
141 void _InvalidateBreakpointMarkers();
142 void _UpdateIPMarkers();
143 void _UpdateBreakpointMarkers();
145 // TODO: "public" to workaround a GCC2 problem:
147 static int _CompareMarkers(const Marker
* a
,
149 static int _CompareBreakpointMarkers(
150 const BreakpointMarker
* a
,
151 const BreakpointMarker
* b
);
153 template<typename MarkerType
>
154 static int _CompareLineMarkerTemplate(const uint32
* line
,
155 const MarkerType
* marker
);
156 static int _CompareLineMarker(const uint32
* line
,
157 const Marker
* marker
);
158 static int _CompareLineBreakpointMarker(
160 const BreakpointMarker
* marker
);
165 SourceCode
* fSourceCode
;
166 StackTrace
* fStackTrace
;
167 StackFrame
* fStackFrame
;
168 MarkerList fIPMarkers
;
169 BreakpointMarkerList fBreakpointMarkers
;
170 bool fIPMarkersValid
;
171 bool fBreakpointMarkersValid
;
176 class SourceView::MarkerView
: public BaseView
{
178 MarkerView(SourceView
* sourceView
, Team
* team
,
179 Listener
* listener
, MarkerManager
*manager
,
183 virtual void SetSourceCode(SourceCode
* sourceCode
);
185 void SetStackTrace(StackTrace
* stackTrace
);
186 void SetStackFrame(StackFrame
* stackFrame
);
188 void UserBreakpointChanged(
189 UserBreakpoint
* breakpoint
);
191 virtual BSize
MinSize();
192 virtual BSize
MaxSize();
194 virtual void Draw(BRect updateRect
);
196 virtual void MouseDown(BPoint where
);
199 virtual bool GetToolTipAt(BPoint point
, BToolTip
** _tip
);
204 MarkerManager
* fMarkerManager
;
205 StackTrace
* fStackTrace
;
206 StackFrame
* fStackFrame
;
207 rgb_color fBackgroundColor
;
208 rgb_color fBreakpointOptionMarker
;
212 struct SourceView::MarkerManager::Marker
{
216 inline uint32
Line() const;
218 virtual void Draw(BView
* view
, BRect rect
) = 0;
225 struct SourceView::MarkerManager::InstructionPointerMarker
: Marker
{
226 InstructionPointerMarker(uint32 line
,
227 bool topIP
, bool currentIP
);
229 virtual void Draw(BView
* view
, BRect rect
);
231 bool IsCurrentIP() const { return fIsCurrentIP
; }
234 void _DrawArrow(BView
* view
, BPoint tip
, BSize size
,
235 BSize base
, const rgb_color
& color
,
244 struct SourceView::MarkerManager::BreakpointMarker
: Marker
{
245 BreakpointMarker(uint32 line
,
246 target_addr_t address
,
247 UserBreakpoint
* breakpoint
);
250 target_addr_t
Address() const { return fAddress
; }
251 bool IsEnabled() const
252 { return fBreakpoint
->IsEnabled(); }
253 bool HasCondition() const
254 { return fBreakpoint
->HasCondition(); }
255 UserBreakpoint
* Breakpoint() const
256 { return fBreakpoint
; }
258 virtual void Draw(BView
* view
, BRect rect
);
261 target_addr_t fAddress
;
262 UserBreakpoint
* fBreakpoint
;
266 template<typename MarkerType
>
267 struct SourceView::MarkerManager::MarkerByLinePredicate
268 : UnaryPredicate
<MarkerType
> {
269 MarkerByLinePredicate(uint32 line
)
275 virtual int operator()(const MarkerType
* marker
) const
277 return -_CompareLineMarkerTemplate
<MarkerType
>(&fLine
, marker
);
285 class SourceView::TextView
: public BaseView
{
287 TextView(SourceView
* sourceView
,
288 MarkerManager
* manager
,
291 virtual void SetSourceCode(SourceCode
* sourceCode
);
292 void UserBreakpointChanged(
293 UserBreakpoint
* breakpoint
);
295 virtual BSize
MinSize();
296 virtual BSize
MaxSize();
298 virtual void Draw(BRect updateRect
);
300 virtual void KeyDown(const char* bytes
, int32 numBytes
);
301 virtual void MakeFocus(bool isFocused
);
302 virtual void MessageReceived(BMessage
* message
);
303 virtual void MouseDown(BPoint where
);
304 virtual void MouseMoved(BPoint where
, uint32 transit
,
305 const BMessage
* dragMessage
);
306 virtual void MouseUp(BPoint where
);
309 struct SelectionPoint
311 SelectionPoint(int32 _line
, int32 _offset
)
317 bool operator==(const SelectionPoint
& other
)
319 return line
== other
.line
&& offset
== other
.offset
;
333 float _MaxLineWidth();
334 inline float _FormattedLineWidth(const char* line
) const;
336 void _DrawLineSyntaxSection(const char* line
,
337 int32 length
, int32
& _column
,
339 inline void _DrawLineSegment(const char* line
,
340 int32 length
, BPoint
& _offset
);
341 inline int32
_NextTabStop(int32 column
) const;
342 float _FormattedPosition(int32 line
,
344 SelectionPoint
_SelectionPointAt(BPoint where
) const;
345 void _GetSelectionRegion(BRegion
& region
) const;
346 void _GetSelectionText(BString
& text
) const;
347 void _CopySelectionToClipboard() const;
348 void _SelectWordAt(const SelectionPoint
& point
,
349 bool extend
= false);
350 void _SelectLineAt(const SelectionPoint
& point
,
351 bool extend
= false);
352 void _HandleAutoScroll();
353 void _ScrollHorizontal(int32 charCount
);
354 void _ScrollByLines(int32 lineCount
);
355 void _ScrollByPages(int32 pageCount
);
357 void _ScrollToBottom();
359 bool _AddGeneralActions(BPopUpMenu
* menu
,
361 bool _AddFlowControlActions(BPopUpMenu
* menu
,
364 bool _AddGeneralActionItem(BPopUpMenu
* menu
,
365 const char* text
, BMessage
* message
) const;
366 // takes ownership of message
367 // regardless of outcome
368 bool _AddFlowControlActionItem(BPopUpMenu
* menu
,
369 const char* text
, uint32 what
,
370 target_addr_t address
) const;
375 float fCharacterWidth
;
376 SelectionPoint fSelectionStart
;
377 SelectionPoint fSelectionEnd
;
378 SelectionPoint fSelectionBase
;
379 SelectionPoint fLastClickPoint
;
380 bigtime_t fLastClickTime
;
382 rgb_color fTextColor
;
384 TrackingState fTrackState
;
385 BMessageRunner
* fScrollRunner
;
386 MarkerManager
* fMarkerManager
;
390 // #pragma mark - BaseView
393 SourceView::BaseView::BaseView(const char* name
, SourceView
* sourceView
,
396 BView(name
, B_WILL_DRAW
| B_SUBPIXEL_PRECISE
),
397 fSourceView(sourceView
),
405 SourceView::BaseView::SetSourceCode(SourceCode
* sourceCode
)
407 fSourceCode
= sourceCode
;
415 SourceView::BaseView::PreferredSize()
422 SourceView::BaseView::LineCount() const
424 return fSourceCode
!= NULL
? fSourceCode
->CountLines() : 0;
429 SourceView::BaseView::TotalHeight() const
431 float height
= LineCount() * fFontInfo
->lineHeight
- 1;
432 return std::max(height
, kMinViewHeight
);
437 SourceView::BaseView::LineAtOffset(float yOffset
) const
439 int32 lineCount
= LineCount();
440 if (yOffset
< 0 || lineCount
== 0)
443 int32 line
= (int32
)yOffset
/ (int32
)fFontInfo
->lineHeight
;
444 return line
< lineCount
? line
: -1;
449 SourceView::BaseView::GetLineRange(BRect rect
, int32
& minLine
,
450 int32
& maxLine
) const
452 int32 lineHeight
= (int32
)fFontInfo
->lineHeight
;
453 minLine
= (int32
)rect
.top
/ lineHeight
;
454 maxLine
= ((int32
)ceilf(rect
.bottom
) + lineHeight
- 1) / lineHeight
;
455 minLine
= std::max(minLine
, (int32
)0);
456 maxLine
= std::min(maxLine
, fSourceCode
->CountLines() - 1);
461 SourceView::BaseView::LineRect(uint32 line
) const
463 float y
= (float)line
* fFontInfo
->lineHeight
;
464 return BRect(0, y
, Bounds().right
, y
+ fFontInfo
->lineHeight
- 1);
468 // #pragma mark - MarkerView::Marker
471 SourceView::MarkerManager::Marker::Marker(uint32 line
)
478 SourceView::MarkerManager::Marker::~Marker()
484 SourceView::MarkerManager::Marker::Line() const
490 // #pragma mark - MarkerManager::InstructionPointerMarker
493 SourceView::MarkerManager::InstructionPointerMarker::InstructionPointerMarker(
494 uint32 line
, bool topIP
, bool currentIP
)
498 fIsCurrentIP(currentIP
)
504 SourceView::MarkerManager::InstructionPointerMarker::Draw(BView
* view
,
507 // Get the arrow color -- for the top IP, if current, we use blue,
510 if (fIsCurrentIP
&& fIsTopIP
) {
511 color
.set_to(0, 0, 255, 255);
513 color
= tint_color(ui_color(B_PANEL_BACKGROUND_COLOR
),
517 // Draw a filled array for the current IP, otherwise just an
519 BPoint
tip(rect
.right
- 3.5f
, floorf((rect
.top
+ rect
.bottom
) / 2));
521 _DrawArrow(view
, tip
, BSize(10, 10), BSize(5, 5), color
, true);
523 _DrawArrow(view
, tip
+ BPoint(-0.5f
, 0), BSize(9, 8),
524 BSize(5, 4), color
, false);
530 SourceView::MarkerManager::InstructionPointerMarker::_DrawArrow(BView
* view
,
531 BPoint tip
, BSize size
, BSize base
, const rgb_color
& color
, bool fill
)
533 view
->SetHighColor(color
);
535 float baseTop
= tip
.y
- base
.height
/ 2;
536 float baseBottom
= tip
.y
+ base
.height
/ 2;
537 float top
= tip
.y
- size
.height
/ 2;
538 float bottom
= tip
.y
+ size
.height
/ 2;
539 float left
= tip
.x
- size
.width
;
540 float middle
= left
+ base
.width
;
543 points
[0].Set(tip
.x
, tip
.y
);
544 points
[1].Set(middle
, top
);
545 points
[2].Set(middle
, baseTop
);
546 points
[3].Set(left
, baseTop
);
547 points
[4].Set(left
, baseBottom
);
548 points
[5].Set(middle
, baseBottom
);
549 points
[6].Set(middle
, bottom
);
552 view
->FillPolygon(points
, 7);
554 view
->StrokePolygon(points
, 7);
558 // #pragma mark - MarkerManager::BreakpointMarker
561 SourceView::MarkerManager::BreakpointMarker::BreakpointMarker(uint32 line
,
562 target_addr_t address
, UserBreakpoint
* breakpoint
)
566 fBreakpoint(breakpoint
)
568 fBreakpoint
->AcquireReference();
572 SourceView::MarkerManager::BreakpointMarker::~BreakpointMarker()
574 fBreakpoint
->ReleaseReference();
579 SourceView::MarkerManager::BreakpointMarker::Draw(BView
* view
, BRect rect
)
581 float y
= (rect
.top
+ rect
.bottom
) / 2;
582 if (fBreakpoint
->HasCondition())
583 view
->SetHighColor((rgb_color
){0, 192, 0, 255});
585 view
->SetHighColor((rgb_color
){255,0,0,255});
587 if (fBreakpoint
->IsEnabled())
588 view
->FillEllipse(BPoint(rect
.right
- 8, y
), 4, 4);
590 view
->StrokeEllipse(BPoint(rect
.right
- 8, y
), 3.5f
, 3.5f
);
594 // #pragma mark - MarkerManager
597 SourceView::MarkerManager::MarkerManager(SourceView
* sourceView
, Team
* team
,
604 fIPMarkers(10, true),
605 fBreakpointMarkers(20, true),
606 fIPMarkersValid(false),
607 fBreakpointMarkersValid(false)
613 SourceView::MarkerManager::SetSourceCode(SourceCode
* sourceCode
)
615 fSourceCode
= sourceCode
;
616 _InvalidateIPMarkers();
617 _InvalidateBreakpointMarkers();
622 SourceView::MarkerManager::SetStackTrace(StackTrace
* stackTrace
)
624 fStackTrace
= stackTrace
;
625 _InvalidateIPMarkers();
630 SourceView::MarkerManager::SetStackFrame(StackFrame
* stackFrame
)
632 fStackFrame
= stackFrame
;
633 _InvalidateIPMarkers();
638 SourceView::MarkerManager::UserBreakpointChanged(UserBreakpoint
* breakpoint
)
640 _InvalidateBreakpointMarkers();
645 SourceView::MarkerManager::_InvalidateIPMarkers()
647 fIPMarkersValid
= false;
648 fIPMarkers
.MakeEmpty();
653 SourceView::MarkerManager::_InvalidateBreakpointMarkers()
655 fBreakpointMarkersValid
= false;
656 fBreakpointMarkers
.MakeEmpty();
661 SourceView::MarkerManager::_UpdateIPMarkers()
666 fIPMarkers
.MakeEmpty();
668 if (fSourceCode
!= NULL
&& fStackTrace
!= NULL
) {
669 LocatableFile
* sourceFile
= fSourceCode
->GetSourceFile();
671 AutoLocker
<Team
> locker(fTeam
);
673 for (int32 i
= 0; StackFrame
* frame
= fStackTrace
->FrameAt(i
);
675 target_addr_t ip
= frame
->InstructionPointer();
676 FunctionInstance
* functionInstance
;
677 Statement
* statement
;
678 if (fTeam
->GetStatementAtAddress(ip
,
679 functionInstance
, statement
) != B_OK
) {
682 BReference
<Statement
> statementReference(statement
, true);
684 int32 line
= statement
->StartSourceLocation().Line();
685 if (line
< 0 || line
>= fSourceCode
->CountLines())
688 if (sourceFile
!= NULL
) {
689 if (functionInstance
->GetFunction()->SourceFile() != sourceFile
)
692 if (functionInstance
->GetSourceCode() != fSourceCode
)
696 bool isTopFrame
= i
== 0
697 && frame
->Type() != STACK_FRAME_TYPE_SYSCALL
;
699 Marker
* marker
= new(std::nothrow
) InstructionPointerMarker(
700 line
, isTopFrame
, frame
== fStackFrame
);
701 if (marker
== NULL
|| !fIPMarkers
.AddItem(marker
)) {
708 fIPMarkers
.SortItems(&_CompareMarkers
);
710 // TODO: Filter duplicate IP markers (recursive functions)!
713 fIPMarkersValid
= true;
718 SourceView::MarkerManager::_UpdateBreakpointMarkers()
720 if (fBreakpointMarkersValid
)
723 fBreakpointMarkers
.MakeEmpty();
725 if (fSourceCode
!= NULL
) {
726 LocatableFile
* sourceFile
= fSourceCode
->GetSourceFile();
728 AutoLocker
<Team
> locker(fTeam
);
730 // get the breakpoints in our source code range
731 BObjectList
<UserBreakpoint
> breakpoints
;
732 fTeam
->GetBreakpointsForSourceCode(fSourceCode
, breakpoints
);
734 for (int32 i
= 0; UserBreakpoint
* breakpoint
= breakpoints
.ItemAt(i
);
736 if (breakpoint
->IsHidden())
738 UserBreakpointInstance
* breakpointInstance
739 = breakpoint
->InstanceAt(0);
740 FunctionInstance
* functionInstance
;
741 Statement
* statement
;
742 if (fTeam
->GetStatementAtAddress(
743 breakpointInstance
->Address(), functionInstance
,
744 statement
) != B_OK
) {
747 BReference
<Statement
> statementReference(statement
, true);
749 int32 line
= statement
->StartSourceLocation().Line();
750 if (line
< 0 || line
>= fSourceCode
->CountLines())
753 if (sourceFile
!= NULL
) {
754 if (functionInstance
->GetFunction()->SourceFile() != sourceFile
)
757 if (functionInstance
->GetSourceCode() != fSourceCode
)
761 BreakpointMarker
* marker
= new(std::nothrow
) BreakpointMarker(
762 line
, breakpointInstance
->Address(), breakpoint
);
763 if (marker
== NULL
|| !fBreakpointMarkers
.AddItem(marker
)) {
770 fBreakpointMarkers
.SortItems(&_CompareBreakpointMarkers
);
773 fBreakpointMarkersValid
= true;
778 SourceView::MarkerManager::GetMarkers(uint32 minLine
, uint32 maxLine
,
782 _UpdateBreakpointMarkers();
784 int32 ipIndex
= fIPMarkers
.FindBinaryInsertionIndex(
785 MarkerByLinePredicate
<Marker
>(minLine
));
786 int32 breakpointIndex
= fBreakpointMarkers
.FindBinaryInsertionIndex(
787 MarkerByLinePredicate
<BreakpointMarker
>(minLine
));
789 Marker
* ipMarker
= fIPMarkers
.ItemAt(ipIndex
);
790 Marker
* breakpointMarker
= fBreakpointMarkers
.ItemAt(breakpointIndex
);
792 while (ipMarker
!= NULL
&& breakpointMarker
!= NULL
793 && ipMarker
->Line() <= maxLine
&& breakpointMarker
->Line() <= maxLine
) {
794 if (breakpointMarker
->Line() <= ipMarker
->Line()) {
795 markers
.AddItem(breakpointMarker
);
796 breakpointMarker
= fBreakpointMarkers
.ItemAt(++breakpointIndex
);
798 markers
.AddItem(ipMarker
);
799 ipMarker
= fIPMarkers
.ItemAt(++ipIndex
);
803 while (breakpointMarker
!= NULL
&& breakpointMarker
->Line() <= maxLine
) {
804 markers
.AddItem(breakpointMarker
);
805 breakpointMarker
= fBreakpointMarkers
.ItemAt(++breakpointIndex
);
808 while (ipMarker
!= NULL
&& ipMarker
->Line() <= maxLine
) {
809 markers
.AddItem(ipMarker
);
810 ipMarker
= fIPMarkers
.ItemAt(++ipIndex
);
815 SourceView::MarkerManager::BreakpointMarker
*
816 SourceView::MarkerManager::BreakpointMarkerAtLine(uint32 line
)
818 return fBreakpointMarkers
.BinarySearchByKey(line
,
819 &_CompareLineBreakpointMarker
);
824 SourceView::MarkerManager::_CompareMarkers(const Marker
* a
,
827 if (a
->Line() < b
->Line())
829 return a
->Line() == b
->Line() ? 0 : 1;
834 SourceView::MarkerManager::_CompareBreakpointMarkers(const BreakpointMarker
* a
,
835 const BreakpointMarker
* b
)
837 if (a
->Line() < b
->Line())
839 return a
->Line() == b
->Line() ? 0 : 1;
843 template<typename MarkerType
>
845 SourceView::MarkerManager::_CompareLineMarkerTemplate(const uint32
* line
,
846 const MarkerType
* marker
)
848 if (*line
< marker
->Line())
850 return *line
== marker
->Line() ? 0 : 1;
855 SourceView::MarkerManager::_CompareLineMarker(const uint32
* line
,
856 const Marker
* marker
)
858 return _CompareLineMarkerTemplate
<Marker
>(line
, marker
);
863 SourceView::MarkerManager::_CompareLineBreakpointMarker(const uint32
* line
,
864 const BreakpointMarker
* marker
)
866 return _CompareLineMarkerTemplate
<BreakpointMarker
>(line
, marker
);
870 // #pragma mark - MarkerView
873 SourceView::MarkerView::MarkerView(SourceView
* sourceView
, Team
* team
,
874 Listener
* listener
, MarkerManager
* manager
, FontInfo
* fontInfo
)
876 BaseView("source marker view", sourceView
, fontInfo
),
879 fMarkerManager(manager
),
883 rgb_color background
= ui_color(B_PANEL_BACKGROUND_COLOR
);
884 fBreakpointOptionMarker
= tint_color(background
, B_DARKEN_1_TINT
);
885 fBackgroundColor
= tint_color(background
, B_LIGHTEN_2_TINT
);
886 SetViewColor(B_TRANSPARENT_COLOR
);
890 SourceView::MarkerView::~MarkerView()
896 SourceView::MarkerView::SetSourceCode(SourceCode
* sourceCode
)
898 BaseView::SetSourceCode(sourceCode
);
903 SourceView::MarkerView::SetStackTrace(StackTrace
* stackTrace
)
910 SourceView::MarkerView::SetStackFrame(StackFrame
* stackFrame
)
917 SourceView::MarkerView::UserBreakpointChanged(UserBreakpoint
* breakpoint
)
924 SourceView::MarkerView::MinSize()
926 return BSize(40, TotalHeight());
931 SourceView::MarkerView::MaxSize()
933 return BSize(MinSize().width
, B_SIZE_UNLIMITED
);
937 SourceView::MarkerView::Draw(BRect updateRect
)
939 SetLowColor(fBackgroundColor
);
940 if (fSourceCode
== NULL
) {
941 FillRect(updateRect
, B_SOLID_LOW
);
945 // get the lines intersecting with the update rect
946 int32 minLine
, maxLine
;
947 GetLineRange(updateRect
, minLine
, maxLine
);
948 if (minLine
<= maxLine
) {
949 // get the markers in that range
950 SourceView::MarkerManager::MarkerList markers
;
951 fMarkerManager
->GetMarkers(minLine
, maxLine
, markers
);
953 float width
= Bounds().Width();
955 AutoLocker
<SourceCode
> sourceLocker(fSourceCode
);
957 int32 markerIndex
= 0;
958 for (int32 line
= minLine
; line
<= maxLine
; line
++) {
959 bool drawBreakpointOptionMarker
= true;
961 SourceView::MarkerManager::Marker
* marker
;
962 FillRect(LineRect(line
), B_SOLID_LOW
);
963 while ((marker
= markers
.ItemAt(markerIndex
)) != NULL
964 && marker
->Line() == (uint32
)line
) {
965 marker
->Draw(this, LineRect(line
));
966 drawBreakpointOptionMarker
= false;
970 if (!drawBreakpointOptionMarker
)
973 SourceLocation statementStart
, statementEnd
;
974 if (!fSourceCode
->GetStatementLocationRange(SourceLocation(line
),
975 statementStart
, statementEnd
)
976 || statementStart
.Line() != line
) {
980 float y
= ((float)line
+ 0.5f
) * fFontInfo
->lineHeight
;
981 SetHighColor(fBreakpointOptionMarker
);
982 FillEllipse(BPoint(width
- 8, y
), 2, 2);
986 float y
= (maxLine
+ 1) * fFontInfo
->lineHeight
;
987 if (y
< updateRect
.bottom
) {
988 FillRect(BRect(0.0, y
, Bounds().right
, updateRect
.bottom
),
996 SourceView::MarkerView::MouseDown(BPoint where
)
998 if (fSourceCode
== NULL
)
1001 int32 line
= LineAtOffset(where
.y
);
1003 Statement
* statement
;
1004 if (!fSourceView
->GetStatementForLine(line
, statement
))
1006 BReference
<Statement
> statementReference(statement
, true);
1010 BMessage
* message
= Looper()->CurrentMessage();
1011 if (message
->FindInt32("modifiers", &modifiers
) != B_OK
)
1013 if (message
->FindInt32("buttons", &buttons
) != B_OK
)
1014 buttons
= B_PRIMARY_MOUSE_BUTTON
;
1016 SourceView::MarkerManager::BreakpointMarker
* marker
=
1017 fMarkerManager
->BreakpointMarkerAtLine(line
);
1018 target_addr_t address
= marker
!= NULL
1019 ? marker
->Address() : statement
->CoveringAddressRange().Start();
1021 if ((buttons
& B_PRIMARY_MOUSE_BUTTON
) != 0) {
1022 if ((modifiers
& B_SHIFT_KEY
) != 0) {
1023 if (marker
!= NULL
&& !marker
->IsEnabled())
1024 fListener
->ClearBreakpointRequested(address
);
1026 fListener
->SetBreakpointRequested(address
, false);
1028 if (marker
!= NULL
&& marker
->IsEnabled())
1029 fListener
->ClearBreakpointRequested(address
);
1031 fListener
->SetBreakpointRequested(address
, true);
1033 } else if (marker
!= NULL
&& (buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0) {
1034 UserBreakpoint
* breakpoint
= marker
->Breakpoint();
1035 BMessage
message(MSG_SHOW_BREAKPOINT_EDIT_WINDOW
);
1036 message
.AddPointer("breakpoint", breakpoint
);
1037 Looper()->PostMessage(&message
);
1043 SourceView::MarkerView::GetToolTipAt(BPoint point
, BToolTip
** _tip
)
1045 if (fSourceCode
== NULL
)
1048 int32 line
= LineAtOffset(point
.y
);
1052 AutoLocker
<Team
> locker(fTeam
);
1053 Statement
* statement
;
1054 if (fTeam
->GetStatementAtSourceLocation(fSourceCode
,
1055 SourceLocation(line
), statement
) != B_OK
) {
1058 BReference
<Statement
> statementReference(statement
, true);
1059 if (statement
->StartSourceLocation().Line() != line
)
1062 SourceView::MarkerManager::BreakpointMarker
* marker
=
1063 fMarkerManager
->BreakpointMarkerAtLine(line
);
1066 if (marker
== NULL
) {
1067 text
.SetToFormat(kEnableBreakpointMessage
, line
);
1068 } else if ((modifiers() & B_SHIFT_KEY
) != 0) {
1069 if (!marker
->IsEnabled())
1070 text
.SetToFormat(kClearBreakpointMessage
, line
);
1072 text
.SetToFormat(kDisableBreakpointMessage
, line
);
1074 if (marker
->IsEnabled())
1075 text
.SetToFormat(kClearBreakpointMessage
, line
);
1077 text
.SetToFormat(kEnableBreakpointMessage
, line
);
1080 if (text
.Length() > 0) {
1081 BTextToolTip
* tip
= new(std::nothrow
) BTextToolTip(text
);
1093 // #pragma mark - TextView
1096 SourceView::TextView::TextView(SourceView
* sourceView
, MarkerManager
* manager
,
1099 BaseView("source text view", sourceView
, fontInfo
),
1101 fCharacterWidth(fontInfo
->font
.StringWidth("Q")),
1102 fSelectionStart(-1, -1),
1103 fSelectionEnd(-1, -1),
1104 fSelectionBase(-1, -1),
1105 fLastClickPoint(-1, -1),
1108 fSelectionMode(false),
1109 fTrackState(kNotTracking
),
1110 fScrollRunner(NULL
),
1111 fMarkerManager(manager
)
1113 SetViewColor(B_TRANSPARENT_COLOR
);
1114 fTextColor
= ui_color(B_DOCUMENT_TEXT_COLOR
);
1115 SetFlags(Flags() | B_NAVIGABLE
);
1120 SourceView::TextView::SetSourceCode(SourceCode
* sourceCode
)
1123 fSelectionStart
= fSelectionBase
= fSelectionEnd
= SelectionPoint(-1, -1);
1125 BaseView::SetSourceCode(sourceCode
);
1130 SourceView::TextView::UserBreakpointChanged(UserBreakpoint
* breakpoint
)
1137 SourceView::TextView::MinSize()
1139 return BSize(kLeftTextMargin
+ _MaxLineWidth() - 1, TotalHeight());
1144 SourceView::TextView::MaxSize()
1146 return BSize(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
);
1151 SourceView::TextView::Draw(BRect updateRect
)
1153 if (fSourceCode
== NULL
) {
1154 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR
));
1155 FillRect(updateRect
, B_SOLID_LOW
);
1159 // get the lines intersecting with the update rect
1160 int32 minLine
, maxLine
;
1161 GetLineRange(updateRect
, minLine
, maxLine
);
1162 SourceView::MarkerManager::MarkerList markers
;
1163 fMarkerManager
->GetMarkers(minLine
, maxLine
, markers
);
1165 // draw the affected lines
1166 SetHighColor(fTextColor
);
1167 SetFont(&fFontInfo
->font
);
1168 SourceView::MarkerManager::Marker
* marker
;
1169 SourceView::MarkerManager::InstructionPointerMarker
* ipMarker
;
1170 int32 markerIndex
= 0;
1174 int32 columns
[kMaxHighlightsPerLine
];
1175 syntax_highlight_type types
[kMaxHighlightsPerLine
];
1176 SyntaxHighlightInfo
* info
= fSourceView
->fCurrentSyntaxInfo
;
1178 for (int32 i
= minLine
; i
<= maxLine
; i
++) {
1179 int32 syntaxCount
= 0;
1181 syntaxCount
= info
->GetLineHighlightRanges(i
, columns
, types
,
1182 kMaxHighlightsPerLine
);
1185 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR
));
1186 y
= i
* fFontInfo
->lineHeight
;
1188 FillRect(BRect(0.0, y
, kLeftTextMargin
, y
+ fFontInfo
->lineHeight
),
1190 for (int32 j
= markerIndex
; j
< markers
.CountItems(); j
++) {
1191 marker
= markers
.ItemAt(j
);
1192 if (marker
->Line() < (uint32
)i
) {
1195 } else if (marker
->Line() == (uint32
)i
) {
1197 ipMarker
= dynamic_cast<SourceView::MarkerManager
1198 ::InstructionPointerMarker
*>(marker
);
1199 if (ipMarker
!= NULL
) {
1200 if (ipMarker
->IsCurrentIP())
1201 SetLowColor(96, 216, 216, 255);
1203 SetLowColor(216, 216, 216, 255);
1206 SetLowColor(255, 255, 0, 255);
1212 FillRect(BRect(kLeftTextMargin
, y
, Bounds().right
,
1213 y
+ fFontInfo
->lineHeight
- 1), B_SOLID_LOW
);
1215 syntax_highlight_type currentHighlight
= SYNTAX_HIGHLIGHT_NONE
;
1216 SetHighColor(kSyntaxColors
[currentHighlight
]);
1217 const char* lineData
= fSourceCode
->LineAt(i
);
1218 int32 lineLength
= fSourceCode
->LineLengthAt(i
);
1219 BPoint
linePoint(kLeftTextMargin
, y
+ fFontInfo
->fontHeight
.ascent
);
1220 int32 lineOffset
= 0;
1221 int32 currentColumn
= 0;
1222 for (int32 j
= 0; j
< syntaxCount
; j
++) {
1223 int32 length
= columns
[j
] - lineOffset
;
1225 _DrawLineSyntaxSection(lineData
+ lineOffset
, length
,
1226 currentColumn
, linePoint
);
1227 lineOffset
+= length
;
1229 currentHighlight
= types
[j
];
1230 SetHighColor(kSyntaxColors
[currentHighlight
]);
1233 // draw remainder, if any.
1234 if (lineOffset
< lineLength
) {
1235 _DrawLineSyntaxSection(lineData
+ lineOffset
,
1236 lineLength
- lineOffset
, currentColumn
, linePoint
);
1240 y
= (maxLine
+ 1) * fFontInfo
->lineHeight
;
1241 if (y
< updateRect
.bottom
) {
1242 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR
));
1243 FillRect(BRect(0.0, y
, Bounds().right
, updateRect
.bottom
),
1247 if (fSelectionStart
.line
!= -1 && fSelectionEnd
.line
!= -1) {
1249 BRegion selectionRegion
;
1250 _GetSelectionRegion(selectionRegion
);
1251 SetDrawingMode(B_OP_INVERT
);
1252 FillRegion(&selectionRegion
, B_SOLID_HIGH
);
1259 SourceView::TextView::KeyDown(const char* bytes
, int32 numBytes
)
1287 SourceView::BaseView::KeyDown(bytes
, numBytes
);
1292 SourceView::TextView::MakeFocus(bool isFocused
)
1294 fSourceView
->HighlightBorder(isFocused
);
1296 SourceView::BaseView::MakeFocus(isFocused
);
1301 SourceView::TextView::MessageReceived(BMessage
* message
)
1303 switch (message
->what
)
1306 _CopySelectionToClipboard();
1310 fSelectionStart
.line
= 0;
1311 fSelectionStart
.offset
= 0;
1312 fSelectionEnd
.line
= fSourceCode
->CountLines() - 1;
1313 fSelectionEnd
.offset
= fSourceCode
->LineLengthAt(
1314 fSelectionEnd
.line
);
1318 case MSG_TEXTVIEW_AUTOSCROLL
:
1319 _HandleAutoScroll();
1323 SourceView::BaseView::MessageReceived(message
);
1330 SourceView::TextView::MouseDown(BPoint where
)
1332 if (fSourceCode
== NULL
)
1336 if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons
) != B_OK
)
1337 buttons
= B_PRIMARY_MOUSE_BUTTON
;
1340 if (buttons
== B_PRIMARY_MOUSE_BUTTON
) {
1343 fTrackState
= kTracking
;
1345 // don't reset the selection if the user clicks within the
1346 // current selection range
1348 _GetSelectionRegion(region
);
1349 bigtime_t clickTime
= system_time();
1350 SelectionPoint point
= _SelectionPointAt(where
);
1351 fLastClickPoint
= point
;
1352 bigtime_t clickSpeed
= 0;
1353 get_click_speed(&clickSpeed
);
1354 if (clickTime
- fLastClickTime
< clickSpeed
1355 && fSelectionBase
== point
) {
1356 if (fClickCount
> 3) {
1361 fLastClickTime
= clickTime
;
1365 fLastClickTime
= clickTime
;
1368 if (fClickCount
== 2) {
1369 _SelectWordAt(point
);
1370 fSelectionMode
= true;
1371 } else if (fClickCount
== 3) {
1372 _SelectLineAt(point
);
1373 fSelectionMode
= true;
1374 } else if (!region
.Contains(where
)) {
1375 fSelectionBase
= fSelectionStart
= fSelectionEnd
= point
;
1376 fSelectionMode
= true;
1378 SetMouseEventMask(B_POINTER_EVENTS
, B_NO_POINTER_HISTORY
);
1380 } else if (buttons
== B_SECONDARY_MOUSE_BUTTON
) {
1381 int32 line
= LineAtOffset(where
.y
);
1385 ::Team
* team
= fSourceView
->fTeam
;
1386 AutoLocker
<Team
> locker(team
);
1387 ::Thread
* activeThread
= fSourceView
->fActiveThread
;
1389 if (activeThread
== NULL
)
1391 else if (activeThread
->State() != THREAD_STATE_STOPPED
)
1394 BPopUpMenu
* menu
= new(std::nothrow
) BPopUpMenu("");
1397 ObjectDeleter
<BPopUpMenu
> menuDeleter(menu
);
1399 if (!_AddGeneralActions(menu
, line
))
1402 if (!_AddFlowControlActions(menu
, line
))
1405 menuDeleter
.Detach();
1407 BPoint
screenWhere(where
);
1408 ConvertToScreen(&screenWhere
);
1409 menu
->SetTargetForItems(fSourceView
);
1410 BRect
mouseRect(screenWhere
, screenWhere
);
1411 mouseRect
.InsetBy(-4.0, -4.0);
1412 menu
->Go(screenWhere
, true, false, mouseRect
, true);
1418 SourceView::TextView::MouseMoved(BPoint where
, uint32 transit
,
1419 const BMessage
* dragMessage
)
1422 if (fSelectionMode
) {
1424 _GetSelectionRegion(oldRegion
);
1425 SelectionPoint point
= _SelectionPointAt(where
);
1431 case B_OUTSIDE_VIEW
:
1432 if (fClickCount
== 2)
1433 _SelectWordAt(point
, true);
1434 else if (fClickCount
== 3)
1435 _SelectLineAt(point
, true);
1437 if (point
.line
> fSelectionBase
.line
) {
1438 fSelectionStart
= fSelectionBase
;
1439 fSelectionEnd
= point
;
1440 } else if (point
.line
< fSelectionBase
.line
) {
1441 fSelectionEnd
= fSelectionBase
;
1442 fSelectionStart
= point
;
1443 } else if (point
.offset
> fSelectionBase
.offset
) {
1444 fSelectionStart
= fSelectionBase
;
1445 fSelectionEnd
= point
;
1447 fSelectionEnd
= fSelectionBase
;
1448 fSelectionStart
= point
;
1454 fScrollRunner
= new BMessageRunner(BMessenger(this),
1455 new BMessage(MSG_TEXTVIEW_AUTOSCROLL
), kScrollTimer
);
1458 case B_ENTERED_VIEW
:
1459 delete fScrollRunner
;
1460 fScrollRunner
= NULL
;
1463 _GetSelectionRegion(region
);
1464 region
.Include(&oldRegion
);
1465 Invalidate(®ion
);
1466 } else if (fTrackState
== kTracking
) {
1467 _GetSelectionRegion(region
);
1468 if (region
.CountRects() > 0) {
1470 _GetSelectionText(text
);
1472 message
.AddData ("text/plain", B_MIME_TYPE
, text
.String(),
1475 if (fSourceCode
->GetSourceFile() != NULL
)
1476 clipName
= fSourceCode
->GetSourceFile()->Name();
1477 else if (fSourceCode
->GetSourceLanguage() != NULL
)
1478 clipName
= fSourceCode
->GetSourceLanguage()->Name();
1481 clipName
<< " clipping";
1482 message
.AddString ("be:clip_name", clipName
.String());
1483 message
.AddInt32 ("be:actions", B_COPY_TARGET
);
1484 BRect dragRect
= region
.Frame();
1485 BRect visibleRect
= fSourceView
->Bounds();
1486 if (dragRect
.Height() > visibleRect
.Height()) {
1488 dragRect
.bottom
= visibleRect
.Height();
1490 if (dragRect
.Width() > visibleRect
.Width()) {
1492 dragRect
.right
= visibleRect
.Width();
1494 DragMessage(&message
, dragRect
);
1495 fTrackState
= kDragging
;
1502 SourceView::TextView::MouseUp(BPoint where
)
1504 fSelectionMode
= false;
1505 if (fTrackState
== kTracking
&& fClickCount
< 2) {
1507 // if we clicked without dragging or double/triple clicking,
1508 // clear the current selection (if any)
1509 SelectionPoint point
= _SelectionPointAt(where
);
1510 if (fLastClickPoint
== point
) {
1511 fSelectionBase
= fSelectionStart
= fSelectionEnd
;
1515 delete fScrollRunner
;
1516 fScrollRunner
= NULL
;
1517 fTrackState
= kNotTracking
;
1522 SourceView::TextView::_MaxLineWidth()
1524 if (fMaxLineWidth
>= 0)
1525 return fMaxLineWidth
;
1528 if (fSourceCode
!= NULL
) {
1529 for (int32 i
= 0; const char* line
= fSourceCode
->LineAt(i
); i
++)
1530 fMaxLineWidth
= std::max(fMaxLineWidth
, _FormattedLineWidth(line
));
1533 return fMaxLineWidth
;
1538 SourceView::TextView::_FormattedLineWidth(const char* line
) const
1542 for (; line
[i
] != '\0'; i
++) {
1543 if (line
[i
] == '\t')
1544 column
= _NextTabStop(column
);
1549 return column
* fCharacterWidth
;
1554 SourceView::TextView::_DrawLineSyntaxSection(const char* line
, int32 length
,
1555 int32
& _column
, BPoint
& _offset
)
1558 int32 currentLength
= 0;
1559 for (int32 i
= 0; i
< length
; i
++) {
1560 if (line
[i
] == '\t') {
1561 currentLength
= i
- start
;
1562 if (currentLength
!= 0)
1563 _DrawLineSegment(line
+ start
, currentLength
, _offset
);
1565 // set new starting offset to the position after this tab
1567 int32 nextTabStop
= _NextTabStop(_column
);
1568 int32 diff
= nextTabStop
- _column
;
1569 _column
= nextTabStop
;
1570 _offset
.x
+= diff
* fCharacterWidth
;
1575 // draw last segment
1576 currentLength
= length
- start
;
1577 if (currentLength
> 0)
1578 _DrawLineSegment(line
+ start
, currentLength
, _offset
);
1583 SourceView::TextView::_DrawLineSegment(const char* line
, int32 length
,
1586 DrawString(line
, length
, _offset
);
1587 _offset
.x
+= fCharacterWidth
* length
;
1592 SourceView::TextView::_NextTabStop(int32 column
) const
1594 return (column
/ kSpacesPerTab
+ 1) * kSpacesPerTab
;
1599 SourceView::TextView::_FormattedPosition(int32 line
, int32 offset
) const
1602 for (int32 i
= 0; i
< offset
; i
++) {
1603 if (fSourceCode
->LineAt(line
)[i
] == '\t')
1604 column
= _NextTabStop(column
);
1609 return column
* fCharacterWidth
;
1613 SourceView::TextView::SelectionPoint
1614 SourceView::TextView::_SelectionPointAt(BPoint where
) const
1616 int32 line
= LineAtOffset(where
.y
);
1620 int32 lineLength
= fSourceCode
->LineLengthAt(line
);
1621 const char* sourceLine
= fSourceCode
->LineAt(line
);
1623 for (int32 i
= 0; i
< lineLength
; i
++) {
1624 if (sourceLine
[i
] == '\t')
1625 column
= _NextTabStop(column
);
1629 if (column
* fCharacterWidth
> where
.x
) {
1636 offset
= lineLength
;
1639 return SelectionPoint(line
, offset
);
1644 SourceView::TextView::_GetSelectionRegion(BRegion
®ion
) const
1646 if (fSelectionStart
.line
== -1 && fSelectionEnd
.line
== -1)
1649 BRect selectionRect
;
1651 if (fSelectionStart
.line
== fSelectionEnd
.line
) {
1652 if (fSelectionStart
.offset
!= fSelectionEnd
.offset
) {
1653 selectionRect
.left
= _FormattedPosition(fSelectionStart
.line
,
1654 fSelectionStart
.offset
);
1655 selectionRect
.top
= fSelectionStart
.line
* fFontInfo
->lineHeight
;
1656 selectionRect
.right
= _FormattedPosition(fSelectionEnd
.line
,
1657 fSelectionEnd
.offset
);
1658 selectionRect
.bottom
= selectionRect
.top
+ fFontInfo
->lineHeight
;
1659 region
.Include(selectionRect
);
1662 // add rect for starting line
1663 selectionRect
.left
= _FormattedPosition(fSelectionStart
.line
,
1664 fSelectionStart
.offset
);
1665 selectionRect
.top
= fSelectionStart
.line
* fFontInfo
->lineHeight
;
1666 selectionRect
.right
= Bounds().right
;
1667 selectionRect
.bottom
= selectionRect
.top
+ fFontInfo
->lineHeight
;
1668 region
.Include(selectionRect
);
1670 // compute rect for all lines in middle of selection
1671 if (fSelectionEnd
.line
- fSelectionStart
.line
> 1) {
1672 selectionRect
.left
= 0.0;
1673 selectionRect
.top
= (fSelectionStart
.line
+ 1)
1674 * fFontInfo
->lineHeight
;
1675 selectionRect
.right
= Bounds().right
;
1676 selectionRect
.bottom
= fSelectionEnd
.line
* fFontInfo
->lineHeight
;
1677 region
.Include(selectionRect
);
1680 // add rect for last line (if needed)
1681 if (fSelectionEnd
.offset
> 0) {
1682 selectionRect
.left
= 0.0;
1683 selectionRect
.top
= fSelectionEnd
.line
* fFontInfo
->lineHeight
;
1684 selectionRect
.right
= _FormattedPosition(fSelectionEnd
.line
,
1685 fSelectionEnd
.offset
);
1686 selectionRect
.bottom
= selectionRect
.top
+ fFontInfo
->lineHeight
;
1687 region
.Include(selectionRect
);
1690 region
.OffsetBy(kLeftTextMargin
, 0.0);
1695 SourceView::TextView::_GetSelectionText(BString
& text
) const
1697 if (fSelectionStart
.line
== -1 || fSelectionEnd
.line
== -1)
1700 if (fSelectionStart
.line
== fSelectionEnd
.line
) {
1701 text
.SetTo(fSourceCode
->LineAt(fSelectionStart
.line
)
1702 + fSelectionStart
.offset
, fSelectionEnd
.offset
1703 - fSelectionStart
.offset
);
1705 text
.SetTo(fSourceCode
->LineAt(fSelectionStart
.line
)
1706 + fSelectionStart
.offset
);
1708 for (int32 i
= fSelectionStart
.line
+ 1; i
< fSelectionEnd
.line
; i
++)
1709 text
<< fSourceCode
->LineAt(i
) << "\n";
1710 text
.Append(fSourceCode
->LineAt(fSelectionEnd
.line
),
1711 fSelectionEnd
.offset
);
1717 SourceView::TextView::_CopySelectionToClipboard(void) const
1720 _GetSelectionText(text
);
1722 if (text
.Length() > 0) {
1723 be_clipboard
->Lock();
1724 be_clipboard
->Data()->RemoveData("text/plain");
1725 be_clipboard
->Data()->AddData ("text/plain",
1726 B_MIME_TYPE
, text
.String(), text
.Length());
1727 be_clipboard
->Commit();
1728 be_clipboard
->Unlock();
1734 SourceView::TextView::_SelectWordAt(const SelectionPoint
& point
, bool extend
)
1736 const char* line
= fSourceCode
->LineAt(point
.line
);
1737 int32 length
= fSourceCode
->LineLengthAt(point
.line
);
1738 int32 start
= point
.offset
- 1;
1739 int32 end
= point
.offset
+ 1;
1740 while ((end
) < length
) {
1741 if (!isalpha(line
[end
]) && !isdigit(line
[end
]))
1745 while ((start
- 1) >= 0) {
1746 if (!isalpha(line
[start
- 1]) && !isdigit(line
[start
- 1]))
1752 if (point
.line
>= fSelectionBase
.line
1753 || (point
.line
== fSelectionBase
.line
1754 && point
.offset
> fSelectionBase
.offset
)) {
1755 fSelectionStart
.line
= fSelectionBase
.line
;
1756 fSelectionStart
.offset
= fSelectionBase
.offset
;
1757 fSelectionEnd
.line
= point
.line
;
1758 fSelectionEnd
.offset
= end
;
1759 } else if (point
.line
< fSelectionBase
.line
) {
1760 fSelectionStart
.line
= point
.line
;
1761 fSelectionStart
.offset
= start
;
1762 fSelectionEnd
.line
= fSelectionBase
.line
;
1763 fSelectionEnd
.offset
= fSelectionBase
.offset
;
1764 } else if (point
.line
== fSelectionBase
.line
) {
1765 // if we hit here, our offset is before the actual start.
1766 fSelectionStart
.line
= point
.line
;
1767 fSelectionStart
.offset
= start
;
1768 fSelectionEnd
.line
= point
.line
;
1769 fSelectionEnd
.offset
= fSelectionBase
.offset
;
1772 fSelectionBase
.line
= fSelectionStart
.line
= point
.line
;
1773 fSelectionBase
.offset
= fSelectionStart
.offset
= start
;
1774 fSelectionEnd
.line
= point
.line
;
1775 fSelectionEnd
.offset
= end
;
1778 _GetSelectionRegion(region
);
1779 Invalidate(®ion
);
1784 SourceView::TextView::_SelectLineAt(const SelectionPoint
& point
, bool extend
)
1787 if (point
.line
>= fSelectionBase
.line
) {
1788 fSelectionStart
.line
= fSelectionBase
.line
;
1789 fSelectionStart
.offset
= 0;
1790 fSelectionEnd
.line
= point
.line
;
1791 fSelectionEnd
.offset
= fSourceCode
->LineLengthAt(point
.line
);
1793 fSelectionStart
.line
= point
.line
;
1794 fSelectionStart
.offset
= 0;
1795 fSelectionEnd
.line
= fSelectionBase
.line
;
1796 fSelectionEnd
.offset
= fSourceCode
->LineLengthAt(
1797 fSelectionBase
.line
);
1800 fSelectionStart
.line
= fSelectionEnd
.line
= point
.line
;
1801 fSelectionStart
.offset
= 0;
1802 fSelectionEnd
.offset
= fSourceCode
->LineLengthAt(point
.line
);
1805 _GetSelectionRegion(region
);
1806 Invalidate(®ion
);
1811 SourceView::TextView::_HandleAutoScroll(void)
1815 GetMouse(&point
, &buttons
);
1816 float difference
= 0.0;
1818 BRect visibleRect
= Frame() & fSourceView
->Bounds();
1819 if (point
.y
< visibleRect
.top
)
1820 difference
= point
.y
- visibleRect
.top
;
1821 else if (point
.y
> visibleRect
.bottom
)
1822 difference
= point
.y
- visibleRect
.bottom
;
1823 if (difference
!= 0.0) {
1824 factor
= (int)(ceilf(difference
/ fFontInfo
->lineHeight
));
1825 _ScrollByLines(factor
);
1828 if (point
.x
< visibleRect
.left
)
1829 difference
= point
.x
- visibleRect
.left
;
1830 else if (point
.x
> visibleRect
.right
)
1831 difference
= point
.x
- visibleRect
.right
;
1832 if (difference
!= 0.0) {
1833 factor
= (int)(ceilf(difference
/ fCharacterWidth
));
1834 _ScrollHorizontal(factor
);
1837 MouseMoved(point
, B_OUTSIDE_VIEW
, NULL
);
1842 SourceView::TextView::_ScrollHorizontal(int32 charCount
)
1844 BScrollBar
* horizontal
= fSourceView
->ScrollBar(B_HORIZONTAL
);
1845 if (horizontal
== NULL
)
1848 float value
= horizontal
->Value();
1849 horizontal
->SetValue(value
+ fCharacterWidth
* charCount
);
1854 SourceView::TextView::_ScrollByLines(int32 lineCount
)
1856 BScrollBar
* vertical
= fSourceView
->ScrollBar(B_VERTICAL
);
1857 if (vertical
== NULL
)
1860 float value
= vertical
->Value();
1861 vertical
->SetValue(value
+ fFontInfo
->lineHeight
* lineCount
);
1866 SourceView::TextView::_ScrollByPages(int32 pageCount
)
1868 BScrollBar
* vertical
= fSourceView
->ScrollBar(B_VERTICAL
);
1869 if (vertical
== NULL
)
1872 float value
= vertical
->Value();
1873 vertical
->SetValue(value
1874 + fSourceView
->Frame().Size().height
* pageCount
);
1879 SourceView::TextView::_ScrollToTop(void)
1881 BScrollBar
* vertical
= fSourceView
->ScrollBar(B_VERTICAL
);
1882 if (vertical
== NULL
)
1885 vertical
->SetValue(0.0);
1890 SourceView::TextView::_ScrollToBottom(void)
1892 BScrollBar
* vertical
= fSourceView
->ScrollBar(B_VERTICAL
);
1893 if (vertical
== NULL
)
1897 vertical
->GetRange(&min
, &max
);
1898 vertical
->SetValue(max
);
1903 SourceView::TextView::_AddGeneralActions(BPopUpMenu
* menu
, int32 line
)
1905 if (fSourceCode
== NULL
)
1908 BMessage
* message
= NULL
;
1909 if (fSourceCode
->GetSourceFile() != NULL
) {
1910 message
= new(std::nothrow
) BMessage(MSG_OPEN_SOURCE_FILE
);
1911 if (message
== NULL
)
1913 message
->AddInt32("line", line
);
1915 if (!_AddGeneralActionItem(menu
, "Open source file", message
))
1919 if (fSourceView
->fStackFrame
== NULL
)
1922 FunctionInstance
* instance
= fSourceView
->fStackFrame
->Function();
1923 if (instance
== NULL
)
1926 FileSourceCode
* code
= instance
->GetFunction()->GetSourceCode();
1928 // if we only have disassembly, this option doesn't apply.
1932 // verify that we do in fact know the source file of the function,
1933 // since we can't switch to it if it wasn't found and hasn't been
1936 code
->GetSourceFile()->GetLocatedPath(sourcePath
);
1937 if (sourcePath
.IsEmpty())
1940 message
= new(std::nothrow
) BMessage(
1941 MSG_SWITCH_DISASSEMBLY_STATE
);
1942 if (message
== NULL
)
1945 if (!_AddGeneralActionItem(menu
, dynamic_cast<DisassembledCode
*>(
1946 fSourceCode
) != NULL
? "Show source" : "Show disassembly",
1956 SourceView::TextView::_AddFlowControlActions(BPopUpMenu
* menu
, int32 line
)
1958 Statement
* statement
;
1959 if (!fSourceView
->GetStatementForLine(line
, statement
))
1962 BReference
<Statement
> statementReference(statement
, true);
1963 target_addr_t address
= statement
->CoveringAddressRange().Start();
1965 if (menu
->CountItems() > 0)
1966 menu
->AddSeparatorItem();
1968 if (!_AddFlowControlActionItem(menu
, "Run to cursor", MSG_THREAD_RUN
,
1973 if (!_AddFlowControlActionItem(menu
, "Set next statement",
1974 MSG_THREAD_SET_ADDRESS
, address
)) {
1983 SourceView::TextView::_AddGeneralActionItem(BPopUpMenu
* menu
, const char* text
,
1984 BMessage
* message
) const
1986 ObjectDeleter
<BMessage
> messageDeleter(message
);
1988 BMenuItem
* item
= new(std::nothrow
) BMenuItem(text
, message
);
1991 ObjectDeleter
<BMenuItem
> itemDeleter(item
);
1992 messageDeleter
.Detach();
1994 if (!menu
->AddItem(item
))
1997 itemDeleter
.Detach();
2003 SourceView::TextView::_AddFlowControlActionItem(BPopUpMenu
* menu
,
2004 const char* text
, uint32 what
, target_addr_t address
) const
2006 BMessage
* message
= new(std::nothrow
) BMessage(what
);
2007 if (message
== NULL
)
2009 ObjectDeleter
<BMessage
> messageDeleter(message
);
2011 message
->AddUInt64("address", address
);
2012 BMenuItem
* item
= new(std::nothrow
) BMenuItem(text
, message
);
2015 ObjectDeleter
<BMenuItem
> itemDeleter(item
);
2016 messageDeleter
.Detach();
2018 if (!menu
->AddItem(item
))
2021 itemDeleter
.Detach();
2026 // #pragma mark - SourceView
2029 SourceView::SourceView(Team
* team
, Listener
* listener
)
2031 BView("source view", 0),
2033 fActiveThread(NULL
),
2039 fListener(listener
),
2040 fCurrentSyntaxInfo(NULL
)
2043 fFontInfo
.font
= *be_fixed_font
;
2044 fFontInfo
.font
.GetHeight(&fFontInfo
.fontHeight
);
2045 fFontInfo
.lineHeight
= ceilf(fFontInfo
.fontHeight
.ascent
)
2046 + ceilf(fFontInfo
.fontHeight
.descent
);
2050 SourceView::~SourceView()
2052 SetStackFrame(NULL
);
2053 SetStackTrace(NULL
, NULL
);
2054 SetSourceCode(NULL
);
2058 /*static*/ SourceView
*
2059 SourceView::Create(Team
* team
, Listener
* listener
)
2061 SourceView
* self
= new SourceView(team
, listener
);
2075 SourceView::MessageReceived(BMessage
* message
)
2077 switch(message
->what
) {
2078 case MSG_THREAD_RUN
:
2079 case MSG_THREAD_SET_ADDRESS
:
2081 target_addr_t address
;
2082 if (message
->FindUInt64("address", &address
) != B_OK
)
2084 fListener
->ThreadActionRequested(fActiveThread
, message
->what
,
2089 case MSG_OPEN_SOURCE_FILE
:
2092 if (message
->FindInt32("line", &line
) != B_OK
)
2094 // be:line is 1-based.
2096 if (fSourceCode
== NULL
)
2098 LocatableFile
* file
= fSourceCode
->GetSourceFile();
2103 file
->GetLocatedPath(sourcePath
);
2104 if (sourcePath
.IsEmpty())
2107 BPath
path(sourcePath
);
2109 if (path
.InitCheck() != B_OK
)
2112 if (get_ref_for_path(path
.Path(), &ref
) != B_OK
)
2115 BMessage
trackerMessage(B_REFS_RECEIVED
);
2116 trackerMessage
.AddRef("refs", &ref
);
2117 trackerMessage
.AddInt32("be:line", line
);
2119 BMessenger
messenger(kTrackerSignature
);
2120 messenger
.SendMessage(&trackerMessage
);
2124 case MSG_SWITCH_DISASSEMBLY_STATE
:
2126 if (fStackFrame
== NULL
)
2129 FunctionInstance
* instance
= fStackFrame
->Function();
2130 if (instance
== NULL
)
2133 SourceCode
* code
= NULL
;
2134 if (dynamic_cast<FileSourceCode
*>(fSourceCode
) != NULL
) {
2135 if (instance
->SourceCodeState()
2136 == FUNCTION_SOURCE_NOT_LOADED
) {
2137 fListener
->FunctionSourceCodeRequested(instance
, true);
2141 code
= instance
->GetSourceCode();
2143 Function
* function
= instance
->GetFunction();
2144 if (function
->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED
2145 || function
->SourceCodeState()
2146 == FUNCTION_SOURCE_SUPPRESSED
) {
2147 fListener
->FunctionSourceCodeRequested(instance
, false);
2151 code
= function
->GetSourceCode();
2155 SetSourceCode(code
);
2160 BView::MessageReceived(message
);
2167 SourceView::UnsetListener()
2174 SourceView::SetStackTrace(StackTrace
* stackTrace
, Thread
* activeThread
)
2176 TRACE_GUI("SourceView::SetStackTrace(%p)\n", stackTrace
);
2178 if (stackTrace
== fStackTrace
)
2181 if (fActiveThread
!= NULL
)
2182 fActiveThread
->ReleaseReference();
2184 fActiveThread
= activeThread
;
2186 if (fActiveThread
!= NULL
)
2187 fActiveThread
->AcquireReference();
2189 if (fStackTrace
!= NULL
) {
2190 fMarkerManager
->SetStackTrace(NULL
);
2191 fMarkerView
->SetStackTrace(NULL
);
2192 fStackTrace
->ReleaseReference();
2195 fStackTrace
= stackTrace
;
2197 if (fStackTrace
!= NULL
)
2198 fStackTrace
->AcquireReference();
2200 fMarkerManager
->SetStackTrace(fStackTrace
);
2201 fMarkerView
->SetStackTrace(fStackTrace
);
2206 SourceView::SetStackFrame(StackFrame
* stackFrame
)
2208 TRACE_GUI("SourceView::SetStackFrame(%p)\n", stackFrame
);
2209 if (stackFrame
== fStackFrame
)
2212 if (fStackFrame
!= NULL
) {
2213 fMarkerManager
->SetStackFrame(NULL
);
2214 fMarkerView
->SetStackFrame(NULL
);
2215 fStackFrame
->ReleaseReference();
2218 fStackFrame
= stackFrame
;
2220 if (fStackFrame
!= NULL
)
2221 fStackFrame
->AcquireReference();
2223 fMarkerManager
->SetStackFrame(fStackFrame
);
2224 fMarkerView
->SetStackFrame(fStackFrame
);
2225 fTextView
->Invalidate();
2227 if (fStackFrame
!= NULL
)
2228 ScrollToAddress(fStackFrame
->InstructionPointer());
2233 SourceView::SetSourceCode(SourceCode
* sourceCode
)
2235 // set the source code, if it changed
2236 if (sourceCode
== fSourceCode
)
2239 if (fSourceCode
!= NULL
) {
2240 fMarkerManager
->SetSourceCode(NULL
);
2241 fTextView
->SetSourceCode(NULL
);
2242 fMarkerView
->SetSourceCode(NULL
);
2243 fSourceCode
->ReleaseReference();
2244 delete fCurrentSyntaxInfo
;
2245 fCurrentSyntaxInfo
= NULL
;
2248 fSourceCode
= sourceCode
;
2250 if (fSourceCode
!= NULL
) {
2251 fSourceCode
->AcquireReference();
2253 SourceLanguage
* language
= fSourceCode
->GetSourceLanguage();
2254 if (language
!= NULL
) {
2255 SyntaxHighlighter
* highlighter
= language
->GetSyntaxHighlighter();
2256 if (highlighter
!= NULL
) {
2257 BReference
<SyntaxHighlighter
> syntaxReference(highlighter
,
2259 highlighter
->ParseText(fSourceCode
,
2260 fTeam
->GetTeamTypeInformation(), fCurrentSyntaxInfo
);
2265 fMarkerManager
->SetSourceCode(fSourceCode
);
2266 fTextView
->SetSourceCode(fSourceCode
);
2267 fMarkerView
->SetSourceCode(fSourceCode
);
2268 _UpdateScrollBars();
2270 if (fStackFrame
!= NULL
)
2271 ScrollToAddress(fStackFrame
->InstructionPointer());
2276 SourceView::UserBreakpointChanged(UserBreakpoint
* breakpoint
)
2278 fMarkerManager
->UserBreakpointChanged(breakpoint
);
2279 fMarkerView
->UserBreakpointChanged(breakpoint
);
2280 fTextView
->UserBreakpointChanged(breakpoint
);
2285 SourceView::ScrollToAddress(target_addr_t address
)
2287 TRACE_GUI("SourceView::ScrollToAddress(%#" B_PRIx64
")\n", address
);
2289 if (fSourceCode
== NULL
)
2292 AutoLocker
<Team
> locker(fTeam
);
2294 FunctionInstance
* functionInstance
;
2295 Statement
* statement
;
2296 if (fTeam
->GetStatementAtAddress(address
, functionInstance
,
2297 statement
) != B_OK
) {
2300 BReference
<Statement
> statementReference(statement
, true);
2302 return ScrollToLine(statement
->StartSourceLocation().Line());
2307 SourceView::ScrollToLine(uint32 line
)
2309 TRACE_GUI("SourceView::ScrollToLine(%" B_PRIu32
")\n", line
);
2311 if (fSourceCode
== NULL
|| line
>= (uint32
)fSourceCode
->CountLines())
2314 float top
= (float)line
* fFontInfo
.lineHeight
;
2315 float bottom
= top
+ fFontInfo
.lineHeight
- 1;
2317 BRect visible
= Bounds();
2319 TRACE_GUI("SourceView::ScrollToLine(%" B_PRId32
")\n", line
);
2320 TRACE_GUI(" visible: (%f, %f) - (%f, %f), line: %f - %f\n", visible
.left
,
2321 visible
.top
, visible
.right
, visible
.bottom
, top
, bottom
);
2323 // If not visible at all, scroll to the center, otherwise scroll so that at
2324 // least one more line is visible.
2325 if (top
>= visible
.bottom
|| bottom
<= visible
.top
) {
2326 TRACE_GUI(" -> scrolling to (%f, %f)\n", visible
.left
,
2327 top
- (visible
.Height() + 1) / 2);
2328 ScrollTo(visible
.left
, top
- (visible
.Height() + 1) / 2);
2329 } else if (top
- fFontInfo
.lineHeight
< visible
.top
)
2330 ScrollBy(0, top
- fFontInfo
.lineHeight
- visible
.top
);
2331 else if (bottom
+ fFontInfo
.lineHeight
> visible
.bottom
)
2332 ScrollBy(0, bottom
+ fFontInfo
.lineHeight
- visible
.bottom
);
2339 SourceView::HighlightBorder(bool state
)
2341 BScrollView
* parent
= dynamic_cast<BScrollView
*>(Parent());
2343 parent
->SetBorderHighlighted(state
);
2348 SourceView::TargetedByScrollView(BScrollView
* scrollView
)
2350 _UpdateScrollBars();
2355 SourceView::MinSize()
2357 // BSize markerSize(fMarkerView->MinSize());
2358 // BSize textSize(fTextView->MinSize());
2359 // return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
2360 // std::max(markerSize.height, textSize.height));
2361 return BSize(10, 10);
2366 SourceView::MaxSize()
2368 // BSize markerSize(fMarkerView->MaxSize());
2369 // BSize textSize(fTextView->MaxSize());
2370 // return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
2371 // std::min(markerSize.height, textSize.height));
2372 return BSize(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
);
2377 SourceView::PreferredSize()
2379 BSize
markerSize(fMarkerView
->PreferredSize());
2380 BSize
textSize(fTextView
->PreferredSize());
2381 return BSize(BLayoutUtils::AddDistances(markerSize
.width
, textSize
.width
),
2382 std::max(markerSize
.height
, textSize
.height
));
2383 // return MinSize();
2388 SourceView::DoLayout()
2390 BSize size
= _DataRectSize();
2391 float markerWidth
= fMarkerView
->MinSize().width
;
2393 fMarkerView
->MoveTo(0, 0);
2394 fMarkerView
->ResizeTo(markerWidth
, size
.height
);
2396 fTextView
->MoveTo(markerWidth
+ 1, 0);
2397 fTextView
->ResizeTo(size
.width
- markerWidth
- 1, size
.height
);
2399 _UpdateScrollBars();
2404 SourceView::GetStatementForLine(int32 line
, Statement
*& _statement
)
2409 AutoLocker
<Team
> locker(fTeam
);
2410 Statement
* statement
;
2411 if (fTeam
->GetStatementAtSourceLocation(fSourceCode
, SourceLocation(line
),
2412 statement
) != B_OK
) {
2415 BReference
<Statement
> statementReference(statement
, true);
2416 if (statement
->StartSourceLocation().Line() != line
)
2419 _statement
= statement
;
2420 statementReference
.Detach();
2429 fMarkerManager
= new MarkerManager(this, fTeam
, fListener
);
2430 AddChild(fMarkerView
= new MarkerView(this, fTeam
, fListener
,
2431 fMarkerManager
, &fFontInfo
));
2432 AddChild(fTextView
= new TextView(this, fMarkerManager
, &fFontInfo
));
2437 SourceView::_UpdateScrollBars()
2439 BSize dataRectSize
= _DataRectSize();
2440 BSize size
= Frame().Size();
2442 if (BScrollBar
* scrollBar
= ScrollBar(B_HORIZONTAL
)) {
2443 float range
= dataRectSize
.width
- size
.width
;
2445 scrollBar
->SetRange(0, range
);
2446 scrollBar
->SetProportion(
2447 (size
.width
+ 1) / (dataRectSize
.width
+ 1));
2448 scrollBar
->SetSteps(fFontInfo
.lineHeight
, size
.width
+ 1);
2450 scrollBar
->SetRange(0, 0);
2451 scrollBar
->SetProportion(1);
2455 if (BScrollBar
* scrollBar
= ScrollBar(B_VERTICAL
)) {
2456 float range
= dataRectSize
.height
- size
.height
;
2458 scrollBar
->SetRange(0, range
);
2459 scrollBar
->SetProportion(
2460 (size
.height
+ 1) / (dataRectSize
.height
+ 1));
2461 scrollBar
->SetSteps(fFontInfo
.lineHeight
, size
.height
+ 1);
2463 scrollBar
->SetRange(0, 0);
2464 scrollBar
->SetProportion(1);
2471 SourceView::_DataRectSize() const
2473 float width
= fMarkerView
->MinSize().width
+ fTextView
->MinSize().width
+ 1;
2474 float height
= std::max(fMarkerView
->MinSize().height
,
2475 fTextView
->MinSize().height
);
2477 BSize size
= Frame().Size();
2478 return BSize(std::max(size
.width
, width
), std::max(size
.height
, height
));
2482 // #pragma mark - Listener
2485 SourceView::Listener::~Listener()