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