diff --git a/src/apps/debuganalyzer/gui/Jamfile b/src/apps/debuganalyzer/gui/Jamfile index 4061bb05a9..f3916d11e8 100644 --- a/src/apps/debuganalyzer/gui/Jamfile +++ b/src/apps/debuganalyzer/gui/Jamfile @@ -12,12 +12,14 @@ MergeObject DebugAnalyzer_gui.o SubWindow.cpp SubWindowManager.cpp : + DebugAnalyzer_gui_chart.o DebugAnalyzer_gui_main_window.o DebugAnalyzer_gui_table.o DebugAnalyzer_gui_thread_window.o ; +HaikuSubInclude chart ; HaikuSubInclude main_window ; HaikuSubInclude table ; HaikuSubInclude thread_window ; diff --git a/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.cpp b/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.cpp new file mode 100644 index 0000000000..d79718310e --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/BigtimeChartAxisLegendSource.h" + +#include + +#include "chart/ChartDataRange.h" +#include "chart/StringChartLegend.h" + + +struct decomposed_time { + bigtime_t hours; + int minutes; + int seconds; + int micros; +}; + + +static void +decompose_time(bigtime_t time, decomposed_time& decomposed) +{ + // microsecs + decomposed.micros = time % 1000000; + time /= 1000000; + // secs + decomposed.seconds = time % 60; + time /= 60; + // mins + decomposed.minutes = time % 60; + time /= 60; + // hours + decomposed.hours = time; +} + + +int32 +BigtimeChartAxisLegendSource::GetAxisLegends(const ChartDataRange& range, + ChartLegend** legends, double* values, int32 maxLegends) +{ + // interpret range as time range + bigtime_t startTime = (bigtime_t)range.min; + bigtime_t endTime = (bigtime_t)range.max; +printf("BigtimeChartAxisLegendSource::GetAxisLegends(): range: %lld - %lld\n", startTime, endTime); +// TODO: Handle sub-microsecs ranges! + if (startTime >= endTime) + return 0; + + bigtime_t positionFactors[4]; + positionFactors[3] = 1; + positionFactors[2] = 1000000; + positionFactors[1] = positionFactors[2] * 60; + positionFactors[0] = positionFactors[1] * 60; + + // find the main position (h, m, s, us) we want to play with + int32 position = 0; + bigtime_t rangeTime = endTime - startTime; + while (rangeTime / positionFactors[position] + 1 < maxLegends / 2 + && position < 3) { + position++; + } + + // adjust the factor so that we get maxLegends / 2 to maxLegends legends + bigtime_t baseInterval = positionFactors[position]; + bigtime_t relativeFactor = 1; + while (rangeTime / (baseInterval * relativeFactor) >= maxLegends) { + if (relativeFactor == 1) { + relativeFactor = 2; + } else if (relativeFactor == 2) { + relativeFactor = 5; + } else if (relativeFactor == 5) { + baseInterval *= 10; + relativeFactor = 1; + } + } + + // generate the legends + int32 count = 0; + bigtime_t interval = baseInterval * relativeFactor; + bigtime_t time = (startTime + interval - 1) / interval * interval; + for (; time <= endTime; time += interval) { + decomposed_time decomposed; + decompose_time(time, decomposed); + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%02lld:%02d:%02d.%06d", + decomposed.hours, decomposed.minutes, decomposed.seconds, + decomposed.micros); +// TODO: Drop superfluous micro seconds digits, or even microseconds and seconds +// completely. + + StringChartLegend* legend = new(std::nothrow) StringChartLegend(buffer, + 1); + if (legend == 0) + return count; + + legends[count] = legend; + values[count++] = (double)time; + } + + return count; +} diff --git a/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.h b/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.h new file mode 100644 index 0000000000..42ba22b8a0 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/BigtimeChartAxisLegendSource.h @@ -0,0 +1,19 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef BIGTIME_CHART_AXIS_LEGEND_SOURCE_H +#define BIGTIME_CHART_AXIS_LEGEND_SOURCE_H + +#include "chart/ChartAxisLegendSource.h" + + +class BigtimeChartAxisLegendSource : public ChartAxisLegendSource { +public: + virtual int32 GetAxisLegends(const ChartDataRange& range, + ChartLegend** legends, double* values, + int32 maxLegends); +}; + + +#endif // BIGTIME_CHART_AXIS_LEGEND_SOURCE_H diff --git a/src/apps/debuganalyzer/gui/chart/Chart.cpp b/src/apps/debuganalyzer/gui/chart/Chart.cpp new file mode 100644 index 0000000000..5ac6e699b1 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/Chart.cpp @@ -0,0 +1,340 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/Chart.h" + +#include + +#include + +#include +#include + +#include "chart/ChartAxis.h" +#include "chart/ChartDataSource.h" +#include "chart/ChartRenderer.h" + + +// #pragma mark - Chart::AxisInfo + + +Chart::AxisInfo::AxisInfo() + : + axis(NULL) +{ +} + + +void +Chart::AxisInfo::SetFrame(float left, float top, float right, float bottom) +{ + frame.Set(left, top, right, bottom); + if (axis != NULL) + axis->SetFrame(frame); +} + + +void +Chart::AxisInfo::SetRange(const ChartDataRange& range) +{ + if (axis != NULL) + axis->SetRange(range); +} + + +void +Chart::AxisInfo::Render(BView* view, const BRect& updateRect) +{ + if (axis != NULL) + axis->Render(view, updateRect); +} + + +// #pragma mark - Chart + + +Chart::Chart(ChartRenderer* renderer, const char* name) + : + BView(name, B_FRAME_EVENTS | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), + fRenderer(renderer) +{ + SetViewColor(B_TRANSPARENT_32_BIT); + +// fRenderer->SetFrame(Bounds()); +} + + +Chart::~Chart() +{ +} + + +bool +Chart::AddDataSource(ChartDataSource* dataSource, int32 index, + ChartRendererDataSourceConfig* config) +{ +printf("Chart::AddDataSource(%p)\n", dataSource); + if (dataSource == NULL) + return false; + + if (index < 0 || index > fDataSources.CountItems()) + index = fDataSources.CountItems(); + + if (!fDataSources.AddItem(dataSource, index)) + return false; + + if (!fRenderer->AddDataSource(dataSource, index, config)) { + fDataSources.RemoveItemAt(index); + return false; + } + + _UpdateDomainAndRange(); + + InvalidateLayout(); + Invalidate(); + + return true; +} + + +bool +Chart::AddDataSource(ChartDataSource* dataSource, + ChartRendererDataSourceConfig* config) +{ + return AddDataSource(dataSource, -1, config); +} + + +bool +Chart::RemoveDataSource(ChartDataSource* dataSource) +{ + if (dataSource == NULL) + return false; + + return RemoveDataSource(fDataSources.IndexOf(dataSource)); +} + + +ChartDataSource* +Chart::RemoveDataSource(int32 index) +{ +printf("Chart::RemoveDataSource(%ld)\n", index); + if (index < 0 || index >= fDataSources.CountItems()) + return NULL; + + ChartDataSource* dataSource = fDataSources.RemoveItemAt(index); + + fRenderer->RemoveDataSource(dataSource); + + _UpdateDomainAndRange(); + + return dataSource; +} + + +void +Chart::RemoveAllDataSources() +{ +printf("Chart::RemoveAllDataSources()\n"); + int32 count = fDataSources.CountItems(); + for (int32 i = count - 1; i >= 0; i--) + fRenderer->RemoveDataSource(fDataSources.ItemAt(i)); + + fDataSources.MakeEmpty(); + + _UpdateDomainAndRange(); + + InvalidateLayout(); + Invalidate(); +} + + +void +Chart::SetAxis(ChartAxisLocation location, ChartAxis* axis) +{ + switch (location) { + case CHART_AXIS_LEFT: + fLeftAxis.axis = axis; + break; + case CHART_AXIS_TOP: + fTopAxis.axis = axis; + break; + case CHART_AXIS_RIGHT: + fRightAxis.axis = axis; + break; + case CHART_AXIS_BOTTOM: + fBottomAxis.axis = axis; + break; + default: + return; + } + + axis->SetLocation(location); + + InvalidateLayout(); + Invalidate(); +} + + +#if 0 +ChartDataRange +Chart::Domain() const +{ + return fDataSource != NULL ? fDataSource->Domain() : ChartDataRange(); +} + + +void +Chart::SetDisplayDomain(const ChartDataRange& domain) +{ + fRenderer->SetDomain(domain); + Invalidate(); +} + + +void +Chart::SetDisplayDomain(const ChartDataRange& range) +{ + fRenderer->SetRange(range); + Invalidate(); +} +#endif + + +void +Chart::FrameResized(float newWidth, float newHeight) +{ +//printf("Chart::FrameResized(%f, %f)\n", newWidth, newHeight); +// fRenderer->SetFrame(Bounds()); + + Invalidate(); +} + + +void +Chart::Draw(BRect updateRect) +{ +printf("Chart::Draw((%f, %f) - (%f, %f))\n", updateRect.left, updateRect.top, updateRect.right, updateRect.bottom); + rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); + rgb_color color; + + // clear the axes background + if (fLeftAxis.axis != NULL || fTopAxis.axis != NULL + || fRightAxis.axis != NULL || fBottomAxis.axis != NULL) { + SetHighColor(background); + BRegion clippingRegion(Bounds()); + clippingRegion.Exclude(fChartFrame); + ConstrainClippingRegion(&clippingRegion); + FillRect(Bounds()); + ConstrainClippingRegion(NULL); + } + + // render the axes + fLeftAxis.Render(this, updateRect); + fTopAxis.Render(this, updateRect); + fRightAxis.Render(this, updateRect); + fBottomAxis.Render(this, updateRect); + + // draw the frame around the chart area and clear the background + BRect chartFrame(fChartFrame); + be_control_look->DrawBorder(this, chartFrame, updateRect, background, + B_PLAIN_BORDER); + // DrawBorder() insets the supplied rect + SetHighColor(color.set_to(255, 255, 255, 255)); + FillRect(chartFrame); + + // render the chart + BRegion clippingRegion(chartFrame); + ConstrainClippingRegion(&clippingRegion); + fRenderer->Render(this, updateRect); +} + + +#if 0 +BSize +Chart::MinSize() +{ +} + + +BSize +Chart::MaxSize() +{ +} + + +BSize +Chart::PreferredSize() +{ +} +#endif + + +void +Chart::DoLayout() +{ + // get size in pixels + BSize size = Bounds().Size(); +printf("Chart::DoLayout(%f, %f)\n", size.width, size.height); + int32 width = size.IntegerWidth() + 1; + int32 height = size.IntegerHeight() + 1; + + // compute the axis insets + int32 left = 0; + int32 right = 0; + int32 top = 0; + int32 bottom = 0; + + if (fLeftAxis.axis != NULL) + left = fLeftAxis.axis->PreferredSize(this).IntegerWidth() + 1; + if (fRightAxis.axis != NULL) + right = fRightAxis.axis->PreferredSize(this).IntegerWidth() + 1; + if (fTopAxis.axis != NULL) + top = fTopAxis.axis->PreferredSize(this).IntegerHeight() + 1; + if (fBottomAxis.axis != NULL) + bottom = fBottomAxis.axis->PreferredSize(this).IntegerHeight() + 1; + + fChartFrame = BRect(left, top, width - right - 1, height - bottom - 1); + fRenderer->SetFrame(fChartFrame.InsetByCopy(1, 1)); +printf(" fChartFrame: (%f, %f) - (%f, %f)\n", fChartFrame.left, fChartFrame.top, fChartFrame.right, fChartFrame.bottom); + + fLeftAxis.SetFrame(0, fChartFrame.top, fChartFrame.left - 1, + fChartFrame.bottom); + fRightAxis.SetFrame(fChartFrame.right + 1, fChartFrame.top, width - 1, + fChartFrame.bottom); + fTopAxis.SetFrame(fChartFrame.left, 0, fChartFrame.right, + fChartFrame.top - 1); + fBottomAxis.SetFrame(fChartFrame.left, fChartFrame.bottom + 1, + fChartFrame.right, height - 1); +} + + +void +Chart::_UpdateDomainAndRange() +{ + if (fDataSources.IsEmpty()) { + fDomain = ChartDataRange(); + fRange = ChartDataRange(); + return; + } else { + ChartDataSource* firstSource = fDataSources.ItemAt(0); + fDomain = firstSource->Domain(); + fRange = firstSource->Range(); + + for (int32 i = 1; ChartDataSource* source = fDataSources.ItemAt(i); + i++) { + fDomain.Extend(source->Domain()); + fRange.Extend(source->Range()); + } + } + + fRenderer->SetDomain(fDomain); + fRenderer->SetRange(fRange); + + fLeftAxis.SetRange(fRange); + fTopAxis.SetRange(fDomain); + fRightAxis.SetRange(fRange); + fBottomAxis.SetRange(fDomain); +} diff --git a/src/apps/debuganalyzer/gui/chart/Chart.h b/src/apps/debuganalyzer/gui/chart/Chart.h new file mode 100644 index 0000000000..fcef82db26 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/Chart.h @@ -0,0 +1,95 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_H +#define CHART_H + +#include + +#include + +#include "chart/ChartDataRange.h" +#include "chart/ChartDefs.h" + + +class ChartAxis; +class ChartDataSource; +class ChartRenderer; +class ChartRendererDataSourceConfig; + + +class Chart : public BView { +public: + Chart(ChartRenderer* renderer, + const char* name = NULL); + virtual ~Chart(); + + bool AddDataSource(ChartDataSource* dataSource, + int32 index, + ChartRendererDataSourceConfig* config + = NULL); + bool AddDataSource(ChartDataSource* dataSource, + ChartRendererDataSourceConfig* config + = NULL); + bool RemoveDataSource( + ChartDataSource* dataSource); + ChartDataSource* RemoveDataSource(int32 index); + void RemoveAllDataSources(); + + void SetAxis(ChartAxisLocation location, + ChartAxis* axis); + +#if 0 + ChartDataRange Domain() const; + + void SetDisplayDomain(const ChartDataRange& domain); + void SetDisplayRange(const ChartDataRange& range); +#endif + + virtual void FrameResized(float newWidth, float newHeight); + virtual void Draw(BRect updateRect); + +#if 0 + virtual BSize MinSize(); + virtual BSize MaxSize(); + virtual BSize PreferredSize(); +#endif + + virtual void DoLayout(); + +private: + typedef BObjectList DataSourceList; + + struct AxisInfo { + ChartAxis* axis; + BRect frame; + + AxisInfo(); + void SetFrame(float left, float top, float right, + float bottom); + void SetRange(const ChartDataRange& range); + void Render(BView* view, const BRect& updateRect); + }; + +private: + void _UpdateDomainAndRange(); + +private: + ChartRenderer* fRenderer; + DataSourceList fDataSources; + AxisInfo fLeftAxis; + AxisInfo fTopAxis; + AxisInfo fRightAxis; + AxisInfo fBottomAxis; + ChartDataRange fDomain; + ChartDataRange fRange; +#if 0 + ChartDataRange fDisplayDomain; + ChartDataRange fDisplayRange; +#endif + BRect fChartFrame; +}; + + +#endif // CHART_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartAxis.cpp b/src/apps/debuganalyzer/gui/chart/ChartAxis.cpp new file mode 100644 index 0000000000..61f9f4be7d --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartAxis.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/ChartAxis.h" + + +// #pragma mark - ChartAxis + + +ChartAxis::~ChartAxis() +{ +} diff --git a/src/apps/debuganalyzer/gui/chart/ChartAxis.h b/src/apps/debuganalyzer/gui/chart/ChartAxis.h new file mode 100644 index 0000000000..8c8a99bc10 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartAxis.h @@ -0,0 +1,29 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_AXIS_H +#define CHART_AXIS_H + +#include + +#include "chart/ChartDefs.h" + + +class BView; +class ChartDataRange; + + +class ChartAxis { +public: + virtual ~ChartAxis(); + + virtual void SetLocation(ChartAxisLocation location) = 0; + virtual void SetRange(const ChartDataRange& range) = 0; + virtual void SetFrame(BRect frame) = 0; + virtual BSize PreferredSize(BView* view) = 0; + virtual void Render(BView* view, BRect updateRect) = 0; +}; + + +#endif // CHART_AXIS_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.cpp b/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.cpp new file mode 100644 index 0000000000..0c0ead7556 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.cpp @@ -0,0 +1,11 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/ChartAxisLegendSource.h" + + +ChartAxisLegendSource::~ChartAxisLegendSource() +{ +} diff --git a/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.h b/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.h new file mode 100644 index 0000000000..a0388a9b8b --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartAxisLegendSource.h @@ -0,0 +1,25 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_AXIS_LEGEND_SOURCE_H +#define CHART_AXIS_LEGEND_SOURCE_H + +#include + + +class ChartDataRange; +class ChartLegend; + + +class ChartAxisLegendSource { +public: + virtual ~ChartAxisLegendSource(); + + virtual int32 GetAxisLegends(const ChartDataRange& range, + ChartLegend** legends, double* values, + int32 maxLegends) = 0; +}; + + +#endif // CHART_AXIS_LEGEND_SOURCE_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartDataRange.h b/src/apps/debuganalyzer/gui/chart/ChartDataRange.h new file mode 100644 index 0000000000..6d72d6a2dc --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartDataRange.h @@ -0,0 +1,63 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_DATA_RANGE_H +#define CHART_DATA_RANGE_H + +#include + + +class ChartDataRange { +public: + double min; + double max; + + ChartDataRange() + : + min(0), + max(0) + { + } + + ChartDataRange(double min, double max) + : + min(min), + max(max) + { + } + + ChartDataRange(const ChartDataRange& other) + : + min(other.min), + max(other.max) + { + } + + ChartDataRange& Extend(const ChartDataRange& other) + { + min = std::min(min, other.min); + max = std::max(max, other.max); + return *this; + } + + ChartDataRange& operator=(const ChartDataRange& other) + { + min = other.min; + max = other.max; + return *this; + } + + bool operator==(const ChartDataRange& other) const + { + return min == other.min && max == other.max; + } + + bool operator!=(const ChartDataRange& other) const + { + return !(*this == other); + } +}; + + +#endif // CHART_DATA_RANGE_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartDataSource.cpp b/src/apps/debuganalyzer/gui/chart/ChartDataSource.cpp new file mode 100644 index 0000000000..d24c249a62 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartDataSource.cpp @@ -0,0 +1,11 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/ChartDataSource.h" + + +ChartDataSource::~ChartDataSource() +{ +} diff --git a/src/apps/debuganalyzer/gui/chart/ChartDataSource.h b/src/apps/debuganalyzer/gui/chart/ChartDataSource.h new file mode 100644 index 0000000000..2f9147a980 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartDataSource.h @@ -0,0 +1,24 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_DATA_SOURCE_H +#define CHART_DATA_SOURCE_H + +#include + +#include "chart/ChartDataRange.h" + + +class ChartDataSource { +public: + virtual ~ChartDataSource(); + + virtual ChartDataRange Domain() const = 0; + virtual ChartDataRange Range() const = 0; + + virtual void GetSamples(double start, double end, + double* samples, int32 count) = 0; +}; + +#endif // CHART_DATA_SOURCE_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartDefs.h b/src/apps/debuganalyzer/gui/chart/ChartDefs.h new file mode 100644 index 0000000000..ee3e7312a0 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartDefs.h @@ -0,0 +1,17 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_DEFS_H +#define CHART_DEFS_H + + +enum ChartAxisLocation { + CHART_AXIS_LEFT, + CHART_AXIS_TOP, + CHART_AXIS_RIGHT, + CHART_AXIS_BOTTOM +}; + + +#endif // CHART_DEFS_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartLegend.cpp b/src/apps/debuganalyzer/gui/chart/ChartLegend.cpp new file mode 100644 index 0000000000..6e42600102 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartLegend.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/ChartLegend.h" + + +// #pragma mark - ChartLegend + + +ChartLegend::ChartLegend(int32 level) + : + fLevel(level) +{ +} + + +ChartLegend::~ChartLegend() +{ +} + + +// #pragma mark - ChartLegendRenderer + + +ChartLegendRenderer::~ChartLegendRenderer() +{ +} diff --git a/src/apps/debuganalyzer/gui/chart/ChartLegend.h b/src/apps/debuganalyzer/gui/chart/ChartLegend.h new file mode 100644 index 0000000000..0aa54dcbb2 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartLegend.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_LEGEND_H +#define CHART_LEGEND_H + +#include +#include + + +class BView; +class ChartDataRange; + + +class ChartLegend { +public: + ChartLegend(int32 level = 0); + // A lower level means more likely to be + // shown. <= 0 is mandatory. + virtual ~ChartLegend(); + + int32 Level() { return fLevel; } + +private: + int32 fLevel; +}; + + +class ChartLegendRenderer { +public: + virtual ~ChartLegendRenderer(); + + virtual void GetMinimumLegendSpacing(BView* view, + float* horizontal, float* vertical) = 0; + virtual BSize MaximumLegendSize(BView* view) = 0; + + virtual BSize LegendSize(ChartLegend* legend, + BView* view) = 0; + virtual void RenderLegend(ChartLegend* legend, BView* view, + BPoint point) = 0; +}; + + +#endif // CHART_LEGEND_H diff --git a/src/apps/debuganalyzer/gui/chart/ChartRenderer.cpp b/src/apps/debuganalyzer/gui/chart/ChartRenderer.cpp new file mode 100644 index 0000000000..b367a4af7c --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartRenderer.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/ChartRenderer.h" + + +// #pragma mark - ChartRenderer + + +ChartRenderer::~ChartRenderer() +{ +} + + +// #pragma mark - ChartRendererDataSourceConfig + + +ChartRendererDataSourceConfig::~ChartRendererDataSourceConfig() +{ +} diff --git a/src/apps/debuganalyzer/gui/chart/ChartRenderer.h b/src/apps/debuganalyzer/gui/chart/ChartRenderer.h new file mode 100644 index 0000000000..279a79d818 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/ChartRenderer.h @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHART_RENDERER_H +#define CHART_RENDERER_H + +#include + + +class BView; +class ChartDataRange; +class ChartDataSource; + + +class ChartRendererDataSourceConfig { +public: + virtual ~ChartRendererDataSourceConfig(); +}; + + +class ChartRenderer { +public: + virtual ~ChartRenderer(); + + virtual bool AddDataSource( + ChartDataSource* dataSource, + int32 index, + ChartRendererDataSourceConfig* config) = 0; + virtual void RemoveDataSource( + ChartDataSource* dataSource) = 0; + + virtual void SetFrame(const BRect& frame) = 0; + virtual void SetDomain(const ChartDataRange& domain) = 0; + virtual void SetRange(const ChartDataRange& range) = 0; + + virtual void Render(BView* view, BRect updateRect) = 0; +}; + + +#endif // CHART_RENDERER_H diff --git a/src/apps/debuganalyzer/gui/chart/Jamfile b/src/apps/debuganalyzer/gui/chart/Jamfile new file mode 100644 index 0000000000..a2df2deb25 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/Jamfile @@ -0,0 +1,20 @@ +SubDir HAIKU_TOP src apps debuganalyzer gui chart ; + +UsePrivateHeaders shared ; + +UseHeaders $(HAIKU_DEBUG_ANALYZER_HEADERS) ; + + +MergeObject DebugAnalyzer_gui_chart.o + : + BigtimeChartAxisLegendSource.cpp + Chart.cpp + ChartAxis.cpp + ChartAxisLegendSource.cpp + ChartDataSource.cpp + ChartLegend.cpp + ChartRenderer.cpp + LegendChartAxis.cpp + LineChartRenderer.cpp + StringChartLegend.cpp +; diff --git a/src/apps/debuganalyzer/gui/chart/LegendChartAxis.cpp b/src/apps/debuganalyzer/gui/chart/LegendChartAxis.cpp new file mode 100644 index 0000000000..65d430bb9c --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/LegendChartAxis.cpp @@ -0,0 +1,343 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/LegendChartAxis.h" + +#include +#include + +#include +#include + +#include +#include + +#include "chart/ChartLegend.h" +#include "chart/ChartAxisLegendSource.h" + + +static const int32 kChartRulerDistance = 2; +static const int32 kRulerSize = 3; +static const int32 kRulerMarkSize = 3; +static const int32 kRulerLegendDistance = 2; +static const int32 kChartLegendDistance + = kChartRulerDistance + kRulerSize + kRulerMarkSize + kRulerLegendDistance; + + + +struct LegendChartAxis::LegendInfo { + ChartLegend* legend; + double value; + BSize size; + + LegendInfo(ChartLegend* legend, double value, BSize size) + : + legend(legend), + value(value), + size(size) + { + } + + ~LegendInfo() + { + delete legend; + } +}; + + +float +LegendChartAxis::_LegendPosition(double value, float legendSize, + float totalSize, double scale) +{ + float position = (value - fRange.min) * scale - legendSize / 2; + if (position + legendSize > totalSize) + position = totalSize - legendSize; + if (position < 0) + position = 0; + return position; +} + + +void +LegendChartAxis::_FilterLegends(int32 totalSize, int32 spacing, + float BSize::* sizeField) +{ +printf("LegendChartAxis::_FilterLegends(%ld, %ld)\n", totalSize, spacing); + // compute the min/max legend levels + int32 legendCount = fLegends.CountItems(); + int32 minLevel = INT_MAX; + int32 maxLevel = 0; + + for (int32 i = 0; i < legendCount; i++) { + LegendInfo* info = fLegends.ItemAt(i); + int32 level = info->legend->Level(); + if (level < minLevel) + minLevel = level; + if (level > maxLevel) + maxLevel = level; + } + + if (maxLevel <= 0) + return; + + double rangeSize = fRange.max - fRange.min; + if (rangeSize == 0) + rangeSize = 1.0; + double scale = (double)totalSize / rangeSize; + + // Filter out all higher level legends colliding with lower level or + // preceeding same-level legends. We iterate backwards from the lower to + // the higher levels + for (int32 level = std::max(minLevel, 0L); level <= maxLevel;) { +printf(" level: %ld\n", level); + legendCount = fLegends.CountItems(); + + // get the first legend position/end + LegendInfo* info = fLegends.ItemAt(0); + float position = _LegendPosition(info->value, info->size.*sizeField, + (float)totalSize, scale);; + + int32 previousEnd = (int32)ceilf(position + info->size.*sizeField); + int32 previousLevel = info->legend->Level(); +printf(" first legend: pos: %f, size: %f\n", position, info->size.*sizeField); + + for (int32 i = 1; (info = fLegends.ItemAt(i)) != NULL; i++) { + float position = _LegendPosition(info->value, info->size.*sizeField, + (float)totalSize, scale);; +printf(" legend %ld: pos: %f, size: %f\n", i, position, info->size.*sizeField); + + if (position - spacing < previousEnd + && (previousLevel <= level + || info->legend->Level() <= level) + && std::max(previousLevel, info->legend->Level()) > 0) { + // The item intersects with the previous one -- remove the + // one at the higher level. + if (info->legend->Level() >= previousLevel) { + // This item is at the higher level -- remove it. + delete fLegends.RemoveItemAt(i); + i--; + continue; + } + + // The previous item is at the higher level -- remove it. + delete fLegends.RemoveItemAt(i - 1); + i--; + } + + if (i == 0 && position < 0) + position = 0; + previousEnd = (int32)ceilf(position + info->size.*sizeField); + previousLevel = info->legend->Level(); + } + + // repeat with the level as long as we've removed something + if (legendCount == fLegends.CountItems()) + level++; + } +} + + +LegendChartAxis::LegendChartAxis(ChartAxisLegendSource* legendSource, + ChartLegendRenderer* legendRenderer) + : + fLegendSource(legendSource), + fLegendRenderer(legendRenderer), + fLocation(CHART_AXIS_BOTTOM), + fRange(), + fFrame(), + fLegends(20, true), + fHorizontalSpacing(20), + fVerticalSpacing(10), + fLayoutValid(false) +{ +} + + +LegendChartAxis::~LegendChartAxis() +{ +} + + +void +LegendChartAxis::SetLocation(ChartAxisLocation location) +{ + if (location != fLocation) { + fLocation = location; + _InvalidateLayout(); + } +} + + +void +LegendChartAxis::SetRange(const ChartDataRange& range) +{ + if (range != fRange) { + fRange = range; + _InvalidateLayout(); + } +} + + +void +LegendChartAxis::SetFrame(BRect frame) +{ +printf("LegendChartAxis::SetFrame((%f, %f) - (%f, %f))\n", frame.left, frame.top, frame.right, frame.bottom); + if (frame != fFrame) { + fFrame = frame; + _InvalidateLayout(); + } +} + + +BSize +LegendChartAxis::PreferredSize(BView* view) +{ +// TODO: Implement for real! + BSize size = fLegendRenderer->MaximumLegendSize(view); + if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT) { + size.width += kChartLegendDistance; + size.height = std::max(size.height * 4, 100.0f); + } else { + size.width = std::max(size.width * 4, 100.0f); + size.height += kChartLegendDistance; + } + + return size; +} + + +void +LegendChartAxis::Render(BView* view, BRect updateRect) +{ +printf("LegendChartAxis::Render()\n"); + if (!_ValidateLayout(view)) + return; + + if (fLocation == CHART_AXIS_BOTTOM) { +printf(" rendering...\n"); + float totalSize = floorf(fFrame.Width()) + 1; + double rangeSize = fRange.max - fRange.min; + if (rangeSize == 0) + rangeSize = 1.0; + double scale = (double)totalSize / rangeSize; + float BSize::* sizeField = &BSize::width; + + // draw the ruler + float rulerLeft = fFrame.left; + float rulerTop = fFrame.top + kChartRulerDistance; + float rulerRight = fFrame.right; + float rulerBottom = rulerTop + kRulerSize; +printf(" ruler: (%f, %f) - (%f, %f)\n", rulerLeft, rulerTop, rulerRight, rulerBottom); + + rgb_color black = { 0, 0, 0, 255 }; + view->BeginLineArray(3 + fLegends.CountItems()); + view->AddLine(BPoint(rulerLeft, rulerTop), + BPoint(rulerLeft, rulerBottom), black); + view->AddLine(BPoint(rulerLeft, rulerBottom), + BPoint(rulerRight, rulerBottom), black); + view->AddLine(BPoint(rulerRight, rulerBottom), + BPoint(rulerRight, rulerTop), black); + + // marks + for (int32 i = 0; LegendInfo* info = fLegends.ItemAt(i); i++) { + float position = (info->value - fRange.min) * scale; + position += rulerLeft; +printf(" drawing mark at (%f, %f)\n", position, rulerBottom); + view->AddLine(BPoint(position, rulerBottom), + BPoint(position, rulerBottom + kRulerMarkSize), black); + } + view->EndLineArray(); + + // draw the legends + float legendTop = rulerBottom + kRulerMarkSize + kRulerLegendDistance; + + for (int32 i = 0; LegendInfo* info = fLegends.ItemAt(i); i++) { + float position = _LegendPosition(info->value, info->size.*sizeField, + (float)totalSize, scale);; +printf(" legend %ld: position: (%f, %f), size: (%f, %f)\n", i, position, legendTop, info->size.width, info->size.height); + + fLegendRenderer->RenderLegend(info->legend, view, + BPoint(rulerLeft + position, legendTop)); + } + } +} + + +void +LegendChartAxis::_InvalidateLayout() +{ + fLayoutValid = false; +} + + +bool +LegendChartAxis::_ValidateLayout(BView* view) +{ + if (fLayoutValid) + return true; +printf("LegendChartAxis::_ValidateLayout()\n"); + + fLegends.MakeEmpty(); + + int32 width = fFrame.IntegerWidth() + 1; + int32 height = fFrame.IntegerHeight() + 1; +printf(" width: %ld, height: %ld\n", width, height); + + fLegendRenderer->GetMinimumLegendSpacing(view, &fHorizontalSpacing, + &fVerticalSpacing); + + // estimate the maximum legend count we might need + int32 maxLegends; + if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT) + maxLegends = height / (10 + fVerticalSpacing); + else + maxLegends = width / (20 + fHorizontalSpacing); + +printf(" max %ld legends\n", maxLegends); + if (maxLegends == 0) +{ +printf(" too small for any legends!\n"); + return false; +} + + // get the legends + ChartLegend* legends[maxLegends]; + double values[maxLegends]; + + int32 legendCount = fLegendSource->GetAxisLegends(fRange, legends, values, + maxLegends); +printf(" got %ld legends\n", legendCount); + if (legendCount == 0) +{ +printf(" got no legends from source!\n"); + return false; +} + +printf(" range: %f - %f\n", fRange.min, fRange.max); + // create legend infos + for (int32 i = 0; i < legendCount; i++) { + ChartLegend* legend = legends[i]; + BSize size = fLegendRenderer->LegendSize(legend, view); + LegendInfo* info = new(std::nothrow) LegendInfo(legend, values[i], + size); +printf(" legend %ld: size: (%f, %f), value: %f\n", i, size.width, size.height, info->value); + if (info == NULL || !fLegends.AddItem(info)) { + // TODO: Report error! + delete info; + for (int32 k = i; k < legendCount; k++) + delete legends[k]; +printf(" failed to create legend info!\n"); + return false; + } + } + + if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT) + _FilterLegends(height, fVerticalSpacing, &BSize::height); + else + _FilterLegends(width, fHorizontalSpacing, &BSize::width); + + fLayoutValid = true; + return true; +} diff --git a/src/apps/debuganalyzer/gui/chart/LegendChartAxis.h b/src/apps/debuganalyzer/gui/chart/LegendChartAxis.h new file mode 100644 index 0000000000..796624cd81 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/LegendChartAxis.h @@ -0,0 +1,55 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef LEGEND_CHART_AXIS_H +#define LEGEND_CHART_AXIS_H + +#include + +#include "chart/ChartAxis.h" +#include "chart/ChartDataRange.h" + + +class ChartAxisLegendSource; +class ChartLegendRenderer; + + +class LegendChartAxis : public ChartAxis { +public: + LegendChartAxis( + ChartAxisLegendSource* legendSource, + ChartLegendRenderer* legendRenderer); + virtual ~LegendChartAxis(); + + virtual void SetLocation(ChartAxisLocation location); + virtual void SetRange(const ChartDataRange& range); + virtual void SetFrame(BRect frame); + virtual BSize PreferredSize(BView* view); + virtual void Render(BView* view, BRect updateRect); + +private: + struct LegendInfo; + typedef BObjectList LegendList; +private: + void _InvalidateLayout(); + bool _ValidateLayout(BView* view); + inline float _LegendPosition(double value, float legendSize, + float totalSize, double scale); + inline void _FilterLegends(int32 totalSize, int32 spacing, + float BSize::* sizeField); + +private: + ChartAxisLegendSource* fLegendSource; + ChartLegendRenderer* fLegendRenderer; + ChartAxisLocation fLocation; + ChartDataRange fRange; + BRect fFrame; + LegendList fLegends; + float fHorizontalSpacing; + float fVerticalSpacing; + bool fLayoutValid; +}; + + +#endif // LEGEND_CHART_AXIS_H diff --git a/src/apps/debuganalyzer/gui/chart/LineChartRenderer.cpp b/src/apps/debuganalyzer/gui/chart/LineChartRenderer.cpp new file mode 100644 index 0000000000..ae49a81d41 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/LineChartRenderer.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/LineChartRenderer.h" + +#include + +#include + +#include "chart/ChartDataSource.h" + + +// #pragma mark - DataSourceInfo + + +struct LineChartRenderer::DataSourceInfo { +public: + ChartDataSource* source; + double* samples; + int32 sampleCount; + bool samplesValid; + LineChartRendererDataSourceConfig config; + +public: + DataSourceInfo(ChartDataSource* source, + LineChartRendererDataSourceConfig* config); + ~DataSourceInfo(); + + bool UpdateSamples(const ChartDataRange& domain, + int32 sampleCount); +}; + + +LineChartRenderer::DataSourceInfo::DataSourceInfo(ChartDataSource* source, + LineChartRendererDataSourceConfig* config) + : + source(source), + samples(NULL), + sampleCount(0), + samplesValid(false) +{ + if (config != NULL) + this->config = *config; +} + + +LineChartRenderer::DataSourceInfo::~DataSourceInfo() +{ + delete[] samples; +} + + +bool +LineChartRenderer::DataSourceInfo::UpdateSamples(const ChartDataRange& domain, + int32 sampleCount) +{ + if (samplesValid) + return true; + + // check the sample count -- we might need to realloc the sample array + if (sampleCount != this->sampleCount) { + delete[] samples; + this->sampleCount = 0; + + samples = new(std::nothrow) double[sampleCount]; + if (samples == NULL) + return false; + + this->sampleCount = sampleCount; + } + + // get the new samples + source->GetSamples(domain.min, domain.max, samples, sampleCount); + + samplesValid = true; + return true; +} + + +// #pragma mark - LineChartRenderer + + +LineChartRenderer::LineChartRenderer() + : + fFrame(), + fDomain(), + fRange() +{ +} + + +LineChartRenderer::~LineChartRenderer() +{ +} + + +bool +LineChartRenderer::AddDataSource(ChartDataSource* dataSource, int32 index, + ChartRendererDataSourceConfig* config) +{ + DataSourceInfo* info = new(std::nothrow) DataSourceInfo(dataSource, + dynamic_cast(config)); + if (info == NULL) + return false; + + if (!fDataSources.AddItem(info, index)) { + delete info; + return false; + } + + return true; +} + + +void +LineChartRenderer::RemoveDataSource(ChartDataSource* dataSource) +{ + for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { + info->samplesValid = false; + if (info->source == dataSource) { + delete fDataSources.RemoveItemAt(i); + return; + } + } +} + + +void +LineChartRenderer::SetFrame(const BRect& frame) +{ + fFrame = frame; + _InvalidateSamples(); +} + + +void +LineChartRenderer::SetDomain(const ChartDataRange& domain) +{ + if (domain != fDomain) { + fDomain = domain; + _InvalidateSamples(); + } +} + + +void +LineChartRenderer::SetRange(const ChartDataRange& range) +{ + if (range != fRange) { + fRange = range; + _InvalidateSamples(); + } +} + + +void +LineChartRenderer::Render(BView* view, BRect updateRect) +{ + if (!updateRect.IsValid() || updateRect.left > fFrame.right + || fFrame.left > updateRect.right) { + return; + } + + if (fDomain.min >= fDomain.max || fRange.min >= fRange.max) + return; + + if (!_UpdateSamples()) + return; + + // get the range to draw (draw one more sample on each side) + int32 left = (int32)fFrame.left; + int32 first = (int32)updateRect.left - left - 1; + int32 last = (int32)updateRect.right - left + 1; + if (first < 0) + first = 0; + if (last > fFrame.IntegerWidth()) + last = fFrame.IntegerWidth(); + if (first > last) + return; + + double minRange = fRange.min; + double sampleRange = fRange.max - minRange; + if (sampleRange == 0) { + minRange = fRange.min - 0.5; + sampleRange = 1; + } + double scale = (double)fFrame.IntegerHeight() / sampleRange; + + // draw + for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { +printf("LineChartRenderer::Render(): %p\n", info); + view->SetHighColor(info->config.Color()); + + float bottom = fFrame.bottom; + view->MovePenTo(left + first, + bottom - ((info->samples[first] - minRange) * scale)); + + for (int32 i = first; i <= last; i++) { + view->StrokeLine(BPoint(float(left + i), + float(bottom - ((info->samples[i] - minRange) * scale)))); +//printf(" %f: %f (%f)\n", info->samples[i] - minRange, float(bottom - ((info->samples[i] - minRange) * scale)), bottom); + } + } +} + + +void +LineChartRenderer::_InvalidateSamples() +{ + for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) + info->samplesValid = false; +} + + +bool +LineChartRenderer::_UpdateSamples() +{ + int32 width = fFrame.IntegerWidth() + 1; + if (width <= 0) + return false; + + for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { + if (!info->UpdateSamples(fDomain, width)) + return false; + } + + return true; +} + + +// #pragma mark - LineChartRendererDataSourceConfig + + +LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig() +{ + fColor.red = 0; + fColor.green = 0; + fColor.blue = 0; + fColor.alpha = 255; +} + + +LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig( + const rgb_color& color) +{ + fColor = color; +} + + +LineChartRendererDataSourceConfig::~LineChartRendererDataSourceConfig() +{ +} + + +const rgb_color& +LineChartRendererDataSourceConfig::Color() const +{ + return fColor; +} + + +void +LineChartRendererDataSourceConfig::SetColor(const rgb_color& color) +{ + fColor = color; +} diff --git a/src/apps/debuganalyzer/gui/chart/LineChartRenderer.h b/src/apps/debuganalyzer/gui/chart/LineChartRenderer.h new file mode 100644 index 0000000000..242b13e7e3 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/LineChartRenderer.h @@ -0,0 +1,71 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef LINE_CHART_RENDERER_H +#define LINE_CHART_RENDERER_H + +#include +#include + +#include + +#include "chart/ChartDataRange.h" +#include "chart/ChartRenderer.h" + + +class BView; + + +class LineChartRenderer : public ChartRenderer { +public: + class DataSourceConfig; + +public: + LineChartRenderer(); + virtual ~LineChartRenderer(); + + virtual bool AddDataSource(ChartDataSource* dataSource, + int32 index, + ChartRendererDataSourceConfig* config); + virtual void RemoveDataSource(ChartDataSource* dataSource); + + virtual void SetFrame(const BRect& frame); + virtual void SetDomain(const ChartDataRange& domain); + virtual void SetRange(const ChartDataRange& range); + + virtual void Render(BView* view, BRect updateRect); + +private: + struct DataSourceInfo; + + typedef BObjectList DataSourceList; + +private: + void _InvalidateSamples(); + bool _UpdateSamples(); + +private: + DataSourceList fDataSources; + BRect fFrame; + ChartDataRange fDomain; + ChartDataRange fRange; +}; + + +class LineChartRendererDataSourceConfig : public ChartRendererDataSourceConfig { +public: + LineChartRendererDataSourceConfig(); + LineChartRendererDataSourceConfig( + const rgb_color& color); + virtual ~LineChartRendererDataSourceConfig(); + + const rgb_color& Color() const; + void SetColor(const rgb_color& color); + +private: + rgb_color fColor; +}; + + +#endif // LINE_CHART_RENDERER_H diff --git a/src/apps/debuganalyzer/gui/chart/StringChartLegend.cpp b/src/apps/debuganalyzer/gui/chart/StringChartLegend.cpp new file mode 100644 index 0000000000..088aaa4041 --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/StringChartLegend.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "chart/StringChartLegend.h" + +#include +#include + + +// #pragma mark - StringChartLegend + + +StringChartLegend::StringChartLegend(const char* string, int32 level) + : + ChartLegend(level), + fString(string) +{ +} + + +// #pragma mark - StringChartLegendRenderer + + +void +StringChartLegendRenderer::GetMinimumLegendSpacing(BView* view, + float* horizontal, float* vertical) +{ +// TODO: Implement for real! + *horizontal = 40; + *vertical = 20; +} + + +BSize +StringChartLegendRenderer::MaximumLegendSize(BView* view) +{ +// TODO: Implement for real! + return BSize(100, 20); +} + + +BSize +StringChartLegendRenderer::LegendSize(ChartLegend* _legend, BView* view) +{ + StringChartLegend* legend = dynamic_cast(_legend); + if (legend == NULL) + return BSize(); + +// TODO: It's a bit expensive to do that for each legend! +// TODO: Have a font per renderer? + BFont font; + view->GetFont(&font); + font_height fh; + font.GetHeight(&fh); + float fontHeight = ceilf(fh.ascent) + ceilf(fh.descent); + float width = ceilf(font.StringWidth(legend->String())); + + return BSize(width, fontHeight); +} + + +void +StringChartLegendRenderer::RenderLegend(ChartLegend* _legend, BView* view, + BPoint point) +{ + StringChartLegend* legend = dynamic_cast(_legend); + if (legend == NULL) + return; + + BFont font; + view->GetFont(&font); + font_height fh; + font.GetHeight(&fh); + point.y += fh.ascent; + + view->SetHighColor(rgb_color().set_to(0, 0, 0, 255)); + view->DrawString(legend->String(), point); +} diff --git a/src/apps/debuganalyzer/gui/chart/StringChartLegend.h b/src/apps/debuganalyzer/gui/chart/StringChartLegend.h new file mode 100644 index 0000000000..724399460a --- /dev/null +++ b/src/apps/debuganalyzer/gui/chart/StringChartLegend.h @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STRING_CHART_LEGEND_H +#define STRING_CHART_LEGEND_H + +#include + +#include "chart/ChartLegend.h" + + +class StringChartLegend : public ChartLegend { +public: + StringChartLegend(const char* string, + int32 level = 0); + + const char* String() const { return fString.String(); } + +private: + BString fString; +}; + + +class StringChartLegendRenderer : public ChartLegendRenderer { +public: + virtual void GetMinimumLegendSpacing(BView* view, + float* horizontal, float* vertical); + virtual BSize MaximumLegendSize(BView* view); + + virtual BSize LegendSize(ChartLegend* legend, + BView* view); + virtual void RenderLegend(ChartLegend* legend, BView* view, + BPoint point); +}; + + +#endif // STRING_CHART_LEGEND_H