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