- Split MessageCodes.h into a second file that separates out the application-specific message codes from those used by the core. Adjust includes accordingly. No functional change.
2488 lines
58 KiB
C++
2488 lines
58 KiB
C++
/*
|
|
* Copyright 2009-2012, Ingo Weinhold, [email protected].
|
|
* Copyright 2009-2016, Rene Gollent, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "SourceView.h"
|
|
|
|
#include <algorithm>
|
|
#include <new>
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
|
|
#include <Clipboard.h>
|
|
#include <Entry.h>
|
|
#include <LayoutUtils.h>
|
|
#include <Looper.h>
|
|
#include <MenuItem.h>
|
|
#include <Message.h>
|
|
#include <MessageRunner.h>
|
|
#include <Path.h>
|
|
#include <Polygon.h>
|
|
#include <PopUpMenu.h>
|
|
#include <Region.h>
|
|
#include <ScrollBar.h>
|
|
#include <ScrollView.h>
|
|
#include <ToolTip.h>
|
|
|
|
#include <AutoLocker.h>
|
|
#include <ObjectList.h>
|
|
|
|
#include "AppMessageCodes.h"
|
|
#include "AutoDeleter.h"
|
|
#include "Breakpoint.h"
|
|
#include "DisassembledCode.h"
|
|
#include "Function.h"
|
|
#include "FileSourceCode.h"
|
|
#include "LocatableFile.h"
|
|
#include "MessageCodes.h"
|
|
#include "SourceLanguage.h"
|
|
#include "StackTrace.h"
|
|
#include "Statement.h"
|
|
#include "SyntaxHighlighter.h"
|
|
#include "Team.h"
|
|
#include "Tracing.h"
|
|
|
|
|
|
static const int32 kLeftTextMargin = 3;
|
|
static const float kMinViewHeight = 80.0f;
|
|
static const int32 kSpacesPerTab = 4;
|
|
// TODO: Should be settable!
|
|
|
|
static const int32 kMaxHighlightsPerLine = 64;
|
|
|
|
static const bigtime_t kScrollTimer = 10000LL;
|
|
|
|
static const char* kClearBreakpointMessage = "Click to clear breakpoint at "
|
|
"line %" B_PRId32 ".";
|
|
static const char* kDisableBreakpointMessage = "Click to disable breakpoint at "
|
|
"line %" B_PRId32 ".";
|
|
static const char* kEnableBreakpointMessage = "Click to enable breakpoint at "
|
|
"line %" B_PRId32 ".";
|
|
|
|
static const uint32 MSG_OPEN_SOURCE_FILE = 'mosf';
|
|
static const uint32 MSG_SWITCH_DISASSEMBLY_STATE = 'msds';
|
|
|
|
static const char* kTrackerSignature = "application/x-vnd.Be-TRAK";
|
|
|
|
|
|
// TODO: make configurable.
|
|
// Current values taken from Pe's defaults.
|
|
static rgb_color kSyntaxColors[] = {
|
|
{0, 0, 0, 255}, // SYNTAX_HIGHLIGHT_NONE
|
|
{0x39, 0x74, 0x79, 255}, // SYNTAX_HIGHLIGHT_KEYWORD
|
|
{0, 0x64, 0, 255}, // SYNTAX_HIGHLIGHT_PREPROCESSOR_KEYWORD
|
|
{0, 0, 0, 255}, // SYNTAX_HIGHLIGHT_IDENTIFIER
|
|
{0x44, 0x8a, 0, 255}, // SYNTAX_HIGHLIGHT_OPERATOR
|
|
{0x70, 0x70, 0x70, 255}, // SYNTAX_HIGHLIGHT_TYPE
|
|
{0x85, 0x19, 0x19, 255}, // SYNTAX_HIGHLIGHT_NUMERIC_LITERAL
|
|
{0x3f, 0x48, 0x84, 255}, // SYNTAX_HIGHLIGHT_STRING_LITERAL
|
|
{0xa1, 0x64, 0xe, 255}, // SYNTAX_HIGHLIGHT_COMMENT
|
|
};
|
|
|
|
|
|
class SourceView::BaseView : public BView {
|
|
public:
|
|
BaseView(const char* name,
|
|
SourceView* sourceView, FontInfo* fontInfo);
|
|
|
|
virtual void SetSourceCode(SourceCode* sourceCode);
|
|
|
|
virtual BSize PreferredSize();
|
|
|
|
protected:
|
|
inline int32 LineCount() const;
|
|
|
|
inline float TotalHeight() const;
|
|
|
|
int32 LineAtOffset(float yOffset) const;
|
|
void GetLineRange(BRect rect, int32& minLine,
|
|
int32& maxLine) const;
|
|
BRect LineRect(uint32 line) const;
|
|
|
|
protected:
|
|
SourceView* fSourceView;
|
|
FontInfo* fFontInfo;
|
|
SourceCode* fSourceCode;
|
|
};
|
|
|
|
|
|
class SourceView::MarkerManager {
|
|
public:
|
|
MarkerManager(SourceView* sourceView,
|
|
Team* team, Listener* listener);
|
|
|
|
void SetSourceCode(SourceCode* sourceCode);
|
|
|
|
void SetStackTrace(StackTrace* stackTrace);
|
|
void SetStackFrame(StackFrame* stackFrame);
|
|
|
|
void UserBreakpointChanged(
|
|
UserBreakpoint* breakpoint);
|
|
|
|
struct Marker;
|
|
struct InstructionPointerMarker;
|
|
struct BreakpointMarker;
|
|
|
|
template<typename MarkerType> struct MarkerByLinePredicate;
|
|
|
|
typedef BObjectList<Marker> MarkerList;
|
|
typedef BObjectList<BreakpointMarker> BreakpointMarkerList;
|
|
|
|
void GetMarkers(uint32 minLine, uint32 maxLine,
|
|
MarkerList& markers);
|
|
BreakpointMarker* BreakpointMarkerAtLine(uint32 line);
|
|
|
|
private:
|
|
void _InvalidateIPMarkers();
|
|
void _InvalidateBreakpointMarkers();
|
|
void _UpdateIPMarkers();
|
|
void _UpdateBreakpointMarkers();
|
|
|
|
// TODO: "public" to workaround a GCC2 problem:
|
|
public:
|
|
static int _CompareMarkers(const Marker* a,
|
|
const Marker* b);
|
|
static int _CompareBreakpointMarkers(
|
|
const BreakpointMarker* a,
|
|
const BreakpointMarker* b);
|
|
|
|
template<typename MarkerType>
|
|
static int _CompareLineMarkerTemplate(const uint32* line,
|
|
const MarkerType* marker);
|
|
static int _CompareLineMarker(const uint32* line,
|
|
const Marker* marker);
|
|
static int _CompareLineBreakpointMarker(
|
|
const uint32* line,
|
|
const BreakpointMarker* marker);
|
|
|
|
private:
|
|
Team* fTeam;
|
|
Listener* fListener;
|
|
SourceCode* fSourceCode;
|
|
StackTrace* fStackTrace;
|
|
StackFrame* fStackFrame;
|
|
MarkerList fIPMarkers;
|
|
BreakpointMarkerList fBreakpointMarkers;
|
|
bool fIPMarkersValid;
|
|
bool fBreakpointMarkersValid;
|
|
|
|
};
|
|
|
|
|
|
class SourceView::MarkerView : public BaseView {
|
|
public:
|
|
MarkerView(SourceView* sourceView, Team* team,
|
|
Listener* listener, MarkerManager *manager,
|
|
FontInfo* fontInfo);
|
|
~MarkerView();
|
|
|
|
virtual void SetSourceCode(SourceCode* sourceCode);
|
|
|
|
void SetStackTrace(StackTrace* stackTrace);
|
|
void SetStackFrame(StackFrame* stackFrame);
|
|
|
|
void UserBreakpointChanged(
|
|
UserBreakpoint* breakpoint);
|
|
|
|
virtual BSize MinSize();
|
|
virtual BSize MaxSize();
|
|
|
|
virtual void Draw(BRect updateRect);
|
|
|
|
virtual void MouseDown(BPoint where);
|
|
|
|
protected:
|
|
virtual bool GetToolTipAt(BPoint point, BToolTip** _tip);
|
|
|
|
private:
|
|
Team* fTeam;
|
|
Listener* fListener;
|
|
MarkerManager* fMarkerManager;
|
|
StackTrace* fStackTrace;
|
|
StackFrame* fStackFrame;
|
|
rgb_color fBackgroundColor;
|
|
rgb_color fBreakpointOptionMarker;
|
|
};
|
|
|
|
|
|
struct SourceView::MarkerManager::Marker {
|
|
Marker(uint32 line);
|
|
virtual ~Marker();
|
|
|
|
inline uint32 Line() const;
|
|
|
|
virtual void Draw(BView* view, BRect rect) = 0;
|
|
|
|
private:
|
|
uint32 fLine;
|
|
};
|
|
|
|
|
|
struct SourceView::MarkerManager::InstructionPointerMarker : Marker {
|
|
InstructionPointerMarker(uint32 line,
|
|
bool topIP, bool currentIP);
|
|
|
|
virtual void Draw(BView* view, BRect rect);
|
|
|
|
bool IsCurrentIP() const { return fIsCurrentIP; }
|
|
|
|
private:
|
|
void _DrawArrow(BView* view, BPoint tip, BSize size,
|
|
BSize base, const rgb_color& color,
|
|
bool fill);
|
|
|
|
private:
|
|
bool fIsTopIP;
|
|
bool fIsCurrentIP;
|
|
};
|
|
|
|
|
|
struct SourceView::MarkerManager::BreakpointMarker : Marker {
|
|
BreakpointMarker(uint32 line,
|
|
target_addr_t address,
|
|
UserBreakpoint* breakpoint);
|
|
~BreakpointMarker();
|
|
|
|
target_addr_t Address() const { return fAddress; }
|
|
bool IsEnabled() const
|
|
{ return fBreakpoint->IsEnabled(); }
|
|
bool HasCondition() const
|
|
{ return fBreakpoint->HasCondition(); }
|
|
UserBreakpoint* Breakpoint() const
|
|
{ return fBreakpoint; }
|
|
|
|
virtual void Draw(BView* view, BRect rect);
|
|
|
|
private:
|
|
target_addr_t fAddress;
|
|
UserBreakpoint* fBreakpoint;
|
|
};
|
|
|
|
|
|
template<typename MarkerType>
|
|
struct SourceView::MarkerManager::MarkerByLinePredicate
|
|
: UnaryPredicate<MarkerType> {
|
|
MarkerByLinePredicate(uint32 line)
|
|
:
|
|
fLine(line)
|
|
{
|
|
}
|
|
|
|
virtual int operator()(const MarkerType* marker) const
|
|
{
|
|
return -_CompareLineMarkerTemplate<MarkerType>(&fLine, marker);
|
|
}
|
|
|
|
private:
|
|
uint32 fLine;
|
|
};
|
|
|
|
|
|
class SourceView::TextView : public BaseView {
|
|
public:
|
|
TextView(SourceView* sourceView,
|
|
MarkerManager* manager,
|
|
FontInfo* fontInfo);
|
|
|
|
virtual void SetSourceCode(SourceCode* sourceCode);
|
|
void UserBreakpointChanged(
|
|
UserBreakpoint* breakpoint);
|
|
|
|
virtual BSize MinSize();
|
|
virtual BSize MaxSize();
|
|
|
|
virtual void Draw(BRect updateRect);
|
|
|
|
virtual void KeyDown(const char* bytes, int32 numBytes);
|
|
virtual void MakeFocus(bool isFocused);
|
|
virtual void MessageReceived(BMessage* message);
|
|
virtual void MouseDown(BPoint where);
|
|
virtual void MouseMoved(BPoint where, uint32 transit,
|
|
const BMessage* dragMessage);
|
|
virtual void MouseUp(BPoint where);
|
|
|
|
private:
|
|
struct SelectionPoint
|
|
{
|
|
SelectionPoint(int32 _line, int32 _offset)
|
|
{
|
|
line = _line;
|
|
offset = _offset;
|
|
}
|
|
|
|
bool operator==(const SelectionPoint& other)
|
|
{
|
|
return line == other.line && offset == other.offset;
|
|
}
|
|
|
|
int32 line;
|
|
int32 offset;
|
|
};
|
|
|
|
enum TrackingState
|
|
{
|
|
kNotTracking = 0,
|
|
kTracking = 1,
|
|
kDragging = 2
|
|
};
|
|
|
|
float _MaxLineWidth();
|
|
inline float _FormattedLineWidth(const char* line) const;
|
|
|
|
void _DrawLineSyntaxSection(const char* line,
|
|
int32 length, int32& _column,
|
|
BPoint& _offset);
|
|
inline void _DrawLineSegment(const char* line,
|
|
int32 length, BPoint& _offset);
|
|
inline int32 _NextTabStop(int32 column) const;
|
|
float _FormattedPosition(int32 line,
|
|
int32 offset) const;
|
|
SelectionPoint _SelectionPointAt(BPoint where) const;
|
|
void _GetSelectionRegion(BRegion& region) const;
|
|
void _GetSelectionText(BString& text) const;
|
|
void _CopySelectionToClipboard() const;
|
|
void _SelectWordAt(const SelectionPoint& point,
|
|
bool extend = false);
|
|
void _SelectLineAt(const SelectionPoint& point,
|
|
bool extend = false);
|
|
void _HandleAutoScroll();
|
|
void _ScrollHorizontal(int32 charCount);
|
|
void _ScrollByLines(int32 lineCount);
|
|
void _ScrollByPages(int32 pageCount);
|
|
void _ScrollToTop();
|
|
void _ScrollToBottom();
|
|
|
|
bool _AddGeneralActions(BPopUpMenu* menu,
|
|
int32 line);
|
|
bool _AddFlowControlActions(BPopUpMenu* menu,
|
|
int32 line);
|
|
|
|
bool _AddGeneralActionItem(BPopUpMenu* menu,
|
|
const char* text, BMessage* message) const;
|
|
// takes ownership of message
|
|
// regardless of outcome
|
|
bool _AddFlowControlActionItem(BPopUpMenu* menu,
|
|
const char* text, uint32 what,
|
|
target_addr_t address) const;
|
|
|
|
private:
|
|
|
|
float fMaxLineWidth;
|
|
float fCharacterWidth;
|
|
SelectionPoint fSelectionStart;
|
|
SelectionPoint fSelectionEnd;
|
|
SelectionPoint fSelectionBase;
|
|
SelectionPoint fLastClickPoint;
|
|
bigtime_t fLastClickTime;
|
|
int16 fClickCount;
|
|
rgb_color fTextColor;
|
|
bool fSelectionMode;
|
|
TrackingState fTrackState;
|
|
BMessageRunner* fScrollRunner;
|
|
MarkerManager* fMarkerManager;
|
|
};
|
|
|
|
|
|
// #pragma mark - BaseView
|
|
|
|
|
|
SourceView::BaseView::BaseView(const char* name, SourceView* sourceView,
|
|
FontInfo* fontInfo)
|
|
:
|
|
BView(name, B_WILL_DRAW | B_SUBPIXEL_PRECISE),
|
|
fSourceView(sourceView),
|
|
fFontInfo(fontInfo),
|
|
fSourceCode(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::BaseView::SetSourceCode(SourceCode* sourceCode)
|
|
{
|
|
fSourceCode = sourceCode;
|
|
|
|
InvalidateLayout();
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::BaseView::PreferredSize()
|
|
{
|
|
return MinSize();
|
|
}
|
|
|
|
|
|
int32
|
|
SourceView::BaseView::LineCount() const
|
|
{
|
|
return fSourceCode != NULL ? fSourceCode->CountLines() : 0;
|
|
}
|
|
|
|
|
|
float
|
|
SourceView::BaseView::TotalHeight() const
|
|
{
|
|
float height = LineCount() * fFontInfo->lineHeight - 1;
|
|
return std::max(height, kMinViewHeight);
|
|
}
|
|
|
|
|
|
int32
|
|
SourceView::BaseView::LineAtOffset(float yOffset) const
|
|
{
|
|
int32 lineCount = LineCount();
|
|
if (yOffset < 0 || lineCount == 0)
|
|
return -1;
|
|
|
|
int32 line = (int32)yOffset / (int32)fFontInfo->lineHeight;
|
|
return line < lineCount ? line : -1;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::BaseView::GetLineRange(BRect rect, int32& minLine,
|
|
int32& maxLine) const
|
|
{
|
|
int32 lineHeight = (int32)fFontInfo->lineHeight;
|
|
minLine = (int32)rect.top / lineHeight;
|
|
maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight;
|
|
minLine = std::max(minLine, (int32)0);
|
|
maxLine = std::min(maxLine, fSourceCode->CountLines() - 1);
|
|
}
|
|
|
|
|
|
BRect
|
|
SourceView::BaseView::LineRect(uint32 line) const
|
|
{
|
|
float y = (float)line * fFontInfo->lineHeight;
|
|
return BRect(0, y, Bounds().right, y + fFontInfo->lineHeight - 1);
|
|
}
|
|
|
|
|
|
// #pragma mark - MarkerView::Marker
|
|
|
|
|
|
SourceView::MarkerManager::Marker::Marker(uint32 line)
|
|
:
|
|
fLine(line)
|
|
{
|
|
}
|
|
|
|
|
|
SourceView::MarkerManager::Marker::~Marker()
|
|
{
|
|
}
|
|
|
|
|
|
uint32
|
|
SourceView::MarkerManager::Marker::Line() const
|
|
{
|
|
return fLine;
|
|
}
|
|
|
|
|
|
// #pragma mark - MarkerManager::InstructionPointerMarker
|
|
|
|
|
|
SourceView::MarkerManager::InstructionPointerMarker::InstructionPointerMarker(
|
|
uint32 line, bool topIP, bool currentIP)
|
|
:
|
|
Marker(line),
|
|
fIsTopIP(topIP),
|
|
fIsCurrentIP(currentIP)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::InstructionPointerMarker::Draw(BView* view,
|
|
BRect rect)
|
|
{
|
|
// Get the arrow color -- for the top IP, if current, we use blue,
|
|
// otherwise a gray.
|
|
rgb_color color;
|
|
if (fIsCurrentIP && fIsTopIP) {
|
|
color.set_to(0, 0, 255, 255);
|
|
} else {
|
|
color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
B_DARKEN_3_TINT);
|
|
}
|
|
|
|
// Draw a filled array for the current IP, otherwise just an
|
|
// outline.
|
|
BPoint tip(rect.right - 3.5f, floorf((rect.top + rect.bottom) / 2));
|
|
if (fIsCurrentIP) {
|
|
_DrawArrow(view, tip, BSize(10, 10), BSize(5, 5), color, true);
|
|
} else {
|
|
_DrawArrow(view, tip + BPoint(-0.5f, 0), BSize(9, 8),
|
|
BSize(5, 4), color, false);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::InstructionPointerMarker::_DrawArrow(BView* view,
|
|
BPoint tip, BSize size, BSize base, const rgb_color& color, bool fill)
|
|
{
|
|
view->SetHighColor(color);
|
|
|
|
float baseTop = tip.y - base.height / 2;
|
|
float baseBottom = tip.y + base.height / 2;
|
|
float top = tip.y - size.height / 2;
|
|
float bottom = tip.y + size.height / 2;
|
|
float left = tip.x - size.width;
|
|
float middle = left + base.width;
|
|
|
|
BPoint points[7];
|
|
points[0].Set(tip.x, tip.y);
|
|
points[1].Set(middle, top);
|
|
points[2].Set(middle, baseTop);
|
|
points[3].Set(left, baseTop);
|
|
points[4].Set(left, baseBottom);
|
|
points[5].Set(middle, baseBottom);
|
|
points[6].Set(middle, bottom);
|
|
|
|
if (fill)
|
|
view->FillPolygon(points, 7);
|
|
else
|
|
view->StrokePolygon(points, 7);
|
|
}
|
|
|
|
|
|
// #pragma mark - MarkerManager::BreakpointMarker
|
|
|
|
|
|
SourceView::MarkerManager::BreakpointMarker::BreakpointMarker(uint32 line,
|
|
target_addr_t address, UserBreakpoint* breakpoint)
|
|
:
|
|
Marker(line),
|
|
fAddress(address),
|
|
fBreakpoint(breakpoint)
|
|
{
|
|
fBreakpoint->AcquireReference();
|
|
}
|
|
|
|
|
|
SourceView::MarkerManager::BreakpointMarker::~BreakpointMarker()
|
|
{
|
|
fBreakpoint->ReleaseReference();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::BreakpointMarker::Draw(BView* view, BRect rect)
|
|
{
|
|
float y = (rect.top + rect.bottom) / 2;
|
|
if (fBreakpoint->HasCondition())
|
|
view->SetHighColor((rgb_color){0, 192, 0, 255});
|
|
else
|
|
view->SetHighColor((rgb_color){255,0,0,255});
|
|
|
|
if (fBreakpoint->IsEnabled())
|
|
view->FillEllipse(BPoint(rect.right - 8, y), 4, 4);
|
|
else
|
|
view->StrokeEllipse(BPoint(rect.right - 8, y), 3.5f, 3.5f);
|
|
}
|
|
|
|
|
|
// #pragma mark - MarkerManager
|
|
|
|
|
|
SourceView::MarkerManager::MarkerManager(SourceView* sourceView, Team* team,
|
|
Listener* listener)
|
|
:
|
|
fTeam(team),
|
|
fListener(listener),
|
|
fStackTrace(NULL),
|
|
fStackFrame(NULL),
|
|
fIPMarkers(10, true),
|
|
fBreakpointMarkers(20, true),
|
|
fIPMarkersValid(false),
|
|
fBreakpointMarkersValid(false)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::SetSourceCode(SourceCode* sourceCode)
|
|
{
|
|
fSourceCode = sourceCode;
|
|
_InvalidateIPMarkers();
|
|
_InvalidateBreakpointMarkers();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::SetStackTrace(StackTrace* stackTrace)
|
|
{
|
|
fStackTrace = stackTrace;
|
|
_InvalidateIPMarkers();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::SetStackFrame(StackFrame* stackFrame)
|
|
{
|
|
fStackFrame = stackFrame;
|
|
_InvalidateIPMarkers();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::UserBreakpointChanged(UserBreakpoint* breakpoint)
|
|
{
|
|
_InvalidateBreakpointMarkers();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::_InvalidateIPMarkers()
|
|
{
|
|
fIPMarkersValid = false;
|
|
fIPMarkers.MakeEmpty();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::_InvalidateBreakpointMarkers()
|
|
{
|
|
fBreakpointMarkersValid = false;
|
|
fBreakpointMarkers.MakeEmpty();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::_UpdateIPMarkers()
|
|
{
|
|
if (fIPMarkersValid)
|
|
return;
|
|
|
|
fIPMarkers.MakeEmpty();
|
|
|
|
if (fSourceCode != NULL && fStackTrace != NULL) {
|
|
LocatableFile* sourceFile = fSourceCode->GetSourceFile();
|
|
|
|
AutoLocker<Team> locker(fTeam);
|
|
|
|
for (int32 i = 0; StackFrame* frame = fStackTrace->FrameAt(i);
|
|
i++) {
|
|
target_addr_t ip = frame->InstructionPointer();
|
|
FunctionInstance* functionInstance;
|
|
Statement* statement;
|
|
if (fTeam->GetStatementAtAddress(ip,
|
|
functionInstance, statement) != B_OK) {
|
|
continue;
|
|
}
|
|
BReference<Statement> statementReference(statement, true);
|
|
|
|
int32 line = statement->StartSourceLocation().Line();
|
|
if (line < 0 || line >= fSourceCode->CountLines())
|
|
continue;
|
|
|
|
if (sourceFile != NULL) {
|
|
if (functionInstance->GetFunction()->SourceFile() != sourceFile)
|
|
continue;
|
|
} else {
|
|
if (functionInstance->GetSourceCode() != fSourceCode)
|
|
continue;
|
|
}
|
|
|
|
bool isTopFrame = i == 0
|
|
&& frame->Type() != STACK_FRAME_TYPE_SYSCALL;
|
|
|
|
Marker* marker = new(std::nothrow) InstructionPointerMarker(
|
|
line, isTopFrame, frame == fStackFrame);
|
|
if (marker == NULL || !fIPMarkers.AddItem(marker)) {
|
|
delete marker;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// sort by line
|
|
fIPMarkers.SortItems(&_CompareMarkers);
|
|
|
|
// TODO: Filter duplicate IP markers (recursive functions)!
|
|
}
|
|
|
|
fIPMarkersValid = true;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::_UpdateBreakpointMarkers()
|
|
{
|
|
if (fBreakpointMarkersValid)
|
|
return;
|
|
|
|
fBreakpointMarkers.MakeEmpty();
|
|
|
|
if (fSourceCode != NULL) {
|
|
LocatableFile* sourceFile = fSourceCode->GetSourceFile();
|
|
|
|
AutoLocker<Team> locker(fTeam);
|
|
|
|
// get the breakpoints in our source code range
|
|
BObjectList<UserBreakpoint> breakpoints;
|
|
fTeam->GetBreakpointsForSourceCode(fSourceCode, breakpoints);
|
|
|
|
for (int32 i = 0; UserBreakpoint* breakpoint = breakpoints.ItemAt(i);
|
|
i++) {
|
|
if (breakpoint->IsHidden())
|
|
continue;
|
|
UserBreakpointInstance* breakpointInstance
|
|
= breakpoint->InstanceAt(0);
|
|
FunctionInstance* functionInstance;
|
|
Statement* statement;
|
|
if (fTeam->GetStatementAtAddress(
|
|
breakpointInstance->Address(), functionInstance,
|
|
statement) != B_OK) {
|
|
continue;
|
|
}
|
|
BReference<Statement> statementReference(statement, true);
|
|
|
|
int32 line = statement->StartSourceLocation().Line();
|
|
if (line < 0 || line >= fSourceCode->CountLines())
|
|
continue;
|
|
|
|
if (sourceFile != NULL) {
|
|
if (functionInstance->GetFunction()->SourceFile() != sourceFile)
|
|
continue;
|
|
} else {
|
|
if (functionInstance->GetSourceCode() != fSourceCode)
|
|
continue;
|
|
}
|
|
|
|
BreakpointMarker* marker = new(std::nothrow) BreakpointMarker(
|
|
line, breakpointInstance->Address(), breakpoint);
|
|
if (marker == NULL || !fBreakpointMarkers.AddItem(marker)) {
|
|
delete marker;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// sort by line
|
|
fBreakpointMarkers.SortItems(&_CompareBreakpointMarkers);
|
|
}
|
|
|
|
fBreakpointMarkersValid = true;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerManager::GetMarkers(uint32 minLine, uint32 maxLine,
|
|
MarkerList& markers)
|
|
{
|
|
_UpdateIPMarkers();
|
|
_UpdateBreakpointMarkers();
|
|
|
|
int32 ipIndex = fIPMarkers.FindBinaryInsertionIndex(
|
|
MarkerByLinePredicate<Marker>(minLine));
|
|
int32 breakpointIndex = fBreakpointMarkers.FindBinaryInsertionIndex(
|
|
MarkerByLinePredicate<BreakpointMarker>(minLine));
|
|
|
|
Marker* ipMarker = fIPMarkers.ItemAt(ipIndex);
|
|
Marker* breakpointMarker = fBreakpointMarkers.ItemAt(breakpointIndex);
|
|
|
|
while (ipMarker != NULL && breakpointMarker != NULL
|
|
&& ipMarker->Line() <= maxLine && breakpointMarker->Line() <= maxLine) {
|
|
if (breakpointMarker->Line() <= ipMarker->Line()) {
|
|
markers.AddItem(breakpointMarker);
|
|
breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex);
|
|
} else {
|
|
markers.AddItem(ipMarker);
|
|
ipMarker = fIPMarkers.ItemAt(++ipIndex);
|
|
}
|
|
}
|
|
|
|
while (breakpointMarker != NULL && breakpointMarker->Line() <= maxLine) {
|
|
markers.AddItem(breakpointMarker);
|
|
breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex);
|
|
}
|
|
|
|
while (ipMarker != NULL && ipMarker->Line() <= maxLine) {
|
|
markers.AddItem(ipMarker);
|
|
ipMarker = fIPMarkers.ItemAt(++ipIndex);
|
|
}
|
|
}
|
|
|
|
|
|
SourceView::MarkerManager::BreakpointMarker*
|
|
SourceView::MarkerManager::BreakpointMarkerAtLine(uint32 line)
|
|
{
|
|
return fBreakpointMarkers.BinarySearchByKey(line,
|
|
&_CompareLineBreakpointMarker);
|
|
}
|
|
|
|
|
|
/*static*/ int
|
|
SourceView::MarkerManager::_CompareMarkers(const Marker* a,
|
|
const Marker* b)
|
|
{
|
|
if (a->Line() < b->Line())
|
|
return -1;
|
|
return a->Line() == b->Line() ? 0 : 1;
|
|
}
|
|
|
|
|
|
/*static*/ int
|
|
SourceView::MarkerManager::_CompareBreakpointMarkers(const BreakpointMarker* a,
|
|
const BreakpointMarker* b)
|
|
{
|
|
if (a->Line() < b->Line())
|
|
return -1;
|
|
return a->Line() == b->Line() ? 0 : 1;
|
|
}
|
|
|
|
|
|
template<typename MarkerType>
|
|
/*static*/ int
|
|
SourceView::MarkerManager::_CompareLineMarkerTemplate(const uint32* line,
|
|
const MarkerType* marker)
|
|
{
|
|
if (*line < marker->Line())
|
|
return -1;
|
|
return *line == marker->Line() ? 0 : 1;
|
|
}
|
|
|
|
|
|
/*static*/ int
|
|
SourceView::MarkerManager::_CompareLineMarker(const uint32* line,
|
|
const Marker* marker)
|
|
{
|
|
return _CompareLineMarkerTemplate<Marker>(line, marker);
|
|
}
|
|
|
|
|
|
/*static*/ int
|
|
SourceView::MarkerManager::_CompareLineBreakpointMarker(const uint32* line,
|
|
const BreakpointMarker* marker)
|
|
{
|
|
return _CompareLineMarkerTemplate<BreakpointMarker>(line, marker);
|
|
}
|
|
|
|
|
|
// #pragma mark - MarkerView
|
|
|
|
|
|
SourceView::MarkerView::MarkerView(SourceView* sourceView, Team* team,
|
|
Listener* listener, MarkerManager* manager, FontInfo* fontInfo)
|
|
:
|
|
BaseView("source marker view", sourceView, fontInfo),
|
|
fTeam(team),
|
|
fListener(listener),
|
|
fMarkerManager(manager),
|
|
fStackTrace(NULL),
|
|
fStackFrame(NULL)
|
|
{
|
|
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
|
|
fBreakpointOptionMarker = tint_color(background, B_DARKEN_1_TINT);
|
|
fBackgroundColor = tint_color(background, B_LIGHTEN_2_TINT);
|
|
SetViewColor(B_TRANSPARENT_COLOR);
|
|
}
|
|
|
|
|
|
SourceView::MarkerView::~MarkerView()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerView::SetSourceCode(SourceCode* sourceCode)
|
|
{
|
|
BaseView::SetSourceCode(sourceCode);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerView::SetStackTrace(StackTrace* stackTrace)
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerView::SetStackFrame(StackFrame* stackFrame)
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerView::UserBreakpointChanged(UserBreakpoint* breakpoint)
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::MarkerView::MinSize()
|
|
{
|
|
return BSize(40, TotalHeight());
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::MarkerView::MaxSize()
|
|
{
|
|
return BSize(MinSize().width, B_SIZE_UNLIMITED);
|
|
}
|
|
|
|
void
|
|
SourceView::MarkerView::Draw(BRect updateRect)
|
|
{
|
|
SetLowColor(fBackgroundColor);
|
|
if (fSourceCode == NULL) {
|
|
FillRect(updateRect, B_SOLID_LOW);
|
|
return;
|
|
}
|
|
|
|
// get the lines intersecting with the update rect
|
|
int32 minLine, maxLine;
|
|
GetLineRange(updateRect, minLine, maxLine);
|
|
if (minLine <= maxLine) {
|
|
// get the markers in that range
|
|
SourceView::MarkerManager::MarkerList markers;
|
|
fMarkerManager->GetMarkers(minLine, maxLine, markers);
|
|
|
|
float width = Bounds().Width();
|
|
|
|
AutoLocker<SourceCode> sourceLocker(fSourceCode);
|
|
|
|
int32 markerIndex = 0;
|
|
for (int32 line = minLine; line <= maxLine; line++) {
|
|
bool drawBreakpointOptionMarker = true;
|
|
|
|
SourceView::MarkerManager::Marker* marker;
|
|
FillRect(LineRect(line), B_SOLID_LOW);
|
|
while ((marker = markers.ItemAt(markerIndex)) != NULL
|
|
&& marker->Line() == (uint32)line) {
|
|
marker->Draw(this, LineRect(line));
|
|
drawBreakpointOptionMarker = false;
|
|
markerIndex++;
|
|
}
|
|
|
|
if (!drawBreakpointOptionMarker)
|
|
continue;
|
|
|
|
SourceLocation statementStart, statementEnd;
|
|
if (!fSourceCode->GetStatementLocationRange(SourceLocation(line),
|
|
statementStart, statementEnd)
|
|
|| statementStart.Line() != line) {
|
|
continue;
|
|
}
|
|
|
|
float y = ((float)line + 0.5f) * fFontInfo->lineHeight;
|
|
SetHighColor(fBreakpointOptionMarker);
|
|
FillEllipse(BPoint(width - 8, y), 2, 2);
|
|
}
|
|
}
|
|
|
|
float y = (maxLine + 1) * fFontInfo->lineHeight;
|
|
if (y < updateRect.bottom) {
|
|
FillRect(BRect(0.0, y, Bounds().right, updateRect.bottom),
|
|
B_SOLID_LOW);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MarkerView::MouseDown(BPoint where)
|
|
{
|
|
if (fSourceCode == NULL)
|
|
return;
|
|
|
|
int32 line = LineAtOffset(where.y);
|
|
|
|
Statement* statement;
|
|
if (!fSourceView->GetStatementForLine(line, statement))
|
|
return;
|
|
BReference<Statement> statementReference(statement, true);
|
|
|
|
int32 modifiers;
|
|
int32 buttons;
|
|
BMessage* message = Looper()->CurrentMessage();
|
|
if (message->FindInt32("modifiers", &modifiers) != B_OK)
|
|
modifiers = 0;
|
|
if (message->FindInt32("buttons", &buttons) != B_OK)
|
|
buttons = B_PRIMARY_MOUSE_BUTTON;
|
|
|
|
SourceView::MarkerManager::BreakpointMarker* marker =
|
|
fMarkerManager->BreakpointMarkerAtLine(line);
|
|
target_addr_t address = marker != NULL
|
|
? marker->Address() : statement->CoveringAddressRange().Start();
|
|
|
|
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
|
|
if ((modifiers & B_SHIFT_KEY) != 0) {
|
|
if (marker != NULL && !marker->IsEnabled())
|
|
fListener->ClearBreakpointRequested(address);
|
|
else
|
|
fListener->SetBreakpointRequested(address, false);
|
|
} else {
|
|
if (marker != NULL && marker->IsEnabled())
|
|
fListener->ClearBreakpointRequested(address);
|
|
else
|
|
fListener->SetBreakpointRequested(address, true);
|
|
}
|
|
} else if (marker != NULL && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
|
|
UserBreakpoint* breakpoint = marker->Breakpoint();
|
|
BMessage message(MSG_SHOW_BREAKPOINT_EDIT_WINDOW);
|
|
message.AddPointer("breakpoint", breakpoint);
|
|
Looper()->PostMessage(&message);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::MarkerView::GetToolTipAt(BPoint point, BToolTip** _tip)
|
|
{
|
|
if (fSourceCode == NULL)
|
|
return false;
|
|
|
|
int32 line = LineAtOffset(point.y);
|
|
if (line < 0)
|
|
return false;
|
|
|
|
AutoLocker<Team> locker(fTeam);
|
|
Statement* statement;
|
|
if (fTeam->GetStatementAtSourceLocation(fSourceCode,
|
|
SourceLocation(line), statement) != B_OK) {
|
|
return false;
|
|
}
|
|
BReference<Statement> statementReference(statement, true);
|
|
if (statement->StartSourceLocation().Line() != line)
|
|
return false;
|
|
|
|
SourceView::MarkerManager::BreakpointMarker* marker =
|
|
fMarkerManager->BreakpointMarkerAtLine(line);
|
|
|
|
BString text;
|
|
if (marker == NULL) {
|
|
text.SetToFormat(kEnableBreakpointMessage, line);
|
|
} else if ((modifiers() & B_SHIFT_KEY) != 0) {
|
|
if (!marker->IsEnabled())
|
|
text.SetToFormat(kClearBreakpointMessage, line);
|
|
else
|
|
text.SetToFormat(kDisableBreakpointMessage, line);
|
|
} else {
|
|
if (marker->IsEnabled())
|
|
text.SetToFormat(kClearBreakpointMessage, line);
|
|
else
|
|
text.SetToFormat(kEnableBreakpointMessage, line);
|
|
}
|
|
|
|
if (text.Length() > 0) {
|
|
BTextToolTip* tip = new(std::nothrow) BTextToolTip(text);
|
|
if (tip == NULL)
|
|
return false;
|
|
|
|
*_tip = tip;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// #pragma mark - TextView
|
|
|
|
|
|
SourceView::TextView::TextView(SourceView* sourceView, MarkerManager* manager,
|
|
FontInfo* fontInfo)
|
|
:
|
|
BaseView("source text view", sourceView, fontInfo),
|
|
fMaxLineWidth(-1),
|
|
fCharacterWidth(fontInfo->font.StringWidth("Q")),
|
|
fSelectionStart(-1, -1),
|
|
fSelectionEnd(-1, -1),
|
|
fSelectionBase(-1, -1),
|
|
fLastClickPoint(-1, -1),
|
|
fLastClickTime(0),
|
|
fClickCount(0),
|
|
fSelectionMode(false),
|
|
fTrackState(kNotTracking),
|
|
fScrollRunner(NULL),
|
|
fMarkerManager(manager)
|
|
{
|
|
SetViewColor(B_TRANSPARENT_COLOR);
|
|
fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR);
|
|
SetFlags(Flags() | B_NAVIGABLE);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::SetSourceCode(SourceCode* sourceCode)
|
|
{
|
|
fMaxLineWidth = -1;
|
|
fSelectionStart = fSelectionBase = fSelectionEnd = SelectionPoint(-1, -1);
|
|
fClickCount = 0;
|
|
BaseView::SetSourceCode(sourceCode);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::UserBreakpointChanged(UserBreakpoint* breakpoint)
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::TextView::MinSize()
|
|
{
|
|
return BSize(kLeftTextMargin + _MaxLineWidth() - 1, TotalHeight());
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::TextView::MaxSize()
|
|
{
|
|
return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::Draw(BRect updateRect)
|
|
{
|
|
if (fSourceCode == NULL) {
|
|
SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
|
|
FillRect(updateRect, B_SOLID_LOW);
|
|
return;
|
|
}
|
|
|
|
// get the lines intersecting with the update rect
|
|
int32 minLine, maxLine;
|
|
GetLineRange(updateRect, minLine, maxLine);
|
|
SourceView::MarkerManager::MarkerList markers;
|
|
fMarkerManager->GetMarkers(minLine, maxLine, markers);
|
|
|
|
// draw the affected lines
|
|
SetHighColor(fTextColor);
|
|
SetFont(&fFontInfo->font);
|
|
SourceView::MarkerManager::Marker* marker;
|
|
SourceView::MarkerManager::InstructionPointerMarker* ipMarker;
|
|
int32 markerIndex = 0;
|
|
float y;
|
|
|
|
// syntax line data
|
|
int32 columns[kMaxHighlightsPerLine];
|
|
syntax_highlight_type types[kMaxHighlightsPerLine];
|
|
SyntaxHighlightInfo* info = fSourceView->fCurrentSyntaxInfo;
|
|
|
|
for (int32 i = minLine; i <= maxLine; i++) {
|
|
int32 syntaxCount = 0;
|
|
if (info != NULL) {
|
|
syntaxCount = info->GetLineHighlightRanges(i, columns, types,
|
|
kMaxHighlightsPerLine);
|
|
}
|
|
|
|
SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
|
|
y = i * fFontInfo->lineHeight;
|
|
|
|
FillRect(BRect(0.0, y, kLeftTextMargin, y + fFontInfo->lineHeight),
|
|
B_SOLID_LOW);
|
|
for (int32 j = markerIndex; j < markers.CountItems(); j++) {
|
|
marker = markers.ItemAt(j);
|
|
if (marker->Line() < (uint32)i) {
|
|
++markerIndex;
|
|
continue;
|
|
} else if (marker->Line() == (uint32)i) {
|
|
++markerIndex;
|
|
ipMarker = dynamic_cast<SourceView::MarkerManager
|
|
::InstructionPointerMarker*>(marker);
|
|
if (ipMarker != NULL) {
|
|
if (ipMarker->IsCurrentIP())
|
|
SetLowColor(96, 216, 216, 255);
|
|
else
|
|
SetLowColor(216, 216, 216, 255);
|
|
|
|
} else
|
|
SetLowColor(255, 255, 0, 255);
|
|
break;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
FillRect(BRect(kLeftTextMargin, y, Bounds().right,
|
|
y + fFontInfo->lineHeight - 1), B_SOLID_LOW);
|
|
|
|
syntax_highlight_type currentHighlight = SYNTAX_HIGHLIGHT_NONE;
|
|
SetHighColor(kSyntaxColors[currentHighlight]);
|
|
const char* lineData = fSourceCode->LineAt(i);
|
|
int32 lineLength = fSourceCode->LineLengthAt(i);
|
|
BPoint linePoint(kLeftTextMargin, y + fFontInfo->fontHeight.ascent);
|
|
int32 lineOffset = 0;
|
|
int32 currentColumn = 0;
|
|
for (int32 j = 0; j < syntaxCount; j++) {
|
|
int32 length = columns[j] - lineOffset;
|
|
if (length != 0) {
|
|
_DrawLineSyntaxSection(lineData + lineOffset, length,
|
|
currentColumn, linePoint);
|
|
lineOffset += length;
|
|
}
|
|
currentHighlight = types[j];
|
|
SetHighColor(kSyntaxColors[currentHighlight]);
|
|
}
|
|
|
|
// draw remainder, if any.
|
|
if (lineOffset < lineLength) {
|
|
_DrawLineSyntaxSection(lineData + lineOffset,
|
|
lineLength - lineOffset, currentColumn, linePoint);
|
|
}
|
|
}
|
|
|
|
y = (maxLine + 1) * fFontInfo->lineHeight;
|
|
if (y < updateRect.bottom) {
|
|
SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
|
|
FillRect(BRect(0.0, y, Bounds().right, updateRect.bottom),
|
|
B_SOLID_LOW);
|
|
}
|
|
|
|
if (fSelectionStart.line != -1 && fSelectionEnd.line != -1) {
|
|
PushState();
|
|
BRegion selectionRegion;
|
|
_GetSelectionRegion(selectionRegion);
|
|
SetDrawingMode(B_OP_INVERT);
|
|
FillRegion(&selectionRegion, B_SOLID_HIGH);
|
|
PopState();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::KeyDown(const char* bytes, int32 numBytes)
|
|
{
|
|
switch(bytes[0]) {
|
|
case B_UP_ARROW:
|
|
_ScrollByLines(-1);
|
|
break;
|
|
|
|
case B_DOWN_ARROW:
|
|
_ScrollByLines(1);
|
|
break;
|
|
|
|
case B_PAGE_UP:
|
|
_ScrollByPages(-1);
|
|
break;
|
|
|
|
case B_PAGE_DOWN:
|
|
_ScrollByPages(1);
|
|
break;
|
|
|
|
case B_HOME:
|
|
_ScrollToTop();
|
|
break;
|
|
|
|
case B_END:
|
|
_ScrollToBottom();
|
|
break;
|
|
}
|
|
|
|
SourceView::BaseView::KeyDown(bytes, numBytes);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::MakeFocus(bool isFocused)
|
|
{
|
|
fSourceView->HighlightBorder(isFocused);
|
|
|
|
SourceView::BaseView::MakeFocus(isFocused);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what)
|
|
{
|
|
case B_COPY:
|
|
_CopySelectionToClipboard();
|
|
break;
|
|
|
|
case B_SELECT_ALL:
|
|
fSelectionStart.line = 0;
|
|
fSelectionStart.offset = 0;
|
|
fSelectionEnd.line = fSourceCode->CountLines() - 1;
|
|
fSelectionEnd.offset = fSourceCode->LineLengthAt(
|
|
fSelectionEnd.line);
|
|
Invalidate();
|
|
break;
|
|
|
|
case MSG_TEXTVIEW_AUTOSCROLL:
|
|
_HandleAutoScroll();
|
|
break;
|
|
|
|
default:
|
|
SourceView::BaseView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::MouseDown(BPoint where)
|
|
{
|
|
if (fSourceCode == NULL)
|
|
return;
|
|
|
|
int32 buttons;
|
|
if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
|
|
buttons = B_PRIMARY_MOUSE_BUTTON;
|
|
|
|
|
|
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
|
|
if (!IsFocus())
|
|
MakeFocus(true);
|
|
fTrackState = kTracking;
|
|
|
|
// don't reset the selection if the user clicks within the
|
|
// current selection range
|
|
BRegion region;
|
|
_GetSelectionRegion(region);
|
|
bigtime_t clickTime = system_time();
|
|
SelectionPoint point = _SelectionPointAt(where);
|
|
fLastClickPoint = point;
|
|
bigtime_t clickSpeed = 0;
|
|
get_click_speed(&clickSpeed);
|
|
if (clickTime - fLastClickTime < clickSpeed
|
|
&& fSelectionBase == point) {
|
|
if (fClickCount > 3) {
|
|
fClickCount = 0;
|
|
fLastClickTime = 0;
|
|
} else {
|
|
fClickCount++;
|
|
fLastClickTime = clickTime;
|
|
}
|
|
} else {
|
|
fClickCount = 1;
|
|
fLastClickTime = clickTime;
|
|
}
|
|
|
|
if (fClickCount == 2) {
|
|
_SelectWordAt(point);
|
|
fSelectionMode = true;
|
|
} else if (fClickCount == 3) {
|
|
_SelectLineAt(point);
|
|
fSelectionMode = true;
|
|
} else if (!region.Contains(where)) {
|
|
fSelectionBase = fSelectionStart = fSelectionEnd = point;
|
|
fSelectionMode = true;
|
|
Invalidate();
|
|
SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
|
|
}
|
|
} else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
|
|
int32 line = LineAtOffset(where.y);
|
|
if (line < 0)
|
|
return;
|
|
|
|
::Team* team = fSourceView->fTeam;
|
|
AutoLocker<Team> locker(team);
|
|
::Thread* activeThread = fSourceView->fActiveThread;
|
|
|
|
if (activeThread == NULL)
|
|
return;
|
|
else if (activeThread->State() != THREAD_STATE_STOPPED)
|
|
return;
|
|
|
|
BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("");
|
|
if (menu == NULL)
|
|
return;
|
|
ObjectDeleter<BPopUpMenu> menuDeleter(menu);
|
|
|
|
if (!_AddGeneralActions(menu, line))
|
|
return;
|
|
|
|
if (!_AddFlowControlActions(menu, line))
|
|
return;
|
|
|
|
menuDeleter.Detach();
|
|
|
|
BPoint screenWhere(where);
|
|
ConvertToScreen(&screenWhere);
|
|
menu->SetTargetForItems(fSourceView);
|
|
BRect mouseRect(screenWhere, screenWhere);
|
|
mouseRect.InsetBy(-4.0, -4.0);
|
|
menu->Go(screenWhere, true, false, mouseRect, true);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::MouseMoved(BPoint where, uint32 transit,
|
|
const BMessage* dragMessage)
|
|
{
|
|
BRegion region;
|
|
if (fSelectionMode) {
|
|
BRegion oldRegion;
|
|
_GetSelectionRegion(oldRegion);
|
|
SelectionPoint point = _SelectionPointAt(where);
|
|
if (point.line < 0)
|
|
return;
|
|
|
|
switch (transit) {
|
|
case B_INSIDE_VIEW:
|
|
case B_OUTSIDE_VIEW:
|
|
if (fClickCount == 2)
|
|
_SelectWordAt(point, true);
|
|
else if (fClickCount == 3)
|
|
_SelectLineAt(point, true);
|
|
else {
|
|
if (point.line > fSelectionBase.line) {
|
|
fSelectionStart = fSelectionBase;
|
|
fSelectionEnd = point;
|
|
} else if (point.line < fSelectionBase.line) {
|
|
fSelectionEnd = fSelectionBase;
|
|
fSelectionStart = point;
|
|
} else if (point.offset > fSelectionBase.offset) {
|
|
fSelectionStart = fSelectionBase;
|
|
fSelectionEnd = point;
|
|
} else {
|
|
fSelectionEnd = fSelectionBase;
|
|
fSelectionStart = point;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case B_EXITED_VIEW:
|
|
fScrollRunner = new BMessageRunner(BMessenger(this),
|
|
new BMessage(MSG_TEXTVIEW_AUTOSCROLL), kScrollTimer);
|
|
break;
|
|
|
|
case B_ENTERED_VIEW:
|
|
delete fScrollRunner;
|
|
fScrollRunner = NULL;
|
|
break;
|
|
}
|
|
_GetSelectionRegion(region);
|
|
region.Include(&oldRegion);
|
|
Invalidate(®ion);
|
|
} else if (fTrackState == kTracking) {
|
|
_GetSelectionRegion(region);
|
|
if (region.CountRects() > 0) {
|
|
BString text;
|
|
_GetSelectionText(text);
|
|
BMessage message;
|
|
message.AddData ("text/plain", B_MIME_TYPE, text.String(),
|
|
text.Length());
|
|
BString clipName;
|
|
if (fSourceCode->GetSourceFile() != NULL)
|
|
clipName = fSourceCode->GetSourceFile()->Name();
|
|
else if (fSourceCode->GetSourceLanguage() != NULL)
|
|
clipName = fSourceCode->GetSourceLanguage()->Name();
|
|
else
|
|
clipName = "Text";
|
|
clipName << " clipping";
|
|
message.AddString ("be:clip_name", clipName.String());
|
|
message.AddInt32 ("be:actions", B_COPY_TARGET);
|
|
BRect dragRect = region.Frame();
|
|
BRect visibleRect = fSourceView->Bounds();
|
|
if (dragRect.Height() > visibleRect.Height()) {
|
|
dragRect.top = 0;
|
|
dragRect.bottom = visibleRect.Height();
|
|
}
|
|
if (dragRect.Width() > visibleRect.Width()) {
|
|
dragRect.left = 0;
|
|
dragRect.right = visibleRect.Width();
|
|
}
|
|
DragMessage(&message, dragRect);
|
|
fTrackState = kDragging;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::MouseUp(BPoint where)
|
|
{
|
|
fSelectionMode = false;
|
|
if (fTrackState == kTracking && fClickCount < 2) {
|
|
|
|
// if we clicked without dragging or double/triple clicking,
|
|
// clear the current selection (if any)
|
|
SelectionPoint point = _SelectionPointAt(where);
|
|
if (fLastClickPoint == point) {
|
|
fSelectionBase = fSelectionStart = fSelectionEnd;
|
|
Invalidate();
|
|
}
|
|
}
|
|
delete fScrollRunner;
|
|
fScrollRunner = NULL;
|
|
fTrackState = kNotTracking;
|
|
}
|
|
|
|
|
|
float
|
|
SourceView::TextView::_MaxLineWidth()
|
|
{
|
|
if (fMaxLineWidth >= 0)
|
|
return fMaxLineWidth;
|
|
|
|
fMaxLineWidth = 0;
|
|
if (fSourceCode != NULL) {
|
|
for (int32 i = 0; const char* line = fSourceCode->LineAt(i); i++)
|
|
fMaxLineWidth = std::max(fMaxLineWidth, _FormattedLineWidth(line));
|
|
}
|
|
|
|
return fMaxLineWidth;
|
|
}
|
|
|
|
|
|
float
|
|
SourceView::TextView::_FormattedLineWidth(const char* line) const
|
|
{
|
|
int32 column = 0;
|
|
int32 i = 0;
|
|
for (; line[i] != '\0'; i++) {
|
|
if (line[i] == '\t')
|
|
column = _NextTabStop(column);
|
|
else
|
|
++column;
|
|
}
|
|
|
|
return column * fCharacterWidth;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_DrawLineSyntaxSection(const char* line, int32 length,
|
|
int32& _column, BPoint& _offset)
|
|
{
|
|
int32 start = 0;
|
|
int32 currentLength = 0;
|
|
for (int32 i = 0; i < length; i++) {
|
|
if (line[i] == '\t') {
|
|
currentLength = i - start;
|
|
if (currentLength != 0)
|
|
_DrawLineSegment(line + start, currentLength, _offset);
|
|
|
|
// set new starting offset to the position after this tab
|
|
start = i + 1;
|
|
int32 nextTabStop = _NextTabStop(_column);
|
|
int32 diff = nextTabStop - _column;
|
|
_column = nextTabStop;
|
|
_offset.x += diff * fCharacterWidth;
|
|
} else
|
|
_column++;
|
|
}
|
|
|
|
// draw last segment
|
|
currentLength = length - start;
|
|
if (currentLength > 0)
|
|
_DrawLineSegment(line + start, currentLength, _offset);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_DrawLineSegment(const char* line, int32 length,
|
|
BPoint& _offset)
|
|
{
|
|
DrawString(line, length, _offset);
|
|
_offset.x += fCharacterWidth * length;
|
|
}
|
|
|
|
|
|
int32
|
|
SourceView::TextView::_NextTabStop(int32 column) const
|
|
{
|
|
return (column / kSpacesPerTab + 1) * kSpacesPerTab;
|
|
}
|
|
|
|
|
|
float
|
|
SourceView::TextView::_FormattedPosition(int32 line, int32 offset) const
|
|
{
|
|
int32 column = 0;
|
|
for (int32 i = 0; i < offset; i++) {
|
|
if (fSourceCode->LineAt(line)[i] == '\t')
|
|
column = _NextTabStop(column);
|
|
else
|
|
++column;
|
|
}
|
|
|
|
return column * fCharacterWidth;
|
|
}
|
|
|
|
|
|
SourceView::TextView::SelectionPoint
|
|
SourceView::TextView::_SelectionPointAt(BPoint where) const
|
|
{
|
|
int32 line = LineAtOffset(where.y);
|
|
int32 offset = -1;
|
|
if (line >= 0) {
|
|
int32 column = 0;
|
|
int32 lineLength = fSourceCode->LineLengthAt(line);
|
|
const char* sourceLine = fSourceCode->LineAt(line);
|
|
|
|
for (int32 i = 0; i < lineLength; i++) {
|
|
if (sourceLine[i] == '\t')
|
|
column = _NextTabStop(column);
|
|
else
|
|
++column;
|
|
|
|
if (column * fCharacterWidth > where.x) {
|
|
offset = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (offset < 0)
|
|
offset = lineLength;
|
|
}
|
|
|
|
return SelectionPoint(line, offset);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_GetSelectionRegion(BRegion ®ion) const
|
|
{
|
|
if (fSelectionStart.line == -1 && fSelectionEnd.line == -1)
|
|
return;
|
|
|
|
BRect selectionRect;
|
|
|
|
if (fSelectionStart.line == fSelectionEnd.line) {
|
|
if (fSelectionStart.offset != fSelectionEnd.offset) {
|
|
selectionRect.left = _FormattedPosition(fSelectionStart.line,
|
|
fSelectionStart.offset);
|
|
selectionRect.top = fSelectionStart.line * fFontInfo->lineHeight;
|
|
selectionRect.right = _FormattedPosition(fSelectionEnd.line,
|
|
fSelectionEnd.offset);
|
|
selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight;
|
|
region.Include(selectionRect);
|
|
}
|
|
} else {
|
|
// add rect for starting line
|
|
selectionRect.left = _FormattedPosition(fSelectionStart.line,
|
|
fSelectionStart.offset);
|
|
selectionRect.top = fSelectionStart.line * fFontInfo->lineHeight;
|
|
selectionRect.right = Bounds().right;
|
|
selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight;
|
|
region.Include(selectionRect);
|
|
|
|
// compute rect for all lines in middle of selection
|
|
if (fSelectionEnd.line - fSelectionStart.line > 1) {
|
|
selectionRect.left = 0.0;
|
|
selectionRect.top = (fSelectionStart.line + 1)
|
|
* fFontInfo->lineHeight;
|
|
selectionRect.right = Bounds().right;
|
|
selectionRect.bottom = fSelectionEnd.line * fFontInfo->lineHeight;
|
|
region.Include(selectionRect);
|
|
}
|
|
|
|
// add rect for last line (if needed)
|
|
if (fSelectionEnd.offset > 0) {
|
|
selectionRect.left = 0.0;
|
|
selectionRect.top = fSelectionEnd.line * fFontInfo->lineHeight;
|
|
selectionRect.right = _FormattedPosition(fSelectionEnd.line,
|
|
fSelectionEnd.offset);
|
|
selectionRect.bottom = selectionRect.top + fFontInfo->lineHeight;
|
|
region.Include(selectionRect);
|
|
}
|
|
}
|
|
region.OffsetBy(kLeftTextMargin, 0.0);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_GetSelectionText(BString& text) const
|
|
{
|
|
if (fSelectionStart.line == -1 || fSelectionEnd.line == -1)
|
|
return;
|
|
|
|
if (fSelectionStart.line == fSelectionEnd.line) {
|
|
text.SetTo(fSourceCode->LineAt(fSelectionStart.line)
|
|
+ fSelectionStart.offset, fSelectionEnd.offset
|
|
- fSelectionStart.offset);
|
|
} else {
|
|
text.SetTo(fSourceCode->LineAt(fSelectionStart.line)
|
|
+ fSelectionStart.offset);
|
|
text << "\n";
|
|
for (int32 i = fSelectionStart.line + 1; i < fSelectionEnd.line; i++)
|
|
text << fSourceCode->LineAt(i) << "\n";
|
|
text.Append(fSourceCode->LineAt(fSelectionEnd.line),
|
|
fSelectionEnd.offset);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_CopySelectionToClipboard(void) const
|
|
{
|
|
BString text;
|
|
_GetSelectionText(text);
|
|
|
|
if (text.Length() > 0) {
|
|
be_clipboard->Lock();
|
|
be_clipboard->Data()->RemoveData("text/plain");
|
|
be_clipboard->Data()->AddData ("text/plain",
|
|
B_MIME_TYPE, text.String(), text.Length());
|
|
be_clipboard->Commit();
|
|
be_clipboard->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_SelectWordAt(const SelectionPoint& point, bool extend)
|
|
{
|
|
const char* line = fSourceCode->LineAt(point.line);
|
|
int32 length = fSourceCode->LineLengthAt(point.line);
|
|
int32 start = point.offset - 1;
|
|
int32 end = point.offset + 1;
|
|
while ((end) < length) {
|
|
if (!isalpha(line[end]) && !isdigit(line[end]))
|
|
break;
|
|
++end;
|
|
}
|
|
while ((start - 1) >= 0) {
|
|
if (!isalpha(line[start - 1]) && !isdigit(line[start - 1]))
|
|
break;
|
|
--start;
|
|
}
|
|
|
|
if (extend) {
|
|
if (point.line >= fSelectionBase.line
|
|
|| (point.line == fSelectionBase.line
|
|
&& point.offset > fSelectionBase.offset)) {
|
|
fSelectionStart.line = fSelectionBase.line;
|
|
fSelectionStart.offset = fSelectionBase.offset;
|
|
fSelectionEnd.line = point.line;
|
|
fSelectionEnd.offset = end;
|
|
} else if (point.line < fSelectionBase.line) {
|
|
fSelectionStart.line = point.line;
|
|
fSelectionStart.offset = start;
|
|
fSelectionEnd.line = fSelectionBase.line;
|
|
fSelectionEnd.offset = fSelectionBase.offset;
|
|
} else if (point.line == fSelectionBase.line) {
|
|
// if we hit here, our offset is before the actual start.
|
|
fSelectionStart.line = point.line;
|
|
fSelectionStart.offset = start;
|
|
fSelectionEnd.line = point.line;
|
|
fSelectionEnd.offset = fSelectionBase.offset;
|
|
}
|
|
} else {
|
|
fSelectionBase.line = fSelectionStart.line = point.line;
|
|
fSelectionBase.offset = fSelectionStart.offset = start;
|
|
fSelectionEnd.line = point.line;
|
|
fSelectionEnd.offset = end;
|
|
}
|
|
BRegion region;
|
|
_GetSelectionRegion(region);
|
|
Invalidate(®ion);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_SelectLineAt(const SelectionPoint& point, bool extend)
|
|
{
|
|
if (extend) {
|
|
if (point.line >= fSelectionBase.line) {
|
|
fSelectionStart.line = fSelectionBase.line;
|
|
fSelectionStart.offset = 0;
|
|
fSelectionEnd.line = point.line;
|
|
fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line);
|
|
} else {
|
|
fSelectionStart.line = point.line;
|
|
fSelectionStart.offset = 0;
|
|
fSelectionEnd.line = fSelectionBase.line;
|
|
fSelectionEnd.offset = fSourceCode->LineLengthAt(
|
|
fSelectionBase.line);
|
|
}
|
|
} else {
|
|
fSelectionStart.line = fSelectionEnd.line = point.line;
|
|
fSelectionStart.offset = 0;
|
|
fSelectionEnd.offset = fSourceCode->LineLengthAt(point.line);
|
|
}
|
|
BRegion region;
|
|
_GetSelectionRegion(region);
|
|
Invalidate(®ion);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_HandleAutoScroll(void)
|
|
{
|
|
BPoint point;
|
|
uint32 buttons;
|
|
GetMouse(&point, &buttons);
|
|
float difference = 0.0;
|
|
int factor = 0;
|
|
BRect visibleRect = Frame() & fSourceView->Bounds();
|
|
if (point.y < visibleRect.top)
|
|
difference = point.y - visibleRect.top;
|
|
else if (point.y > visibleRect.bottom)
|
|
difference = point.y - visibleRect.bottom;
|
|
if (difference != 0.0) {
|
|
factor = (int)(ceilf(difference / fFontInfo->lineHeight));
|
|
_ScrollByLines(factor);
|
|
}
|
|
difference = 0.0;
|
|
if (point.x < visibleRect.left)
|
|
difference = point.x - visibleRect.left;
|
|
else if (point.x > visibleRect.right)
|
|
difference = point.x - visibleRect.right;
|
|
if (difference != 0.0) {
|
|
factor = (int)(ceilf(difference / fCharacterWidth));
|
|
_ScrollHorizontal(factor);
|
|
}
|
|
|
|
MouseMoved(point, B_OUTSIDE_VIEW, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_ScrollHorizontal(int32 charCount)
|
|
{
|
|
BScrollBar* horizontal = fSourceView->ScrollBar(B_HORIZONTAL);
|
|
if (horizontal == NULL)
|
|
return;
|
|
|
|
float value = horizontal->Value();
|
|
horizontal->SetValue(value + fCharacterWidth * charCount);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_ScrollByLines(int32 lineCount)
|
|
{
|
|
BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
|
|
if (vertical == NULL)
|
|
return;
|
|
|
|
float value = vertical->Value();
|
|
vertical->SetValue(value + fFontInfo->lineHeight * lineCount);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_ScrollByPages(int32 pageCount)
|
|
{
|
|
BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
|
|
if (vertical == NULL)
|
|
return;
|
|
|
|
float value = vertical->Value();
|
|
vertical->SetValue(value
|
|
+ fSourceView->Frame().Size().height * pageCount);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_ScrollToTop(void)
|
|
{
|
|
BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
|
|
if (vertical == NULL)
|
|
return;
|
|
|
|
vertical->SetValue(0.0);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TextView::_ScrollToBottom(void)
|
|
{
|
|
BScrollBar* vertical = fSourceView->ScrollBar(B_VERTICAL);
|
|
if (vertical == NULL)
|
|
return;
|
|
|
|
float min, max;
|
|
vertical->GetRange(&min, &max);
|
|
vertical->SetValue(max);
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::TextView::_AddGeneralActions(BPopUpMenu* menu, int32 line)
|
|
{
|
|
if (fSourceCode == NULL)
|
|
return true;
|
|
|
|
BMessage* message = NULL;
|
|
if (fSourceCode->GetSourceFile() != NULL) {
|
|
message = new(std::nothrow) BMessage(MSG_OPEN_SOURCE_FILE);
|
|
if (message == NULL)
|
|
return false;
|
|
message->AddInt32("line", line);
|
|
|
|
if (!_AddGeneralActionItem(menu, "Open source file", message))
|
|
return false;
|
|
}
|
|
|
|
if (fSourceView->fStackFrame == NULL)
|
|
return true;
|
|
|
|
FunctionInstance* instance = fSourceView->fStackFrame->Function();
|
|
if (instance == NULL)
|
|
return true;
|
|
|
|
FileSourceCode* code = instance->GetFunction()->GetSourceCode();
|
|
|
|
// if we only have disassembly, this option doesn't apply.
|
|
if (code == NULL)
|
|
return true;
|
|
|
|
// verify that we do in fact know the source file of the function,
|
|
// since we can't switch to it if it wasn't found and hasn't been
|
|
// located.
|
|
BString sourcePath;
|
|
code->GetSourceFile()->GetLocatedPath(sourcePath);
|
|
if (sourcePath.IsEmpty())
|
|
return true;
|
|
|
|
message = new(std::nothrow) BMessage(
|
|
MSG_SWITCH_DISASSEMBLY_STATE);
|
|
if (message == NULL)
|
|
return false;
|
|
|
|
if (!_AddGeneralActionItem(menu, dynamic_cast<DisassembledCode*>(
|
|
fSourceCode) != NULL ? "Show source" : "Show disassembly",
|
|
message)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::TextView::_AddFlowControlActions(BPopUpMenu* menu, int32 line)
|
|
{
|
|
Statement* statement;
|
|
if (!fSourceView->GetStatementForLine(line, statement))
|
|
return true;
|
|
|
|
BReference<Statement> statementReference(statement, true);
|
|
target_addr_t address = statement->CoveringAddressRange().Start();
|
|
|
|
if (menu->CountItems() > 0)
|
|
menu->AddSeparatorItem();
|
|
|
|
if (!_AddFlowControlActionItem(menu, "Run to cursor", MSG_THREAD_RUN,
|
|
address)) {
|
|
return false;
|
|
}
|
|
|
|
if (!_AddFlowControlActionItem(menu, "Set next statement",
|
|
MSG_THREAD_SET_ADDRESS, address)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::TextView::_AddGeneralActionItem(BPopUpMenu* menu, const char* text,
|
|
BMessage* message) const
|
|
{
|
|
ObjectDeleter<BMessage> messageDeleter(message);
|
|
|
|
BMenuItem* item = new(std::nothrow) BMenuItem(text, message);
|
|
if (item == NULL)
|
|
return false;
|
|
ObjectDeleter<BMenuItem> itemDeleter(item);
|
|
messageDeleter.Detach();
|
|
|
|
if (!menu->AddItem(item))
|
|
return false;
|
|
|
|
itemDeleter.Detach();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::TextView::_AddFlowControlActionItem(BPopUpMenu* menu,
|
|
const char* text, uint32 what, target_addr_t address) const
|
|
{
|
|
BMessage* message = new(std::nothrow) BMessage(what);
|
|
if (message == NULL)
|
|
return false;
|
|
ObjectDeleter<BMessage> messageDeleter(message);
|
|
|
|
message->AddUInt64("address", address);
|
|
BMenuItem* item = new(std::nothrow) BMenuItem(text, message);
|
|
if (item == NULL)
|
|
return false;
|
|
ObjectDeleter<BMenuItem> itemDeleter(item);
|
|
messageDeleter.Detach();
|
|
|
|
if (!menu->AddItem(item))
|
|
return false;
|
|
|
|
itemDeleter.Detach();
|
|
return true;
|
|
}
|
|
|
|
|
|
// #pragma mark - SourceView
|
|
|
|
|
|
SourceView::SourceView(Team* team, Listener* listener)
|
|
:
|
|
BView("source view", 0),
|
|
fTeam(team),
|
|
fActiveThread(NULL),
|
|
fStackTrace(NULL),
|
|
fStackFrame(NULL),
|
|
fSourceCode(NULL),
|
|
fMarkerView(NULL),
|
|
fTextView(NULL),
|
|
fListener(listener),
|
|
fCurrentSyntaxInfo(NULL)
|
|
{
|
|
// init font info
|
|
fFontInfo.font = *be_fixed_font;
|
|
fFontInfo.font.GetHeight(&fFontInfo.fontHeight);
|
|
fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent)
|
|
+ ceilf(fFontInfo.fontHeight.descent);
|
|
}
|
|
|
|
|
|
SourceView::~SourceView()
|
|
{
|
|
SetStackFrame(NULL);
|
|
SetStackTrace(NULL, NULL);
|
|
SetSourceCode(NULL);
|
|
}
|
|
|
|
|
|
/*static*/ SourceView*
|
|
SourceView::Create(Team* team, Listener* listener)
|
|
{
|
|
SourceView* self = new SourceView(team, listener);
|
|
|
|
try {
|
|
self->_Init();
|
|
} catch (...) {
|
|
delete self;
|
|
throw;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::MessageReceived(BMessage* message)
|
|
{
|
|
switch(message->what) {
|
|
case MSG_THREAD_RUN:
|
|
case MSG_THREAD_SET_ADDRESS:
|
|
{
|
|
target_addr_t address;
|
|
if (message->FindUInt64("address", &address) != B_OK)
|
|
break;
|
|
fListener->ThreadActionRequested(fActiveThread, message->what,
|
|
address);
|
|
break;
|
|
}
|
|
|
|
case MSG_OPEN_SOURCE_FILE:
|
|
{
|
|
int32 line;
|
|
if (message->FindInt32("line", &line) != B_OK)
|
|
break;
|
|
// be:line is 1-based.
|
|
++line;
|
|
if (fSourceCode == NULL)
|
|
break;
|
|
LocatableFile* file = fSourceCode->GetSourceFile();
|
|
if (file == NULL)
|
|
break;
|
|
|
|
BString sourcePath;
|
|
file->GetLocatedPath(sourcePath);
|
|
if (sourcePath.IsEmpty())
|
|
break;
|
|
|
|
BPath path(sourcePath);
|
|
entry_ref ref;
|
|
if (path.InitCheck() != B_OK)
|
|
break;
|
|
|
|
if (get_ref_for_path(path.Path(), &ref) != B_OK)
|
|
break;
|
|
|
|
BMessage trackerMessage(B_REFS_RECEIVED);
|
|
trackerMessage.AddRef("refs", &ref);
|
|
trackerMessage.AddInt32("be:line", line);
|
|
|
|
BMessenger messenger(kTrackerSignature);
|
|
messenger.SendMessage(&trackerMessage);
|
|
break;
|
|
}
|
|
|
|
case MSG_SWITCH_DISASSEMBLY_STATE:
|
|
{
|
|
if (fStackFrame == NULL)
|
|
break;
|
|
|
|
FunctionInstance* instance = fStackFrame->Function();
|
|
if (instance == NULL)
|
|
break;
|
|
|
|
SourceCode* code = NULL;
|
|
if (dynamic_cast<FileSourceCode*>(fSourceCode) != NULL) {
|
|
if (instance->SourceCodeState()
|
|
== FUNCTION_SOURCE_NOT_LOADED) {
|
|
fListener->FunctionSourceCodeRequested(instance, true);
|
|
break;
|
|
}
|
|
|
|
code = instance->GetSourceCode();
|
|
} else {
|
|
Function* function = instance->GetFunction();
|
|
if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED
|
|
|| function->SourceCodeState()
|
|
== FUNCTION_SOURCE_SUPPRESSED) {
|
|
fListener->FunctionSourceCodeRequested(instance, false);
|
|
break;
|
|
}
|
|
|
|
code = function->GetSourceCode();
|
|
}
|
|
|
|
if (code != NULL)
|
|
SetSourceCode(code);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::UnsetListener()
|
|
{
|
|
fListener = NULL;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::SetStackTrace(StackTrace* stackTrace, Thread* activeThread)
|
|
{
|
|
TRACE_GUI("SourceView::SetStackTrace(%p)\n", stackTrace);
|
|
|
|
if (stackTrace == fStackTrace)
|
|
return;
|
|
|
|
if (fActiveThread != NULL)
|
|
fActiveThread->ReleaseReference();
|
|
|
|
fActiveThread = activeThread;
|
|
|
|
if (fActiveThread != NULL)
|
|
fActiveThread->AcquireReference();
|
|
|
|
if (fStackTrace != NULL) {
|
|
fMarkerManager->SetStackTrace(NULL);
|
|
fMarkerView->SetStackTrace(NULL);
|
|
fStackTrace->ReleaseReference();
|
|
}
|
|
|
|
fStackTrace = stackTrace;
|
|
|
|
if (fStackTrace != NULL)
|
|
fStackTrace->AcquireReference();
|
|
|
|
fMarkerManager->SetStackTrace(fStackTrace);
|
|
fMarkerView->SetStackTrace(fStackTrace);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::SetStackFrame(StackFrame* stackFrame)
|
|
{
|
|
TRACE_GUI("SourceView::SetStackFrame(%p)\n", stackFrame);
|
|
if (stackFrame == fStackFrame)
|
|
return;
|
|
|
|
if (fStackFrame != NULL) {
|
|
fMarkerManager->SetStackFrame(NULL);
|
|
fMarkerView->SetStackFrame(NULL);
|
|
fStackFrame->ReleaseReference();
|
|
}
|
|
|
|
fStackFrame = stackFrame;
|
|
|
|
if (fStackFrame != NULL)
|
|
fStackFrame->AcquireReference();
|
|
|
|
fMarkerManager->SetStackFrame(fStackFrame);
|
|
fMarkerView->SetStackFrame(fStackFrame);
|
|
fTextView->Invalidate();
|
|
|
|
if (fStackFrame != NULL)
|
|
ScrollToAddress(fStackFrame->InstructionPointer());
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::SetSourceCode(SourceCode* sourceCode)
|
|
{
|
|
// set the source code, if it changed
|
|
if (sourceCode == fSourceCode)
|
|
return;
|
|
|
|
if (fSourceCode != NULL) {
|
|
fMarkerManager->SetSourceCode(NULL);
|
|
fTextView->SetSourceCode(NULL);
|
|
fMarkerView->SetSourceCode(NULL);
|
|
fSourceCode->ReleaseReference();
|
|
delete fCurrentSyntaxInfo;
|
|
fCurrentSyntaxInfo = NULL;
|
|
}
|
|
|
|
fSourceCode = sourceCode;
|
|
|
|
if (fSourceCode != NULL) {
|
|
fSourceCode->AcquireReference();
|
|
|
|
SourceLanguage* language = fSourceCode->GetSourceLanguage();
|
|
if (language != NULL) {
|
|
SyntaxHighlighter* highlighter = language->GetSyntaxHighlighter();
|
|
if (highlighter != NULL) {
|
|
BReference<SyntaxHighlighter> syntaxReference(highlighter,
|
|
true);
|
|
highlighter->ParseText(fSourceCode,
|
|
fTeam->GetTeamTypeInformation(), fCurrentSyntaxInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
fMarkerManager->SetSourceCode(fSourceCode);
|
|
fTextView->SetSourceCode(fSourceCode);
|
|
fMarkerView->SetSourceCode(fSourceCode);
|
|
_UpdateScrollBars();
|
|
|
|
if (fStackFrame != NULL)
|
|
ScrollToAddress(fStackFrame->InstructionPointer());
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::UserBreakpointChanged(UserBreakpoint* breakpoint)
|
|
{
|
|
fMarkerManager->UserBreakpointChanged(breakpoint);
|
|
fMarkerView->UserBreakpointChanged(breakpoint);
|
|
fTextView->UserBreakpointChanged(breakpoint);
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::ScrollToAddress(target_addr_t address)
|
|
{
|
|
TRACE_GUI("SourceView::ScrollToAddress(%#" B_PRIx64 ")\n", address);
|
|
|
|
if (fSourceCode == NULL)
|
|
return false;
|
|
|
|
AutoLocker<Team> locker(fTeam);
|
|
|
|
FunctionInstance* functionInstance;
|
|
Statement* statement;
|
|
if (fTeam->GetStatementAtAddress(address, functionInstance,
|
|
statement) != B_OK) {
|
|
return false;
|
|
}
|
|
BReference<Statement> statementReference(statement, true);
|
|
|
|
return ScrollToLine(statement->StartSourceLocation().Line());
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::ScrollToLine(uint32 line)
|
|
{
|
|
TRACE_GUI("SourceView::ScrollToLine(%" B_PRIu32 ")\n", line);
|
|
|
|
if (fSourceCode == NULL || line >= (uint32)fSourceCode->CountLines())
|
|
return false;
|
|
|
|
float top = (float)line * fFontInfo.lineHeight;
|
|
float bottom = top + fFontInfo.lineHeight - 1;
|
|
|
|
BRect visible = Bounds();
|
|
|
|
TRACE_GUI("SourceView::ScrollToLine(%" B_PRId32 ")\n", line);
|
|
TRACE_GUI(" visible: (%f, %f) - (%f, %f), line: %f - %f\n", visible.left,
|
|
visible.top, visible.right, visible.bottom, top, bottom);
|
|
|
|
// If not visible at all, scroll to the center, otherwise scroll so that at
|
|
// least one more line is visible.
|
|
if (top >= visible.bottom || bottom <= visible.top) {
|
|
TRACE_GUI(" -> scrolling to (%f, %f)\n", visible.left,
|
|
top - (visible.Height() + 1) / 2);
|
|
ScrollTo(visible.left, top - (visible.Height() + 1) / 2);
|
|
} else if (top - fFontInfo.lineHeight < visible.top)
|
|
ScrollBy(0, top - fFontInfo.lineHeight - visible.top);
|
|
else if (bottom + fFontInfo.lineHeight > visible.bottom)
|
|
ScrollBy(0, bottom + fFontInfo.lineHeight - visible.bottom);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::HighlightBorder(bool state)
|
|
{
|
|
BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
|
|
if (parent != NULL)
|
|
parent->SetBorderHighlighted(state);
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::TargetedByScrollView(BScrollView* scrollView)
|
|
{
|
|
_UpdateScrollBars();
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::MinSize()
|
|
{
|
|
// BSize markerSize(fMarkerView->MinSize());
|
|
// BSize textSize(fTextView->MinSize());
|
|
// return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
|
|
// std::max(markerSize.height, textSize.height));
|
|
return BSize(10, 10);
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::MaxSize()
|
|
{
|
|
// BSize markerSize(fMarkerView->MaxSize());
|
|
// BSize textSize(fTextView->MaxSize());
|
|
// return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
|
|
// std::min(markerSize.height, textSize.height));
|
|
return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::PreferredSize()
|
|
{
|
|
BSize markerSize(fMarkerView->PreferredSize());
|
|
BSize textSize(fTextView->PreferredSize());
|
|
return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
|
|
std::max(markerSize.height, textSize.height));
|
|
// return MinSize();
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::DoLayout()
|
|
{
|
|
BSize size = _DataRectSize();
|
|
float markerWidth = fMarkerView->MinSize().width;
|
|
|
|
fMarkerView->MoveTo(0, 0);
|
|
fMarkerView->ResizeTo(markerWidth, size.height);
|
|
|
|
fTextView->MoveTo(markerWidth + 1, 0);
|
|
fTextView->ResizeTo(size.width - markerWidth - 1, size.height);
|
|
|
|
_UpdateScrollBars();
|
|
}
|
|
|
|
|
|
bool
|
|
SourceView::GetStatementForLine(int32 line, Statement*& _statement)
|
|
{
|
|
if (line < 0)
|
|
return false;
|
|
|
|
AutoLocker<Team> locker(fTeam);
|
|
Statement* statement;
|
|
if (fTeam->GetStatementAtSourceLocation(fSourceCode, SourceLocation(line),
|
|
statement) != B_OK) {
|
|
return false;
|
|
}
|
|
BReference<Statement> statementReference(statement, true);
|
|
if (statement->StartSourceLocation().Line() != line)
|
|
return false;
|
|
|
|
_statement = statement;
|
|
statementReference.Detach();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::_Init()
|
|
{
|
|
fMarkerManager = new MarkerManager(this, fTeam, fListener);
|
|
AddChild(fMarkerView = new MarkerView(this, fTeam, fListener,
|
|
fMarkerManager, &fFontInfo));
|
|
AddChild(fTextView = new TextView(this, fMarkerManager, &fFontInfo));
|
|
}
|
|
|
|
|
|
void
|
|
SourceView::_UpdateScrollBars()
|
|
{
|
|
BSize dataRectSize = _DataRectSize();
|
|
BSize size = Frame().Size();
|
|
|
|
if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) {
|
|
float range = dataRectSize.width - size.width;
|
|
if (range > 0) {
|
|
scrollBar->SetRange(0, range);
|
|
scrollBar->SetProportion(
|
|
(size.width + 1) / (dataRectSize.width + 1));
|
|
scrollBar->SetSteps(fFontInfo.lineHeight, size.width + 1);
|
|
} else {
|
|
scrollBar->SetRange(0, 0);
|
|
scrollBar->SetProportion(1);
|
|
}
|
|
}
|
|
|
|
if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
|
|
float range = dataRectSize.height - size.height;
|
|
if (range > 0) {
|
|
scrollBar->SetRange(0, range);
|
|
scrollBar->SetProportion(
|
|
(size.height + 1) / (dataRectSize.height + 1));
|
|
scrollBar->SetSteps(fFontInfo.lineHeight, size.height + 1);
|
|
} else {
|
|
scrollBar->SetRange(0, 0);
|
|
scrollBar->SetProportion(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BSize
|
|
SourceView::_DataRectSize() const
|
|
{
|
|
float width = fMarkerView->MinSize().width + fTextView->MinSize().width + 1;
|
|
float height = std::max(fMarkerView->MinSize().height,
|
|
fTextView->MinSize().height);
|
|
|
|
BSize size = Frame().Size();
|
|
return BSize(std::max(size.width, width), std::max(size.height, height));
|
|
}
|
|
|
|
|
|
// #pragma mark - Listener
|
|
|
|
|
|
SourceView::Listener::~Listener()
|
|
{
|
|
}
|