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