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