xref: /haiku/src/apps/debugger/user_interface/gui/team_window/SourceView.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2009-2016, Rene Gollent, rene@gollent.com.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "SourceView.h"
9 
10 #include <algorithm>
11 #include <new>
12 
13 #include <ctype.h>
14 #include <stdio.h>
15 
16 #include <Clipboard.h>
17 #include <Entry.h>
18 #include <LayoutUtils.h>
19 #include <Looper.h>
20 #include <MenuItem.h>
21 #include <Message.h>
22 #include <MessageRunner.h>
23 #include <Path.h>
24 #include <Polygon.h>
25 #include <PopUpMenu.h>
26 #include <Region.h>
27 #include <ScrollBar.h>
28 #include <ScrollView.h>
29 #include <ToolTip.h>
30 
31 #include <AutoLocker.h>
32 #include <ObjectList.h>
33 
34 #include "AppMessageCodes.h"
35 #include "AutoDeleter.h"
36 #include "Breakpoint.h"
37 #include "DisassembledCode.h"
38 #include "Function.h"
39 #include "FileSourceCode.h"
40 #include "LocatableFile.h"
41 #include "MessageCodes.h"
42 #include "SourceLanguage.h"
43 #include "StackTrace.h"
44 #include "Statement.h"
45 #include "SyntaxHighlighter.h"
46 #include "Team.h"
47 #include "Tracing.h"
48 
49 
50 static const int32 kLeftTextMargin = 3;
51 static const float kMinViewHeight = 80.0f;
52 static const int32 kSpacesPerTab = 4;
53 	// TODO: Should be settable!
54 
55 static const int32 kMaxHighlightsPerLine = 64;
56 
57 static const bigtime_t kScrollTimer = 10000LL;
58 
59 static const char* kClearBreakpointMessage = "Click to clear breakpoint at "
60 	"line %" B_PRId32 ".";
61 static const char* kDisableBreakpointMessage = "Click to disable breakpoint at "
62 	"line %" B_PRId32 ".";
63 static const char* kEnableBreakpointMessage = "Click to enable breakpoint at "
64 	"line %" B_PRId32 ".";
65 
66 static const uint32 MSG_OPEN_SOURCE_FILE = 'mosf';
67 static const uint32 MSG_SWITCH_DISASSEMBLY_STATE = 'msds';
68 
69 static const char* kTrackerSignature = "application/x-vnd.Be-TRAK";
70 
71 
72 // TODO: make configurable.
73 // Current values taken from Pe's defaults.
74 static rgb_color 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 
87 class SourceView::BaseView : public BView {
88 public:
89 								BaseView(const char* name,
90 									SourceView* sourceView, FontInfo* fontInfo);
91 
92 	virtual	void				SetSourceCode(SourceCode* sourceCode);
93 
94 	virtual	BSize				PreferredSize();
95 
96 protected:
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 
106 protected:
107 			SourceView*			fSourceView;
108 			FontInfo*			fFontInfo;
109 			SourceCode*			fSourceCode;
110 };
111 
112 
113 class SourceView::MarkerManager {
114 public:
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 
139 private:
140 			void				_InvalidateIPMarkers();
141 			void				_InvalidateBreakpointMarkers();
142 			void				_UpdateIPMarkers();
143 			void				_UpdateBreakpointMarkers();
144 
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);
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 
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;
172 
173 };
174 
175 
176 class SourceView::MarkerView : public BaseView {
177 public:
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 
198 protected:
199 	virtual bool				GetToolTipAt(BPoint point, BToolTip** _tip);
200 
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;
209 };
210 
211 
212 struct 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 
220 private:
221 	uint32	fLine;
222 };
223 
224 
225 struct 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 
233 private:
234 			void				_DrawArrow(BView* view, BPoint tip, BSize size,
235 									BSize base, const rgb_color& color,
236 									bool fill);
237 
238 private:
239 			bool				fIsTopIP;
240 			bool				fIsCurrentIP;
241 };
242 
243 
244 struct 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 
260 private:
261 			target_addr_t		fAddress;
262 			UserBreakpoint*		fBreakpoint;
263 };
264 
265 
266 template<typename MarkerType>
267 struct 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 
280 private:
281 	uint32	fLine;
282 };
283 
284 
285 class SourceView::TextView : public BaseView {
286 public:
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 
308 private:
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 
372 private:
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 
393 SourceView::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 
404 void
405 SourceView::BaseView::SetSourceCode(SourceCode* sourceCode)
406 {
407 	fSourceCode = sourceCode;
408 
409 	InvalidateLayout();
410 	Invalidate();
411 }
412 
413 
414 BSize
415 SourceView::BaseView::PreferredSize()
416 {
417 	return MinSize();
418 }
419 
420 
421 int32
422 SourceView::BaseView::LineCount() const
423 {
424 	return fSourceCode != NULL ? fSourceCode->CountLines() : 0;
425 }
426 
427 
428 float
429 SourceView::BaseView::TotalHeight() const
430 {
431 	float height = LineCount() * fFontInfo->lineHeight - 1;
432 	return std::max(height, kMinViewHeight);
433 }
434 
435 
436 int32
437 SourceView::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 
448 void
449 SourceView::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 
460 BRect
461 SourceView::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 
471 SourceView::MarkerManager::Marker::Marker(uint32 line)
472 	:
473 	fLine(line)
474 {
475 }
476 
477 
478 SourceView::MarkerManager::Marker::~Marker()
479 {
480 }
481 
482 
483 uint32
484 SourceView::MarkerManager::Marker::Line() const
485 {
486 	return fLine;
487 }
488 
489 
490 // #pragma mark - MarkerManager::InstructionPointerMarker
491 
492 
493 SourceView::MarkerManager::InstructionPointerMarker::InstructionPointerMarker(
494 	uint32 line, bool topIP, bool currentIP)
495 	:
496 	Marker(line),
497 	fIsTopIP(topIP),
498 	fIsCurrentIP(currentIP)
499 {
500 }
501 
502 
503 void
504 SourceView::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 
529 void
530 SourceView::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 
561 SourceView::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 
572 SourceView::MarkerManager::BreakpointMarker::~BreakpointMarker()
573 {
574 	fBreakpoint->ReleaseReference();
575 }
576 
577 
578 void
579 SourceView::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 
597 SourceView::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 
612 void
613 SourceView::MarkerManager::SetSourceCode(SourceCode* sourceCode)
614 {
615 	fSourceCode = sourceCode;
616 	_InvalidateIPMarkers();
617 	_InvalidateBreakpointMarkers();
618 }
619 
620 
621 void
622 SourceView::MarkerManager::SetStackTrace(StackTrace* stackTrace)
623 {
624 	fStackTrace = stackTrace;
625 	_InvalidateIPMarkers();
626 }
627 
628 
629 void
630 SourceView::MarkerManager::SetStackFrame(StackFrame* stackFrame)
631 {
632 	fStackFrame = stackFrame;
633 	_InvalidateIPMarkers();
634 }
635 
636 
637 void
638 SourceView::MarkerManager::UserBreakpointChanged(UserBreakpoint* breakpoint)
639 {
640 	_InvalidateBreakpointMarkers();
641 }
642 
643 
644 void
645 SourceView::MarkerManager::_InvalidateIPMarkers()
646 {
647 	fIPMarkersValid = false;
648 	fIPMarkers.MakeEmpty();
649 }
650 
651 
652 void
653 SourceView::MarkerManager::_InvalidateBreakpointMarkers()
654 {
655 	fBreakpointMarkersValid = false;
656 	fBreakpointMarkers.MakeEmpty();
657 }
658 
659 
660 void
661 SourceView::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 
717 void
718 SourceView::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 
777 void
778 SourceView::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 
815 SourceView::MarkerManager::BreakpointMarker*
816 SourceView::MarkerManager::BreakpointMarkerAtLine(uint32 line)
817 {
818 	return fBreakpointMarkers.BinarySearchByKey(line,
819 		&_CompareLineBreakpointMarker);
820 }
821 
822 
823 /*static*/ int
824 SourceView::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
834 SourceView::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 
843 template<typename MarkerType>
844 /*static*/ int
845 SourceView::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
855 SourceView::MarkerManager::_CompareLineMarker(const uint32* line,
856 	const Marker* marker)
857 {
858 	return _CompareLineMarkerTemplate<Marker>(line, marker);
859 }
860 
861 
862 /*static*/ int
863 SourceView::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 
873 SourceView::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 
890 SourceView::MarkerView::~MarkerView()
891 {
892 }
893 
894 
895 void
896 SourceView::MarkerView::SetSourceCode(SourceCode* sourceCode)
897 {
898 	BaseView::SetSourceCode(sourceCode);
899 }
900 
901 
902 void
903 SourceView::MarkerView::SetStackTrace(StackTrace* stackTrace)
904 {
905 	Invalidate();
906 }
907 
908 
909 void
910 SourceView::MarkerView::SetStackFrame(StackFrame* stackFrame)
911 {
912 	Invalidate();
913 }
914 
915 
916 void
917 SourceView::MarkerView::UserBreakpointChanged(UserBreakpoint* breakpoint)
918 {
919 	Invalidate();
920 }
921 
922 
923 BSize
924 SourceView::MarkerView::MinSize()
925 {
926 	return BSize(40, TotalHeight());
927 }
928 
929 
930 BSize
931 SourceView::MarkerView::MaxSize()
932 {
933 	return BSize(MinSize().width, B_SIZE_UNLIMITED);
934 }
935 
936 void
937 SourceView::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 
995 void
996 SourceView::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 
1042 bool
1043 SourceView::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 
1096 SourceView::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 
1119 void
1120 SourceView::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 
1129 void
1130 SourceView::TextView::UserBreakpointChanged(UserBreakpoint* breakpoint)
1131 {
1132 	Invalidate();
1133 }
1134 
1135 
1136 BSize
1137 SourceView::TextView::MinSize()
1138 {
1139 	return BSize(kLeftTextMargin + _MaxLineWidth() - 1, TotalHeight());
1140 }
1141 
1142 
1143 BSize
1144 SourceView::TextView::MaxSize()
1145 {
1146 	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
1147 }
1148 
1149 
1150 void
1151 SourceView::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 
1258 void
1259 SourceView::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 
1291 void
1292 SourceView::TextView::MakeFocus(bool isFocused)
1293 {
1294 	fSourceView->HighlightBorder(isFocused);
1295 
1296 	SourceView::BaseView::MakeFocus(isFocused);
1297 }
1298 
1299 
1300 void
1301 SourceView::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 
1329 void
1330 SourceView::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 
1417 void
1418 SourceView::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 
1501 void
1502 SourceView::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 
1521 float
1522 SourceView::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 
1537 float
1538 SourceView::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 
1553 void
1554 SourceView::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 
1582 void
1583 SourceView::TextView::_DrawLineSegment(const char* line, int32 length,
1584 	BPoint& _offset)
1585 {
1586 	DrawString(line, length, _offset);
1587 	_offset.x += fCharacterWidth * length;
1588 }
1589 
1590 
1591 int32
1592 SourceView::TextView::_NextTabStop(int32 column) const
1593 {
1594 	return (column / kSpacesPerTab + 1) * kSpacesPerTab;
1595 }
1596 
1597 
1598 float
1599 SourceView::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 
1613 SourceView::TextView::SelectionPoint
1614 SourceView::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 
1643 void
1644 SourceView::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 
1694 void
1695 SourceView::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 
1716 void
1717 SourceView::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 
1733 void
1734 SourceView::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 
1783 void
1784 SourceView::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 
1810 void
1811 SourceView::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 
1841 void
1842 SourceView::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 
1853 void
1854 SourceView::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 
1865 void
1866 SourceView::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 
1878 void
1879 SourceView::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 
1889 void
1890 SourceView::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 
1902 bool
1903 SourceView::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 
1955 bool
1956 SourceView::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 
1982 bool
1983 SourceView::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 
2002 bool
2003 SourceView::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 
2029 SourceView::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 
2051 SourceView::~SourceView()
2052 {
2053 	SetStackFrame(NULL);
2054 	SetStackTrace(NULL, NULL);
2055 	SetSourceCode(NULL);
2056 
2057 	delete fMarkerManager;
2058 }
2059 
2060 
2061 /*static*/ SourceView*
2062 SourceView::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 
2077 void
2078 SourceView::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 
2169 void
2170 SourceView::UnsetListener()
2171 {
2172 	fListener = NULL;
2173 }
2174 
2175 
2176 void
2177 SourceView::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 
2208 void
2209 SourceView::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 
2235 void
2236 SourceView::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 
2278 void
2279 SourceView::UserBreakpointChanged(UserBreakpoint* breakpoint)
2280 {
2281 	fMarkerManager->UserBreakpointChanged(breakpoint);
2282 	fMarkerView->UserBreakpointChanged(breakpoint);
2283 	fTextView->UserBreakpointChanged(breakpoint);
2284 }
2285 
2286 
2287 bool
2288 SourceView::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 
2309 bool
2310 SourceView::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 
2341 void
2342 SourceView::HighlightBorder(bool state)
2343 {
2344 	BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
2345 	if (parent != NULL)
2346 		parent->SetBorderHighlighted(state);
2347 }
2348 
2349 
2350 void
2351 SourceView::TargetedByScrollView(BScrollView* scrollView)
2352 {
2353 	_UpdateScrollBars();
2354 }
2355 
2356 
2357 BSize
2358 SourceView::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 
2368 BSize
2369 SourceView::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 
2379 BSize
2380 SourceView::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 
2390 void
2391 SourceView::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 
2406 bool
2407 SourceView::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 
2429 void
2430 SourceView::_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 
2439 void
2440 SourceView::_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 
2473 BSize
2474 SourceView::_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 
2488 SourceView::Listener::~Listener()
2489 {
2490 }
2491