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