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