236 lines
7.2 KiB
C++
236 lines
7.2 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include "gtv-calc-header-bar.hxx"
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
#include <o3tl/unreachable.hxx>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
struct GtvCalcHeaderBarPrivateImpl
|
|
{
|
|
/// Stores size and content of a single row header.
|
|
struct Header
|
|
{
|
|
int m_nSize;
|
|
std::string m_aText;
|
|
Header(int nSize, std::string aText)
|
|
: m_nSize(nSize),
|
|
m_aText(std::move(aText))
|
|
{ }
|
|
};
|
|
|
|
std::vector<Header> m_aHeaders;
|
|
CalcHeaderType m_eType;
|
|
|
|
GtvCalcHeaderBarPrivateImpl()
|
|
: m_eType(CalcHeaderType::NONE)
|
|
{ }
|
|
};
|
|
|
|
struct GtvCalcHeaderBarPrivate
|
|
{
|
|
GtvCalcHeaderBarPrivateImpl* m_pImpl;
|
|
|
|
GtvCalcHeaderBarPrivateImpl* operator->()
|
|
{
|
|
return m_pImpl;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#if defined __clang__
|
|
#if __has_warning("-Wdeprecated-volatile")
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-volatile"
|
|
#endif
|
|
#endif
|
|
G_DEFINE_TYPE_WITH_PRIVATE(GtvCalcHeaderBar, gtv_calc_header_bar, GTK_TYPE_DRAWING_AREA);
|
|
#if defined __clang__
|
|
#if __has_warning("-Wdeprecated-volatile")
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
#endif
|
|
|
|
const int ROW_HEADER_WIDTH = 50;
|
|
const int COLUMN_HEADER_HEIGHT = 20;
|
|
|
|
static GtvCalcHeaderBarPrivate&
|
|
getPrivate(GtvCalcHeaderBar* headerbar)
|
|
{
|
|
return *static_cast<GtvCalcHeaderBarPrivate*>(gtv_calc_header_bar_get_instance_private(headerbar));
|
|
}
|
|
|
|
static void
|
|
gtv_calc_header_bar_init(GtvCalcHeaderBar* bar)
|
|
{
|
|
GtvCalcHeaderBarPrivate& priv = getPrivate(bar);
|
|
priv.m_pImpl = new GtvCalcHeaderBarPrivateImpl();
|
|
}
|
|
|
|
static void
|
|
gtv_calc_header_bar_finalize(GObject* object)
|
|
{
|
|
GtvCalcHeaderBarPrivate& priv = getPrivate(GTV_CALC_HEADER_BAR(object));
|
|
|
|
delete priv.m_pImpl;
|
|
priv.m_pImpl = nullptr;
|
|
|
|
G_OBJECT_CLASS (gtv_calc_header_bar_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void gtv_calc_header_bar_draw_text(cairo_t* pCairo, const GdkRectangle& rRectangle, const std::string& rText)
|
|
{
|
|
cairo_text_extents_t extents;
|
|
cairo_text_extents(pCairo, rText.c_str(), &extents);
|
|
// Cairo reference point for text is the bottom left corner.
|
|
cairo_move_to(pCairo, rRectangle.x + rRectangle.width / 2 - extents.width / 2, rRectangle.y + rRectangle.height / 2 + extents.height / 2);
|
|
cairo_show_text(pCairo, rText.c_str());
|
|
}
|
|
|
|
static bool gtv_calc_header_bar_draw_impl(GtkWidget* pWidget, cairo_t* pCairo)
|
|
{
|
|
GtvCalcHeaderBar* self = GTV_CALC_HEADER_BAR(pWidget);
|
|
GtvCalcHeaderBarPrivate& priv = getPrivate(GTV_CALC_HEADER_BAR(self));
|
|
cairo_set_source_rgb(pCairo, 0, 0, 0);
|
|
|
|
int nPrevious = 0;
|
|
for (const GtvCalcHeaderBarPrivateImpl::Header& rHeader : priv->m_aHeaders)
|
|
{
|
|
GdkRectangle aRectangle;
|
|
if (priv->m_eType == CalcHeaderType::ROW)
|
|
{
|
|
aRectangle.x = 0;
|
|
aRectangle.y = nPrevious;
|
|
aRectangle.width = ROW_HEADER_WIDTH - 1;
|
|
aRectangle.height = rHeader.m_nSize - nPrevious;
|
|
// Left line.
|
|
cairo_rectangle(pCairo, aRectangle.x, aRectangle.y, 1, aRectangle.height);
|
|
cairo_fill(pCairo);
|
|
// Bottom line.
|
|
cairo_rectangle(pCairo, aRectangle.x, aRectangle.y + aRectangle.height, aRectangle.width, 1);
|
|
cairo_fill(pCairo);
|
|
// Right line.
|
|
cairo_rectangle(pCairo, aRectangle.width, aRectangle.y, 1, aRectangle.height);
|
|
cairo_fill(pCairo);
|
|
}
|
|
else if (priv->m_eType == CalcHeaderType::COLUMN)
|
|
{
|
|
aRectangle.x = nPrevious;
|
|
aRectangle.y = 0;
|
|
aRectangle.width = rHeader.m_nSize - nPrevious;
|
|
aRectangle.height = COLUMN_HEADER_HEIGHT - 1;
|
|
// Top line.
|
|
cairo_rectangle(pCairo, aRectangle.x, aRectangle.y, aRectangle.width, 1);
|
|
cairo_fill(pCairo);
|
|
// Right line.
|
|
cairo_rectangle(pCairo, aRectangle.x + aRectangle.width , aRectangle.y, 1, aRectangle.height);
|
|
cairo_fill(pCairo);
|
|
// Bottom line.
|
|
cairo_rectangle(pCairo, aRectangle.x, aRectangle.height, aRectangle.width, 1);
|
|
cairo_fill(pCairo);
|
|
}
|
|
else
|
|
{
|
|
O3TL_UNREACHABLE; // should never happen
|
|
}
|
|
|
|
gtv_calc_header_bar_draw_text(pCairo, aRectangle, rHeader.m_aText);
|
|
nPrevious = rHeader.m_nSize;
|
|
if (rHeader.m_nSize > self->m_nSizePixel)
|
|
break;
|
|
}
|
|
|
|
if (priv->m_aHeaders.empty() && priv->m_eType == CalcHeaderType::CORNER)
|
|
{
|
|
GdkRectangle aRectangle;
|
|
aRectangle.x = 0;
|
|
aRectangle.y = 0;
|
|
aRectangle.width = ROW_HEADER_WIDTH - 1;
|
|
aRectangle.height = COLUMN_HEADER_HEIGHT - 1;
|
|
cairo_rectangle(pCairo, aRectangle.x, aRectangle.y, aRectangle.width, aRectangle.height);
|
|
cairo_stroke(pCairo);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean
|
|
gtv_calc_header_bar_draw(GtkWidget* bar, cairo_t* pCairo)
|
|
{
|
|
return gtv_calc_header_bar_draw_impl(bar, pCairo);
|
|
}
|
|
|
|
static void
|
|
gtv_calc_header_bar_class_init(GtvCalcHeaderBarClass* klass)
|
|
{
|
|
GTK_WIDGET_CLASS(klass)->draw = gtv_calc_header_bar_draw;
|
|
G_OBJECT_CLASS(klass)->finalize = gtv_calc_header_bar_finalize;
|
|
}
|
|
|
|
void gtv_calc_header_bar_configure(GtvCalcHeaderBar* bar, const boost::property_tree::ptree* values)
|
|
{
|
|
GtvCalcHeaderBarPrivate& priv = getPrivate(bar);
|
|
priv->m_aHeaders.clear();
|
|
|
|
if (values)
|
|
{
|
|
boost::property_tree::ptree val = *values;
|
|
try
|
|
{
|
|
for (const boost::property_tree::ptree::value_type& rValue : val)
|
|
{
|
|
int nSize = std::round(std::atof(rValue.second.get<std::string>("size").c_str()));
|
|
if (nSize >= bar->m_nPositionPixel)
|
|
{
|
|
const int nScrolledSize = nSize - bar->m_nPositionPixel;
|
|
GtvCalcHeaderBarPrivateImpl::Header aHeader(nScrolledSize, rValue.second.get<std::string>("text"));
|
|
priv->m_aHeaders.push_back(aHeader);
|
|
}
|
|
}
|
|
}
|
|
catch (boost::property_tree::ptree_bad_path& rException)
|
|
{
|
|
std::cerr << "gtv_calc_header_bar_configure: " << rException.what() << std::endl;
|
|
}
|
|
}
|
|
gtk_widget_show(GTK_WIDGET(bar));
|
|
gtk_widget_queue_draw(GTK_WIDGET(bar));
|
|
}
|
|
|
|
void
|
|
gtv_calc_header_bar_set_type_and_width(GtvCalcHeaderBar* bar, CalcHeaderType eType)
|
|
{
|
|
// TODO: Install type property for this class
|
|
GtvCalcHeaderBarPrivate& priv = getPrivate(bar);
|
|
priv->m_eType = eType;
|
|
|
|
if (eType == CalcHeaderType::ROW)
|
|
gtk_widget_set_size_request(GTK_WIDGET(bar), ROW_HEADER_WIDTH, -1);
|
|
else if (eType == CalcHeaderType::COLUMN)
|
|
gtk_widget_set_size_request(GTK_WIDGET(bar), -1, COLUMN_HEADER_HEIGHT);
|
|
}
|
|
|
|
GtkWidget*
|
|
gtv_calc_header_bar_new()
|
|
{
|
|
return GTK_WIDGET(g_object_new(GTV_TYPE_CALC_HEADER_BAR,
|
|
nullptr));
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|