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