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