Debugger: Cleanup.
[haiku.git] / src / apps / debugger / user_interface / gui / team_window / SourceView.cpp
blobee30aae58584b499cb8fe76bf0cc903e594b1447
1 /*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2009-2016, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
8 #include "SourceView.h"
10 #include <algorithm>
11 #include <new>
13 #include <ctype.h>
14 #include <stdio.h>
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>
31 #include <AutoLocker.h>
32 #include <ObjectList.h>
34 #include "AppMessageCodes.h"
35 #include "AutoDeleter.h"
36 #include "Breakpoint.h"
37 #include "DisassembledCode.h"
38 #include "Function.h"
39 #include "FileSourceCode.h"
40 #include "LocatableFile.h"
41 #include "MessageCodes.h"
42 #include "SourceLanguage.h"
43 #include "StackTrace.h"
44 #include "Statement.h"
45 #include "SyntaxHighlighter.h"
46 #include "Team.h"
47 #include "Tracing.h"
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 {
88 public:
89 BaseView(const char* name,
90 SourceView* sourceView, FontInfo* fontInfo);
92 virtual void SetSourceCode(SourceCode* sourceCode);
94 virtual BSize PreferredSize();
96 protected:
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;
106 protected:
107 SourceView* fSourceView;
108 FontInfo* fFontInfo;
109 SourceCode* fSourceCode;
113 class SourceView::MarkerManager {
114 public:
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);
126 struct Marker;
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);
139 private:
140 void _InvalidateIPMarkers();
141 void _InvalidateBreakpointMarkers();
142 void _UpdateIPMarkers();
143 void _UpdateBreakpointMarkers();
145 // TODO: "public" to workaround a GCC2 problem:
146 public:
147 static int _CompareMarkers(const Marker* a,
148 const Marker* b);
149 static int _CompareBreakpointMarkers(
150 const BreakpointMarker* a,
151 const BreakpointMarker* b);
153 template<typename MarkerType>
154 static int _CompareLineMarkerTemplate(const uint32* line,
155 const MarkerType* marker);
156 static int _CompareLineMarker(const uint32* line,
157 const Marker* marker);
158 static int _CompareLineBreakpointMarker(
159 const uint32* line,
160 const BreakpointMarker* marker);
162 private:
163 Team* fTeam;
164 Listener* fListener;
165 SourceCode* fSourceCode;
166 StackTrace* fStackTrace;
167 StackFrame* fStackFrame;
168 MarkerList fIPMarkers;
169 BreakpointMarkerList fBreakpointMarkers;
170 bool fIPMarkersValid;
171 bool fBreakpointMarkersValid;
176 class SourceView::MarkerView : public BaseView {
177 public:
178 MarkerView(SourceView* sourceView, Team* team,
179 Listener* listener, MarkerManager *manager,
180 FontInfo* fontInfo);
181 ~MarkerView();
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);
198 protected:
199 virtual bool GetToolTipAt(BPoint point, BToolTip** _tip);
201 private:
202 Team* fTeam;
203 Listener* fListener;
204 MarkerManager* fMarkerManager;
205 StackTrace* fStackTrace;
206 StackFrame* fStackFrame;
207 rgb_color fBackgroundColor;
208 rgb_color fBreakpointOptionMarker;
212 struct SourceView::MarkerManager::Marker {
213 Marker(uint32 line);
214 virtual ~Marker();
216 inline uint32 Line() const;
218 virtual void Draw(BView* view, BRect rect) = 0;
220 private:
221 uint32 fLine;
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; }
233 private:
234 void _DrawArrow(BView* view, BPoint tip, BSize size,
235 BSize base, const rgb_color& color,
236 bool fill);
238 private:
239 bool fIsTopIP;
240 bool fIsCurrentIP;
244 struct SourceView::MarkerManager::BreakpointMarker : Marker {
245 BreakpointMarker(uint32 line,
246 target_addr_t address,
247 UserBreakpoint* breakpoint);
248 ~BreakpointMarker();
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);
260 private:
261 target_addr_t fAddress;
262 UserBreakpoint* fBreakpoint;
266 template<typename MarkerType>
267 struct SourceView::MarkerManager::MarkerByLinePredicate
268 : UnaryPredicate<MarkerType> {
269 MarkerByLinePredicate(uint32 line)
271 fLine(line)
275 virtual int operator()(const MarkerType* marker) const
277 return -_CompareLineMarkerTemplate<MarkerType>(&fLine, marker);
280 private:
281 uint32 fLine;
285 class SourceView::TextView : public BaseView {
286 public:
287 TextView(SourceView* sourceView,
288 MarkerManager* manager,
289 FontInfo* fontInfo);
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);
308 private:
309 struct SelectionPoint
311 SelectionPoint(int32 _line, int32 _offset)
313 line = _line;
314 offset = _offset;
317 bool operator==(const SelectionPoint& other)
319 return line == other.line && offset == other.offset;
322 int32 line;
323 int32 offset;
326 enum TrackingState
328 kNotTracking = 0,
329 kTracking = 1,
330 kDragging = 2
333 float _MaxLineWidth();
334 inline float _FormattedLineWidth(const char* line) const;
336 void _DrawLineSyntaxSection(const char* line,
337 int32 length, int32& _column,
338 BPoint& _offset);
339 inline void _DrawLineSegment(const char* line,
340 int32 length, BPoint& _offset);
341 inline int32 _NextTabStop(int32 column) const;
342 float _FormattedPosition(int32 line,
343 int32 offset) const;
344 SelectionPoint _SelectionPointAt(BPoint where) const;
345 void _GetSelectionRegion(BRegion& region) const;
346 void _GetSelectionText(BString& text) const;
347 void _CopySelectionToClipboard() const;
348 void _SelectWordAt(const SelectionPoint& point,
349 bool extend = false);
350 void _SelectLineAt(const SelectionPoint& point,
351 bool extend = false);
352 void _HandleAutoScroll();
353 void _ScrollHorizontal(int32 charCount);
354 void _ScrollByLines(int32 lineCount);
355 void _ScrollByPages(int32 pageCount);
356 void _ScrollToTop();
357 void _ScrollToBottom();
359 bool _AddGeneralActions(BPopUpMenu* menu,
360 int32 line);
361 bool _AddFlowControlActions(BPopUpMenu* menu,
362 int32 line);
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;
372 private:
374 float fMaxLineWidth;
375 float fCharacterWidth;
376 SelectionPoint fSelectionStart;
377 SelectionPoint fSelectionEnd;
378 SelectionPoint fSelectionBase;
379 SelectionPoint fLastClickPoint;
380 bigtime_t fLastClickTime;
381 int16 fClickCount;
382 rgb_color fTextColor;
383 bool fSelectionMode;
384 TrackingState fTrackState;
385 BMessageRunner* fScrollRunner;
386 MarkerManager* fMarkerManager;
390 // #pragma mark - BaseView
393 SourceView::BaseView::BaseView(const char* name, SourceView* sourceView,
394 FontInfo* fontInfo)
396 BView(name, B_WILL_DRAW | B_SUBPIXEL_PRECISE),
397 fSourceView(sourceView),
398 fFontInfo(fontInfo),
399 fSourceCode(NULL)
404 void
405 SourceView::BaseView::SetSourceCode(SourceCode* sourceCode)
407 fSourceCode = sourceCode;
409 InvalidateLayout();
410 Invalidate();
414 BSize
415 SourceView::BaseView::PreferredSize()
417 return MinSize();
421 int32
422 SourceView::BaseView::LineCount() const
424 return fSourceCode != NULL ? fSourceCode->CountLines() : 0;
428 float
429 SourceView::BaseView::TotalHeight() const
431 float height = LineCount() * fFontInfo->lineHeight - 1;
432 return std::max(height, kMinViewHeight);
436 int32
437 SourceView::BaseView::LineAtOffset(float yOffset) const
439 int32 lineCount = LineCount();
440 if (yOffset < 0 || lineCount == 0)
441 return -1;
443 int32 line = (int32)yOffset / (int32)fFontInfo->lineHeight;
444 return line < lineCount ? line : -1;
448 void
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);
460 BRect
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)
473 fLine(line)
478 SourceView::MarkerManager::Marker::~Marker()
483 uint32
484 SourceView::MarkerManager::Marker::Line() const
486 return fLine;
490 // #pragma mark - MarkerManager::InstructionPointerMarker
493 SourceView::MarkerManager::InstructionPointerMarker::InstructionPointerMarker(
494 uint32 line, bool topIP, bool currentIP)
496 Marker(line),
497 fIsTopIP(topIP),
498 fIsCurrentIP(currentIP)
503 void
504 SourceView::MarkerManager::InstructionPointerMarker::Draw(BView* view,
505 BRect rect)
507 // Get the arrow color -- for the top IP, if current, we use blue,
508 // otherwise a gray.
509 rgb_color color;
510 if (fIsCurrentIP && fIsTopIP) {
511 color.set_to(0, 0, 255, 255);
512 } else {
513 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
514 B_DARKEN_3_TINT);
517 // Draw a filled array for the current IP, otherwise just an
518 // outline.
519 BPoint tip(rect.right - 3.5f, floorf((rect.top + rect.bottom) / 2));
520 if (fIsCurrentIP) {
521 _DrawArrow(view, tip, BSize(10, 10), BSize(5, 5), color, true);
522 } else {
523 _DrawArrow(view, tip + BPoint(-0.5f, 0), BSize(9, 8),
524 BSize(5, 4), color, false);
529 void
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;
542 BPoint points[7];
543 points[0].Set(tip.x, tip.y);
544 points[1].Set(middle, top);
545 points[2].Set(middle, baseTop);
546 points[3].Set(left, baseTop);
547 points[4].Set(left, baseBottom);
548 points[5].Set(middle, baseBottom);
549 points[6].Set(middle, bottom);
551 if (fill)
552 view->FillPolygon(points, 7);
553 else
554 view->StrokePolygon(points, 7);
558 // #pragma mark - MarkerManager::BreakpointMarker
561 SourceView::MarkerManager::BreakpointMarker::BreakpointMarker(uint32 line,
562 target_addr_t address, UserBreakpoint* breakpoint)
564 Marker(line),
565 fAddress(address),
566 fBreakpoint(breakpoint)
568 fBreakpoint->AcquireReference();
572 SourceView::MarkerManager::BreakpointMarker::~BreakpointMarker()
574 fBreakpoint->ReleaseReference();
578 void
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});
584 else
585 view->SetHighColor((rgb_color){255,0,0,255});
587 if (fBreakpoint->IsEnabled())
588 view->FillEllipse(BPoint(rect.right - 8, y), 4, 4);
589 else
590 view->StrokeEllipse(BPoint(rect.right - 8, y), 3.5f, 3.5f);
594 // #pragma mark - MarkerManager
597 SourceView::MarkerManager::MarkerManager(SourceView* sourceView, Team* team,
598 Listener* listener)
600 fTeam(team),
601 fListener(listener),
602 fStackTrace(NULL),
603 fStackFrame(NULL),
604 fIPMarkers(10, true),
605 fBreakpointMarkers(20, true),
606 fIPMarkersValid(false),
607 fBreakpointMarkersValid(false)
612 void
613 SourceView::MarkerManager::SetSourceCode(SourceCode* sourceCode)
615 fSourceCode = sourceCode;
616 _InvalidateIPMarkers();
617 _InvalidateBreakpointMarkers();
621 void
622 SourceView::MarkerManager::SetStackTrace(StackTrace* stackTrace)
624 fStackTrace = stackTrace;
625 _InvalidateIPMarkers();
629 void
630 SourceView::MarkerManager::SetStackFrame(StackFrame* stackFrame)
632 fStackFrame = stackFrame;
633 _InvalidateIPMarkers();
637 void
638 SourceView::MarkerManager::UserBreakpointChanged(UserBreakpoint* breakpoint)
640 _InvalidateBreakpointMarkers();
644 void
645 SourceView::MarkerManager::_InvalidateIPMarkers()
647 fIPMarkersValid = false;
648 fIPMarkers.MakeEmpty();
652 void
653 SourceView::MarkerManager::_InvalidateBreakpointMarkers()
655 fBreakpointMarkersValid = false;
656 fBreakpointMarkers.MakeEmpty();
660 void
661 SourceView::MarkerManager::_UpdateIPMarkers()
663 if (fIPMarkersValid)
664 return;
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);
674 i++) {
675 target_addr_t ip = frame->InstructionPointer();
676 FunctionInstance* functionInstance;
677 Statement* statement;
678 if (fTeam->GetStatementAtAddress(ip,
679 functionInstance, statement) != B_OK) {
680 continue;
682 BReference<Statement> statementReference(statement, true);
684 int32 line = statement->StartSourceLocation().Line();
685 if (line < 0 || line >= fSourceCode->CountLines())
686 continue;
688 if (sourceFile != NULL) {
689 if (functionInstance->GetFunction()->SourceFile() != sourceFile)
690 continue;
691 } else {
692 if (functionInstance->GetSourceCode() != fSourceCode)
693 continue;
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)) {
702 delete marker;
703 break;
707 // sort by line
708 fIPMarkers.SortItems(&_CompareMarkers);
710 // TODO: Filter duplicate IP markers (recursive functions)!
713 fIPMarkersValid = true;
717 void
718 SourceView::MarkerManager::_UpdateBreakpointMarkers()
720 if (fBreakpointMarkersValid)
721 return;
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);
735 i++) {
736 if (breakpoint->IsHidden())
737 continue;
738 UserBreakpointInstance* breakpointInstance
739 = breakpoint->InstanceAt(0);
740 FunctionInstance* functionInstance;
741 Statement* statement;
742 if (fTeam->GetStatementAtAddress(
743 breakpointInstance->Address(), functionInstance,
744 statement) != B_OK) {
745 continue;
747 BReference<Statement> statementReference(statement, true);
749 int32 line = statement->StartSourceLocation().Line();
750 if (line < 0 || line >= fSourceCode->CountLines())
751 continue;
753 if (sourceFile != NULL) {
754 if (functionInstance->GetFunction()->SourceFile() != sourceFile)
755 continue;
756 } else {
757 if (functionInstance->GetSourceCode() != fSourceCode)
758 continue;
761 BreakpointMarker* marker = new(std::nothrow) BreakpointMarker(
762 line, breakpointInstance->Address(), breakpoint);
763 if (marker == NULL || !fBreakpointMarkers.AddItem(marker)) {
764 delete marker;
765 break;
769 // sort by line
770 fBreakpointMarkers.SortItems(&_CompareBreakpointMarkers);
773 fBreakpointMarkersValid = true;
777 void
778 SourceView::MarkerManager::GetMarkers(uint32 minLine, uint32 maxLine,
779 MarkerList& markers)
781 _UpdateIPMarkers();
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);
797 } else {
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);
823 /*static*/ int
824 SourceView::MarkerManager::_CompareMarkers(const Marker* a,
825 const Marker* b)
827 if (a->Line() < b->Line())
828 return -1;
829 return a->Line() == b->Line() ? 0 : 1;
833 /*static*/ int
834 SourceView::MarkerManager::_CompareBreakpointMarkers(const BreakpointMarker* a,
835 const BreakpointMarker* b)
837 if (a->Line() < b->Line())
838 return -1;
839 return a->Line() == b->Line() ? 0 : 1;
843 template<typename MarkerType>
844 /*static*/ int
845 SourceView::MarkerManager::_CompareLineMarkerTemplate(const uint32* line,
846 const MarkerType* marker)
848 if (*line < marker->Line())
849 return -1;
850 return *line == marker->Line() ? 0 : 1;
854 /*static*/ int
855 SourceView::MarkerManager::_CompareLineMarker(const uint32* line,
856 const Marker* marker)
858 return _CompareLineMarkerTemplate<Marker>(line, marker);
862 /*static*/ int
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),
877 fTeam(team),
878 fListener(listener),
879 fMarkerManager(manager),
880 fStackTrace(NULL),
881 fStackFrame(NULL)
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()
895 void
896 SourceView::MarkerView::SetSourceCode(SourceCode* sourceCode)
898 BaseView::SetSourceCode(sourceCode);
902 void
903 SourceView::MarkerView::SetStackTrace(StackTrace* stackTrace)
905 Invalidate();
909 void
910 SourceView::MarkerView::SetStackFrame(StackFrame* stackFrame)
912 Invalidate();
916 void
917 SourceView::MarkerView::UserBreakpointChanged(UserBreakpoint* breakpoint)
919 Invalidate();
923 BSize
924 SourceView::MarkerView::MinSize()
926 return BSize(40, TotalHeight());
930 BSize
931 SourceView::MarkerView::MaxSize()
933 return BSize(MinSize().width, B_SIZE_UNLIMITED);
936 void
937 SourceView::MarkerView::Draw(BRect updateRect)
939 SetLowColor(fBackgroundColor);
940 if (fSourceCode == NULL) {
941 FillRect(updateRect, B_SOLID_LOW);
942 return;
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;
967 markerIndex++;
970 if (!drawBreakpointOptionMarker)
971 continue;
973 SourceLocation statementStart, statementEnd;
974 if (!fSourceCode->GetStatementLocationRange(SourceLocation(line),
975 statementStart, statementEnd)
976 || statementStart.Line() != line) {
977 continue;
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),
989 B_SOLID_LOW);
995 void
996 SourceView::MarkerView::MouseDown(BPoint where)
998 if (fSourceCode == NULL)
999 return;
1001 int32 line = LineAtOffset(where.y);
1003 Statement* statement;
1004 if (!fSourceView->GetStatementForLine(line, statement))
1005 return;
1006 BReference<Statement> statementReference(statement, true);
1008 int32 modifiers;
1009 int32 buttons;
1010 BMessage* message = Looper()->CurrentMessage();
1011 if (message->FindInt32("modifiers", &modifiers) != B_OK)
1012 modifiers = 0;
1013 if (message->FindInt32("buttons", &buttons) != B_OK)
1014 buttons = B_PRIMARY_MOUSE_BUTTON;
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);
1025 else
1026 fListener->SetBreakpointRequested(address, false);
1027 } else {
1028 if (marker != NULL && marker->IsEnabled())
1029 fListener->ClearBreakpointRequested(address);
1030 else
1031 fListener->SetBreakpointRequested(address, true);
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);
1042 bool
1043 SourceView::MarkerView::GetToolTipAt(BPoint point, BToolTip** _tip)
1045 if (fSourceCode == NULL)
1046 return false;
1048 int32 line = LineAtOffset(point.y);
1049 if (line < 0)
1050 return false;
1052 AutoLocker<Team> locker(fTeam);
1053 Statement* statement;
1054 if (fTeam->GetStatementAtSourceLocation(fSourceCode,
1055 SourceLocation(line), statement) != B_OK) {
1056 return false;
1058 BReference<Statement> statementReference(statement, true);
1059 if (statement->StartSourceLocation().Line() != line)
1060 return false;
1062 SourceView::MarkerManager::BreakpointMarker* marker =
1063 fMarkerManager->BreakpointMarkerAtLine(line);
1065 BString text;
1066 if (marker == NULL) {
1067 text.SetToFormat(kEnableBreakpointMessage, line);
1068 } else if ((modifiers() & B_SHIFT_KEY) != 0) {
1069 if (!marker->IsEnabled())
1070 text.SetToFormat(kClearBreakpointMessage, line);
1071 else
1072 text.SetToFormat(kDisableBreakpointMessage, line);
1073 } else {
1074 if (marker->IsEnabled())
1075 text.SetToFormat(kClearBreakpointMessage, line);
1076 else
1077 text.SetToFormat(kEnableBreakpointMessage, line);
1080 if (text.Length() > 0) {
1081 BTextToolTip* tip = new(std::nothrow) BTextToolTip(text);
1082 if (tip == NULL)
1083 return false;
1085 *_tip = tip;
1086 return true;
1089 return false;
1093 // #pragma mark - TextView
1096 SourceView::TextView::TextView(SourceView* sourceView, MarkerManager* manager,
1097 FontInfo* fontInfo)
1099 BaseView("source text view", sourceView, fontInfo),
1100 fMaxLineWidth(-1),
1101 fCharacterWidth(fontInfo->font.StringWidth("Q")),
1102 fSelectionStart(-1, -1),
1103 fSelectionEnd(-1, -1),
1104 fSelectionBase(-1, -1),
1105 fLastClickPoint(-1, -1),
1106 fLastClickTime(0),
1107 fClickCount(0),
1108 fSelectionMode(false),
1109 fTrackState(kNotTracking),
1110 fScrollRunner(NULL),
1111 fMarkerManager(manager)
1113 SetViewColor(B_TRANSPARENT_COLOR);
1114 fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR);
1115 SetFlags(Flags() | B_NAVIGABLE);
1119 void
1120 SourceView::TextView::SetSourceCode(SourceCode* sourceCode)
1122 fMaxLineWidth = -1;
1123 fSelectionStart = fSelectionBase = fSelectionEnd = SelectionPoint(-1, -1);
1124 fClickCount = 0;
1125 BaseView::SetSourceCode(sourceCode);
1129 void
1130 SourceView::TextView::UserBreakpointChanged(UserBreakpoint* breakpoint)
1132 Invalidate();
1136 BSize
1137 SourceView::TextView::MinSize()
1139 return BSize(kLeftTextMargin + _MaxLineWidth() - 1, TotalHeight());
1143 BSize
1144 SourceView::TextView::MaxSize()
1146 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
1150 void
1151 SourceView::TextView::Draw(BRect updateRect)
1153 if (fSourceCode == NULL) {
1154 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
1155 FillRect(updateRect, B_SOLID_LOW);
1156 return;
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;
1171 float y;
1173 // syntax line data
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;
1180 if (info != NULL) {
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),
1189 B_SOLID_LOW);
1190 for (int32 j = markerIndex; j < markers.CountItems(); j++) {
1191 marker = markers.ItemAt(j);
1192 if (marker->Line() < (uint32)i) {
1193 ++markerIndex;
1194 continue;
1195 } else if (marker->Line() == (uint32)i) {
1196 ++markerIndex;
1197 ipMarker = dynamic_cast<SourceView::MarkerManager
1198 ::InstructionPointerMarker*>(marker);
1199 if (ipMarker != NULL) {
1200 if (ipMarker->IsCurrentIP())
1201 SetLowColor(96, 216, 216, 255);
1202 else
1203 SetLowColor(216, 216, 216, 255);
1205 } else
1206 SetLowColor(255, 255, 0, 255);
1207 break;
1208 } else
1209 break;
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;
1224 if (length != 0) {
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),
1244 B_SOLID_LOW);
1247 if (fSelectionStart.line != -1 && fSelectionEnd.line != -1) {
1248 PushState();
1249 BRegion selectionRegion;
1250 _GetSelectionRegion(selectionRegion);
1251 SetDrawingMode(B_OP_INVERT);
1252 FillRegion(&selectionRegion, B_SOLID_HIGH);
1253 PopState();
1258 void
1259 SourceView::TextView::KeyDown(const char* bytes, int32 numBytes)
1261 switch(bytes[0]) {
1262 case B_UP_ARROW:
1263 _ScrollByLines(-1);
1264 break;
1266 case B_DOWN_ARROW:
1267 _ScrollByLines(1);
1268 break;
1270 case B_PAGE_UP:
1271 _ScrollByPages(-1);
1272 break;
1274 case B_PAGE_DOWN:
1275 _ScrollByPages(1);
1276 break;
1278 case B_HOME:
1279 _ScrollToTop();
1280 break;
1282 case B_END:
1283 _ScrollToBottom();
1284 break;
1287 SourceView::BaseView::KeyDown(bytes, numBytes);
1291 void
1292 SourceView::TextView::MakeFocus(bool isFocused)
1294 fSourceView->HighlightBorder(isFocused);
1296 SourceView::BaseView::MakeFocus(isFocused);
1300 void
1301 SourceView::TextView::MessageReceived(BMessage* message)
1303 switch (message->what)
1305 case B_COPY:
1306 _CopySelectionToClipboard();
1307 break;
1309 case B_SELECT_ALL:
1310 fSelectionStart.line = 0;
1311 fSelectionStart.offset = 0;
1312 fSelectionEnd.line = fSourceCode->CountLines() - 1;
1313 fSelectionEnd.offset = fSourceCode->LineLengthAt(
1314 fSelectionEnd.line);
1315 Invalidate();
1316 break;
1318 case MSG_TEXTVIEW_AUTOSCROLL:
1319 _HandleAutoScroll();
1320 break;
1322 default:
1323 SourceView::BaseView::MessageReceived(message);
1324 break;
1329 void
1330 SourceView::TextView::MouseDown(BPoint where)
1332 if (fSourceCode == NULL)
1333 return;
1335 int32 buttons;
1336 if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
1337 buttons = B_PRIMARY_MOUSE_BUTTON;
1340 if (buttons == B_PRIMARY_MOUSE_BUTTON) {
1341 if (!IsFocus())
1342 MakeFocus(true);
1343 fTrackState = kTracking;
1345 // don't reset the selection if the user clicks within the
1346 // current selection range
1347 BRegion region;
1348 _GetSelectionRegion(region);
1349 bigtime_t clickTime = system_time();
1350 SelectionPoint point = _SelectionPointAt(where);
1351 fLastClickPoint = point;
1352 bigtime_t clickSpeed = 0;
1353 get_click_speed(&clickSpeed);
1354 if (clickTime - fLastClickTime < clickSpeed
1355 && fSelectionBase == point) {
1356 if (fClickCount > 3) {
1357 fClickCount = 0;
1358 fLastClickTime = 0;
1359 } else {
1360 fClickCount++;
1361 fLastClickTime = clickTime;
1363 } else {
1364 fClickCount = 1;
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;
1377 Invalidate();
1378 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
1380 } else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
1381 int32 line = LineAtOffset(where.y);
1382 if (line < 0)
1383 return;
1385 ::Team* team = fSourceView->fTeam;
1386 AutoLocker<Team> locker(team);
1387 ::Thread* activeThread = fSourceView->fActiveThread;
1389 if (activeThread == NULL)
1390 return;
1391 else if (activeThread->State() != THREAD_STATE_STOPPED)
1392 return;
1394 BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("");
1395 if (menu == NULL)
1396 return;
1397 ObjectDeleter<BPopUpMenu> menuDeleter(menu);
1399 if (!_AddGeneralActions(menu, line))
1400 return;
1402 if (!_AddFlowControlActions(menu, line))
1403 return;
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);
1417 void
1418 SourceView::TextView::MouseMoved(BPoint where, uint32 transit,
1419 const BMessage* dragMessage)
1421 BRegion region;
1422 if (fSelectionMode) {
1423 BRegion oldRegion;
1424 _GetSelectionRegion(oldRegion);
1425 SelectionPoint point = _SelectionPointAt(where);
1426 if (point.line < 0)
1427 return;
1429 switch (transit) {
1430 case B_INSIDE_VIEW:
1431 case B_OUTSIDE_VIEW:
1432 if (fClickCount == 2)
1433 _SelectWordAt(point, true);
1434 else if (fClickCount == 3)
1435 _SelectLineAt(point, true);
1436 else {
1437 if (point.line > fSelectionBase.line) {
1438 fSelectionStart = fSelectionBase;
1439 fSelectionEnd = point;
1440 } else if (point.line < fSelectionBase.line) {
1441 fSelectionEnd = fSelectionBase;
1442 fSelectionStart = point;
1443 } else if (point.offset > fSelectionBase.offset) {
1444 fSelectionStart = fSelectionBase;
1445 fSelectionEnd = point;
1446 } else {
1447 fSelectionEnd = fSelectionBase;
1448 fSelectionStart = point;
1451 break;
1453 case B_EXITED_VIEW:
1454 fScrollRunner = new BMessageRunner(BMessenger(this),
1455 new BMessage(MSG_TEXTVIEW_AUTOSCROLL), kScrollTimer);
1456 break;
1458 case B_ENTERED_VIEW:
1459 delete fScrollRunner;
1460 fScrollRunner = NULL;
1461 break;
1463 _GetSelectionRegion(region);
1464 region.Include(&oldRegion);
1465 Invalidate(&region);
1466 } else if (fTrackState == kTracking) {
1467 _GetSelectionRegion(region);
1468 if (region.CountRects() > 0) {
1469 BString text;
1470 _GetSelectionText(text);
1471 BMessage message;
1472 message.AddData ("text/plain", B_MIME_TYPE, text.String(),
1473 text.Length());
1474 BString clipName;
1475 if (fSourceCode->GetSourceFile() != NULL)
1476 clipName = fSourceCode->GetSourceFile()->Name();
1477 else if (fSourceCode->GetSourceLanguage() != NULL)
1478 clipName = fSourceCode->GetSourceLanguage()->Name();
1479 else
1480 clipName = "Text";
1481 clipName << " clipping";
1482 message.AddString ("be:clip_name", clipName.String());
1483 message.AddInt32 ("be:actions", B_COPY_TARGET);
1484 BRect dragRect = region.Frame();
1485 BRect visibleRect = fSourceView->Bounds();
1486 if (dragRect.Height() > visibleRect.Height()) {
1487 dragRect.top = 0;
1488 dragRect.bottom = visibleRect.Height();
1490 if (dragRect.Width() > visibleRect.Width()) {
1491 dragRect.left = 0;
1492 dragRect.right = visibleRect.Width();
1494 DragMessage(&message, dragRect);
1495 fTrackState = kDragging;
1501 void
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;
1512 Invalidate();
1515 delete fScrollRunner;
1516 fScrollRunner = NULL;
1517 fTrackState = kNotTracking;
1521 float
1522 SourceView::TextView::_MaxLineWidth()
1524 if (fMaxLineWidth >= 0)
1525 return fMaxLineWidth;
1527 fMaxLineWidth = 0;
1528 if (fSourceCode != NULL) {
1529 for (int32 i = 0; const char* line = fSourceCode->LineAt(i); i++)
1530 fMaxLineWidth = std::max(fMaxLineWidth, _FormattedLineWidth(line));
1533 return fMaxLineWidth;
1537 float
1538 SourceView::TextView::_FormattedLineWidth(const char* line) const
1540 int32 column = 0;
1541 int32 i = 0;
1542 for (; line[i] != '\0'; i++) {
1543 if (line[i] == '\t')
1544 column = _NextTabStop(column);
1545 else
1546 ++column;
1549 return column * fCharacterWidth;
1553 void
1554 SourceView::TextView::_DrawLineSyntaxSection(const char* line, int32 length,
1555 int32& _column, BPoint& _offset)
1557 int32 start = 0;
1558 int32 currentLength = 0;
1559 for (int32 i = 0; i < length; i++) {
1560 if (line[i] == '\t') {
1561 currentLength = i - start;
1562 if (currentLength != 0)
1563 _DrawLineSegment(line + start, currentLength, _offset);
1565 // set new starting offset to the position after this tab
1566 start = i + 1;
1567 int32 nextTabStop = _NextTabStop(_column);
1568 int32 diff = nextTabStop - _column;
1569 _column = nextTabStop;
1570 _offset.x += diff * fCharacterWidth;
1571 } else
1572 _column++;
1575 // draw last segment
1576 currentLength = length - start;
1577 if (currentLength > 0)
1578 _DrawLineSegment(line + start, currentLength, _offset);
1582 void
1583 SourceView::TextView::_DrawLineSegment(const char* line, int32 length,
1584 BPoint& _offset)
1586 DrawString(line, length, _offset);
1587 _offset.x += fCharacterWidth * length;
1591 int32
1592 SourceView::TextView::_NextTabStop(int32 column) const
1594 return (column / kSpacesPerTab + 1) * kSpacesPerTab;
1598 float
1599 SourceView::TextView::_FormattedPosition(int32 line, int32 offset) const
1601 int32 column = 0;
1602 for (int32 i = 0; i < offset; i++) {
1603 if (fSourceCode->LineAt(line)[i] == '\t')
1604 column = _NextTabStop(column);
1605 else
1606 ++column;
1609 return column * fCharacterWidth;
1613 SourceView::TextView::SelectionPoint
1614 SourceView::TextView::_SelectionPointAt(BPoint where) const
1616 int32 line = LineAtOffset(where.y);
1617 int32 offset = -1;
1618 if (line >= 0) {
1619 int32 column = 0;
1620 int32 lineLength = fSourceCode->LineLengthAt(line);
1621 const char* sourceLine = fSourceCode->LineAt(line);
1623 for (int32 i = 0; i < lineLength; i++) {
1624 if (sourceLine[i] == '\t')
1625 column = _NextTabStop(column);
1626 else
1627 ++column;
1629 if (column * fCharacterWidth > where.x) {
1630 offset = i;
1631 break;
1635 if (offset < 0)
1636 offset = lineLength;
1639 return SelectionPoint(line, offset);
1643 void
1644 SourceView::TextView::_GetSelectionRegion(BRegion &region) const
1646 if (fSelectionStart.line == -1 && fSelectionEnd.line == -1)
1647 return;
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);
1661 } else {
1662 // add rect for starting line
1663 selectionRect.left = _FormattedPosition(fSelectionStart.line,
1664 fSelectionStart.offset);
1665 selectionRect.top = fSelectionStart.line * fFontInfo->lineHeight;
1666 selectionRect.right = Bounds().right;
1667 selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight;
1668 region.Include(selectionRect);
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);
1694 void
1695 SourceView::TextView::_GetSelectionText(BString& text) const
1697 if (fSelectionStart.line == -1 || fSelectionEnd.line == -1)
1698 return;
1700 if (fSelectionStart.line == fSelectionEnd.line) {
1701 text.SetTo(fSourceCode->LineAt(fSelectionStart.line)
1702 + fSelectionStart.offset, fSelectionEnd.offset
1703 - fSelectionStart.offset);
1704 } else {
1705 text.SetTo(fSourceCode->LineAt(fSelectionStart.line)
1706 + fSelectionStart.offset);
1707 text << "\n";
1708 for (int32 i = fSelectionStart.line + 1; i < fSelectionEnd.line; i++)
1709 text << fSourceCode->LineAt(i) << "\n";
1710 text.Append(fSourceCode->LineAt(fSelectionEnd.line),
1711 fSelectionEnd.offset);
1716 void
1717 SourceView::TextView::_CopySelectionToClipboard(void) const
1719 BString text;
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();
1733 void
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]))
1742 break;
1743 ++end;
1745 while ((start - 1) >= 0) {
1746 if (!isalpha(line[start - 1]) && !isdigit(line[start - 1]))
1747 break;
1748 --start;
1751 if (extend) {
1752 if (point.line >= fSelectionBase.line
1753 || (point.line == fSelectionBase.line
1754 && point.offset > fSelectionBase.offset)) {
1755 fSelectionStart.line = fSelectionBase.line;
1756 fSelectionStart.offset = fSelectionBase.offset;
1757 fSelectionEnd.line = point.line;
1758 fSelectionEnd.offset = end;
1759 } else if (point.line < fSelectionBase.line) {
1760 fSelectionStart.line = point.line;
1761 fSelectionStart.offset = start;
1762 fSelectionEnd.line = fSelectionBase.line;
1763 fSelectionEnd.offset = fSelectionBase.offset;
1764 } else if (point.line == fSelectionBase.line) {
1765 // if we hit here, our offset is before the actual start.
1766 fSelectionStart.line = point.line;
1767 fSelectionStart.offset = start;
1768 fSelectionEnd.line = point.line;
1769 fSelectionEnd.offset = fSelectionBase.offset;
1771 } else {
1772 fSelectionBase.line = fSelectionStart.line = point.line;
1773 fSelectionBase.offset = fSelectionStart.offset = start;
1774 fSelectionEnd.line = point.line;
1775 fSelectionEnd.offset = end;
1777 BRegion region;
1778 _GetSelectionRegion(region);
1779 Invalidate(&region);
1783 void
1784 SourceView::TextView::_SelectLineAt(const SelectionPoint& point, bool extend)
1786 if (extend) {
1787 if (point.line >= fSelectionBase.line) {
1788 fSelectionStart.line = fSelectionBase.line;
1789 fSelectionStart.offset = 0;
1790 fSelectionEnd.line = point.line;
1791 fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line);
1792 } else {
1793 fSelectionStart.line = point.line;
1794 fSelectionStart.offset = 0;
1795 fSelectionEnd.line = fSelectionBase.line;
1796 fSelectionEnd.offset = fSourceCode->LineLengthAt(
1797 fSelectionBase.line);
1799 } else {
1800 fSelectionStart.line = fSelectionEnd.line = point.line;
1801 fSelectionStart.offset = 0;
1802 fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line);
1804 BRegion region;
1805 _GetSelectionRegion(region);
1806 Invalidate(&region);
1810 void
1811 SourceView::TextView::_HandleAutoScroll(void)
1813 BPoint point;
1814 uint32 buttons;
1815 GetMouse(&point, &buttons);
1816 float difference = 0.0;
1817 int factor = 0;
1818 BRect visibleRect = Frame() & fSourceView->Bounds();
1819 if (point.y < visibleRect.top)
1820 difference = point.y - visibleRect.top;
1821 else if (point.y > visibleRect.bottom)
1822 difference = point.y - visibleRect.bottom;
1823 if (difference != 0.0) {
1824 factor = (int)(ceilf(difference / fFontInfo->lineHeight));
1825 _ScrollByLines(factor);
1827 difference = 0.0;
1828 if (point.x < visibleRect.left)
1829 difference = point.x - visibleRect.left;
1830 else if (point.x > visibleRect.right)
1831 difference = point.x - visibleRect.right;
1832 if (difference != 0.0) {
1833 factor = (int)(ceilf(difference / fCharacterWidth));
1834 _ScrollHorizontal(factor);
1837 MouseMoved(point, B_OUTSIDE_VIEW, NULL);
1841 void
1842 SourceView::TextView::_ScrollHorizontal(int32 charCount)
1844 BScrollBar* horizontal = fSourceView->ScrollBar(B_HORIZONTAL);
1845 if (horizontal == NULL)
1846 return;
1848 float value = horizontal->Value();
1849 horizontal->SetValue(value + fCharacterWidth * charCount);
1853 void
1854 SourceView::TextView::_ScrollByLines(int32 lineCount)
1856 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
1857 if (vertical == NULL)
1858 return;
1860 float value = vertical->Value();
1861 vertical->SetValue(value + fFontInfo->lineHeight * lineCount);
1865 void
1866 SourceView::TextView::_ScrollByPages(int32 pageCount)
1868 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
1869 if (vertical == NULL)
1870 return;
1872 float value = vertical->Value();
1873 vertical->SetValue(value
1874 + fSourceView->Frame().Size().height * pageCount);
1878 void
1879 SourceView::TextView::_ScrollToTop(void)
1881 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
1882 if (vertical == NULL)
1883 return;
1885 vertical->SetValue(0.0);
1889 void
1890 SourceView::TextView::_ScrollToBottom(void)
1892 BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
1893 if (vertical == NULL)
1894 return;
1896 float min, max;
1897 vertical->GetRange(&min, &max);
1898 vertical->SetValue(max);
1902 bool
1903 SourceView::TextView::_AddGeneralActions(BPopUpMenu* menu, int32 line)
1905 if (fSourceCode == NULL)
1906 return true;
1908 BMessage* message = NULL;
1909 if (fSourceCode->GetSourceFile() != NULL) {
1910 message = new(std::nothrow) BMessage(MSG_OPEN_SOURCE_FILE);
1911 if (message == NULL)
1912 return false;
1913 message->AddInt32("line", line);
1915 if (!_AddGeneralActionItem(menu, "Open source file", message))
1916 return false;
1919 if (fSourceView->fStackFrame == NULL)
1920 return true;
1922 FunctionInstance* instance = fSourceView->fStackFrame->Function();
1923 if (instance == NULL)
1924 return true;
1926 FileSourceCode* code = instance->GetFunction()->GetSourceCode();
1928 // if we only have disassembly, this option doesn't apply.
1929 if (code == NULL)
1930 return true;
1932 // verify that we do in fact know the source file of the function,
1933 // since we can't switch to it if it wasn't found and hasn't been
1934 // located.
1935 BString sourcePath;
1936 code->GetSourceFile()->GetLocatedPath(sourcePath);
1937 if (sourcePath.IsEmpty())
1938 return true;
1940 message = new(std::nothrow) BMessage(
1941 MSG_SWITCH_DISASSEMBLY_STATE);
1942 if (message == NULL)
1943 return false;
1945 if (!_AddGeneralActionItem(menu, dynamic_cast<DisassembledCode*>(
1946 fSourceCode) != NULL ? "Show source" : "Show disassembly",
1947 message)) {
1948 return false;
1951 return true;
1955 bool
1956 SourceView::TextView::_AddFlowControlActions(BPopUpMenu* menu, int32 line)
1958 Statement* statement;
1959 if (!fSourceView->GetStatementForLine(line, statement))
1960 return true;
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,
1969 address)) {
1970 return false;
1973 if (!_AddFlowControlActionItem(menu, "Set next statement",
1974 MSG_THREAD_SET_ADDRESS, address)) {
1975 return false;
1978 return true;
1982 bool
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);
1989 if (item == NULL)
1990 return false;
1991 ObjectDeleter<BMenuItem> itemDeleter(item);
1992 messageDeleter.Detach();
1994 if (!menu->AddItem(item))
1995 return false;
1997 itemDeleter.Detach();
1998 return true;
2002 bool
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)
2008 return false;
2009 ObjectDeleter<BMessage> messageDeleter(message);
2011 message->AddUInt64("address", address);
2012 BMenuItem* item = new(std::nothrow) BMenuItem(text, message);
2013 if (item == NULL)
2014 return false;
2015 ObjectDeleter<BMenuItem> itemDeleter(item);
2016 messageDeleter.Detach();
2018 if (!menu->AddItem(item))
2019 return false;
2021 itemDeleter.Detach();
2022 return true;
2026 // #pragma mark - SourceView
2029 SourceView::SourceView(Team* team, Listener* listener)
2031 BView("source view", 0),
2032 fTeam(team),
2033 fActiveThread(NULL),
2034 fStackTrace(NULL),
2035 fStackFrame(NULL),
2036 fSourceCode(NULL),
2037 fMarkerView(NULL),
2038 fTextView(NULL),
2039 fListener(listener),
2040 fCurrentSyntaxInfo(NULL)
2042 // init font info
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);
2063 try {
2064 self->_Init();
2065 } catch (...) {
2066 delete self;
2067 throw;
2070 return self;
2074 void
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)
2083 break;
2084 fListener->ThreadActionRequested(fActiveThread, message->what,
2085 address);
2086 break;
2089 case MSG_OPEN_SOURCE_FILE:
2091 int32 line;
2092 if (message->FindInt32("line", &line) != B_OK)
2093 break;
2094 // be:line is 1-based.
2095 ++line;
2096 if (fSourceCode == NULL)
2097 break;
2098 LocatableFile* file = fSourceCode->GetSourceFile();
2099 if (file == NULL)
2100 break;
2102 BString sourcePath;
2103 file->GetLocatedPath(sourcePath);
2104 if (sourcePath.IsEmpty())
2105 break;
2107 BPath path(sourcePath);
2108 entry_ref ref;
2109 if (path.InitCheck() != B_OK)
2110 break;
2112 if (get_ref_for_path(path.Path(), &ref) != B_OK)
2113 break;
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);
2121 break;
2124 case MSG_SWITCH_DISASSEMBLY_STATE:
2126 if (fStackFrame == NULL)
2127 break;
2129 FunctionInstance* instance = fStackFrame->Function();
2130 if (instance == NULL)
2131 break;
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);
2138 break;
2141 code = instance->GetSourceCode();
2142 } else {
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);
2148 break;
2151 code = function->GetSourceCode();
2154 if (code != NULL)
2155 SetSourceCode(code);
2156 break;
2159 default:
2160 BView::MessageReceived(message);
2161 break;
2166 void
2167 SourceView::UnsetListener()
2169 fListener = NULL;
2173 void
2174 SourceView::SetStackTrace(StackTrace* stackTrace, Thread* activeThread)
2176 TRACE_GUI("SourceView::SetStackTrace(%p)\n", stackTrace);
2178 if (stackTrace == fStackTrace)
2179 return;
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);
2205 void
2206 SourceView::SetStackFrame(StackFrame* stackFrame)
2208 TRACE_GUI("SourceView::SetStackFrame(%p)\n", stackFrame);
2209 if (stackFrame == fStackFrame)
2210 return;
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());
2232 void
2233 SourceView::SetSourceCode(SourceCode* sourceCode)
2235 // set the source code, if it changed
2236 if (sourceCode == fSourceCode)
2237 return;
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,
2258 true);
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());
2275 void
2276 SourceView::UserBreakpointChanged(UserBreakpoint* breakpoint)
2278 fMarkerManager->UserBreakpointChanged(breakpoint);
2279 fMarkerView->UserBreakpointChanged(breakpoint);
2280 fTextView->UserBreakpointChanged(breakpoint);
2284 bool
2285 SourceView::ScrollToAddress(target_addr_t address)
2287 TRACE_GUI("SourceView::ScrollToAddress(%#" B_PRIx64 ")\n", address);
2289 if (fSourceCode == NULL)
2290 return false;
2292 AutoLocker<Team> locker(fTeam);
2294 FunctionInstance* functionInstance;
2295 Statement* statement;
2296 if (fTeam->GetStatementAtAddress(address, functionInstance,
2297 statement) != B_OK) {
2298 return false;
2300 BReference<Statement> statementReference(statement, true);
2302 return ScrollToLine(statement->StartSourceLocation().Line());
2306 bool
2307 SourceView::ScrollToLine(uint32 line)
2309 TRACE_GUI("SourceView::ScrollToLine(%" B_PRIu32 ")\n", line);
2311 if (fSourceCode == NULL || line >= (uint32)fSourceCode->CountLines())
2312 return false;
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);
2334 return true;
2338 void
2339 SourceView::HighlightBorder(bool state)
2341 BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
2342 if (parent != NULL)
2343 parent->SetBorderHighlighted(state);
2347 void
2348 SourceView::TargetedByScrollView(BScrollView* scrollView)
2350 _UpdateScrollBars();
2354 BSize
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);
2365 BSize
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);
2376 BSize
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();
2387 void
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();
2403 bool
2404 SourceView::GetStatementForLine(int32 line, Statement*& _statement)
2406 if (line < 0)
2407 return false;
2409 AutoLocker<Team> locker(fTeam);
2410 Statement* statement;
2411 if (fTeam->GetStatementAtSourceLocation(fSourceCode, SourceLocation(line),
2412 statement) != B_OK) {
2413 return false;
2415 BReference<Statement> statementReference(statement, true);
2416 if (statement->StartSourceLocation().Line() != line)
2417 return false;
2419 _statement = statement;
2420 statementReference.Detach();
2422 return true;
2426 void
2427 SourceView::_Init()
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));
2436 void
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;
2444 if (range > 0) {
2445 scrollBar->SetRange(0, range);
2446 scrollBar->SetProportion(
2447 (size.width + 1) / (dataRectSize.width + 1));
2448 scrollBar->SetSteps(fFontInfo.lineHeight, size.width + 1);
2449 } else {
2450 scrollBar->SetRange(0, 0);
2451 scrollBar->SetProportion(1);
2455 if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
2456 float range = dataRectSize.height - size.height;
2457 if (range > 0) {
2458 scrollBar->SetRange(0, range);
2459 scrollBar->SetProportion(
2460 (size.height + 1) / (dataRectSize.height + 1));
2461 scrollBar->SetSteps(fFontInfo.lineHeight, size.height + 1);
2462 } else {
2463 scrollBar->SetRange(0, 0);
2464 scrollBar->SetProportion(1);
2470 BSize
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()