/*
* Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2016, Rene Gollent, rene@gollent.com.
* 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),
fMarkerManager(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);
delete fMarkerManager;
}
/*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 && activeThread == fActiveThread)
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()
{
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fSourceCode.