Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
about.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
10#include "about.h"
11
12#include <fstream>
13#include <gtkmm/aspectframe.h>
14#include <gtkmm/binlayout.h>
15#include <gtkmm/cssprovider.h>
16#include <gtkmm/eventcontrollerkey.h>
17#include <gtkmm/picture.h>
18#include <gtkmm/textview.h>
19#include <random>
20#include <regex>
21
22#include "desktop.h"
23#include "display/cairo-utils.h"
25#include "inkscape.h"
26#include "inkscape-window.h"
27#include "io/resource.h"
28#include "ui/builder-utils.h"
29#include "ui/svg-renderer.h"
30#include "ui/themes.h"
31#include "ui/util.h"
32
33// how long to show each about screen in seconds
34constexpr int SLIDESHOW_DELAY_sec = 10;
35
36using namespace Inkscape::IO;
37
38namespace Inkscape::UI::Dialog {
39namespace {
40
41class AboutWindow : public Gtk::Window {
42public:
43 AboutWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder): Gtk::Window(cobject) {
44 find_about_screens();
45 if (_about_screens.empty()) {
46 g_error("AboutWindow: Missing about screens.");
47 return;
48 }
49
50 _viewer1 = &get_widget<Gtk::Picture>(builder, "viewer1");
51 _viewer1->set_layout_manager(Gtk::BinLayout::create());
52 _viewer2 = &get_widget<Gtk::Picture>(builder, "viewer2");
53 _viewer2->set_layout_manager(Gtk::BinLayout::create());
54 _frame = &get_widget<Gtk::AspectFrame>(builder, "aspect-frame");
55 _footer = &get_widget<Gtk::Box>(builder, "dialog-footer");
56 }
57
58 void show_window() {
59 _refresh = Glib::signal_timeout().connect_seconds([this] {
60 transition();
61 return true;
63
64 // reset the stage
65 _viewer1->set_paintable({});
66 _viewer2->set_paintable({});
67 _about_index = 0;
68 _tick = false;
69 auto ctx = _viewer2->get_style_context();
70 ctx->remove_class("fade-out");
71 ctx->remove_class("fade-in");
72
73 present();
74 transition();
75 }
76
77private:
78 std::vector<std::string> _about_screens;
79 size_t _about_index = 0;
80 bool _tick = false;
81 Gtk::Box* _footer;
82 Glib::RefPtr<Gtk::CssProvider> _footer_style;
83 Glib::RefPtr<Glib::TimeoutSource> _timer;
84 Gtk::Picture *_viewer1;
85 Gtk::Picture *_viewer2;
86 sigc::scoped_connection _refresh;
87 Gtk::AspectFrame* _frame = nullptr;
88
89 void find_about_screens() {
90 auto path = Glib::build_filename(get_path_string(Resource::SYSTEM, Resource::SCREENS), "about");
92 if (_about_screens.empty()) {
93 g_warning("Error loading about screens SVGZs: no such documents in share/screen/about folder.");
94 // fall back
95 _about_screens.push_back(Resource::get_filename(Resource::SCREENS, "about.svg", true, false));
96 }
97 std::sort(_about_screens.begin(), _about_screens.end());
98 }
99
100 Cairo::RefPtr<Cairo::ImageSurface> load_next(Gtk::Picture *viewer, const Glib::ustring& fname, int device_scale) {
101 svg_renderer renderer(fname.c_str());
102 auto surface = renderer.render_surface(device_scale);
103 if (surface) {
104 auto width = renderer.get_width_px();
105 auto height = renderer.get_height_px();
106 _frame->property_ratio() = width / height;
107 viewer->set_size_request(width, height);
108 }
109 viewer->set_paintable(to_texture(surface));
110 return surface;
111 }
112
113 void set_footer_matching_color(Cairo::RefPtr<Cairo::ImageSurface> const &image)
114 {
115 if (!image) return;
116
117 auto scale = get_scale_factor();
118
119 // extract color from a strip at the bottom of the rendered about image
120 int width = image->get_width();
121 int height = 5 * scale;
122 int y = (image->get_height() - height) / scale;
123 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width, height);
124 cairo_surface_set_device_scale(surface->cobj(), scale, scale);
125 auto ctx = Cairo::Context::create(surface);
126 ctx->set_source(image, 0, -y);
127 ctx->paint();
128
129 // calculate footer color: light/dark depending on a theme
130 bool dark = INKSCAPE.themecontext->isCurrentThemeDark(this);
132
133 auto style_context = _footer->get_style_context();
134 _footer_style = Gtk::CssProvider::create();
135 _footer_style->load_from_data("box {background-color:" + foot.toString() + ";}");
136 if (_footer_style) style_context->remove_provider(_footer_style);
137 style_context->add_provider(_footer_style, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
138 }
139
140 // load next about screen
141 void transition() {
142 _tick = !_tick;
143 auto nv = _tick ? _viewer1 : _viewer2;
144 auto image = load_next(nv, _about_screens[_about_index++ % _about_screens.size()], get_scale_factor());
145
146 auto ctx = _viewer2->get_style_context();
147 if (_tick) {
148 ctx->add_class("fade-out");
149 ctx->remove_class("fade-in");
150 }
151 else {
152 ctx->remove_class("fade-out");
153 ctx->add_class("fade-in");
154 }
155
156 set_footer_matching_color(image);
157 }
158};
159
160void copy(Gtk::Button *button, Gtk::Label *label, Glib::ustring const &text)
161{
162 auto clipboard = Gdk::Display::get_default()->get_clipboard();
163 clipboard->set_text(text);
164 reveal_widget(button, false);
165 reveal_widget(label, true);
166 Glib::signal_timeout().connect_seconds(
167 sigc::track_object([=] { // disconnects on destruction
168 reveal_widget(button, true);
169 reveal_widget(label, false);
170 return false;
171 },
172 *button),
173 2);
174}
175
176} // namespace
177
178template <class Random>
179[[nodiscard]] static auto get_shuffled_lines(std::string const &filename, Random &&random)
180{
181 std::ifstream fn{Resource::get_filename(Resource::DOCS, filename.c_str())};
182 std::vector<std::string> lines;
183 std::size_t capacity = 0;
184 for (std::string line; getline(fn, line);) {
185 capacity += line.size() + 1;
186 lines.push_back(std::move(line));
187 }
188 std::shuffle(lines.begin(), lines.end(), random);
189 return std::pair{std::move(lines), capacity};
190}
191
193{
194 // Load builder file here
195 auto builder = create_builder("inkscape-about.glade");
196 auto window = &get_derived_widget<AboutWindow>(builder, "about-screen-window");
197 auto tabs = &get_widget<Gtk::Notebook>(builder, "tabs");
198 auto version = &get_widget<Gtk::Button> (builder, "version");
199 auto version_lbl = &get_widget<Gtk::Label> (builder, "version-label");
200 auto label = &get_widget<Gtk::Label> (builder, "version-copied");
201 auto debug_info = &get_widget<Gtk::Button> (builder, "debug-info");
202 auto label2 = &get_widget<Gtk::Label> (builder, "debug-info-copied");
203 auto copyright = &get_widget<Gtk::Label> (builder, "copyright");
204 auto authors = &get_widget<Gtk::TextView>(builder, "credits-authors");
205 auto translators = &get_widget<Gtk::TextView>(builder, "credits-translators");
206 auto license = &get_widget<Gtk::Label> (builder, "license-text");
207
208 auto text = Inkscape::inkscape_version();
209 version_lbl->set_label(text);
210 version->signal_clicked().connect(
211 sigc::bind(&copy, version, label, std::move(text)));
212
213 debug_info->signal_clicked().connect(
214 sigc::bind(&copy, version, label2, Inkscape::debug_info()));
215
216 copyright->set_label(
217 Glib::ustring::compose(copyright->get_label(), std::to_string(Inkscape::inkscape_build_year())));
218
219 std::random_device rd;
220 std::mt19937 g(rd());
221 auto const [authors_data, capacity] = get_shuffled_lines("AUTHORS", g);
222 std::string str_authors;
223 str_authors.reserve(capacity);
224 for (auto const &author : authors_data) {
225 str_authors.append(author).append(1, '\n');
226 }
227 authors->get_buffer()->set_text(str_authors.c_str());
228
229 auto const [translators_data, capacity2] = get_shuffled_lines("TRANSLATORS", g);
230 std::string str_translators;
231 str_translators.reserve(capacity2);
232 std::regex e("(.*?)(<.*|)");
233 for (auto const &translator : translators_data) {
234 str_translators.append(std::regex_replace(translator, e, "$1")).append(1, '\n');
235 }
236 translators->get_buffer()->set_text(str_translators.c_str());
237
238 std::ifstream fn(Resource::get_filename(Resource::DOCS, "LICENSE"));
239 std::string str((std::istreambuf_iterator<char>(fn)),
240 std::istreambuf_iterator<char>());
241 license->set_markup(str.c_str());
242
243 // Handle Esc to close the window
244 auto const controller = Gtk::EventControllerKey::create();
245 controller->signal_key_pressed().connect(
246 sigc::track_object([window] (unsigned keyval, unsigned, Gdk::ModifierType) {
247 if (keyval == GDK_KEY_Escape) {
248 window->close();
249 return true;
250 }
251 return false;
252 }, *window),
253 false);
254 window->add_controller(controller);
255
256 if (auto top = SP_ACTIVE_DESKTOP ? SP_ACTIVE_DESKTOP->getInkscapeWindow() : nullptr) {
257 window->set_transient_for(*top);
258 }
259 tabs->set_current_page(0);
260 window->show_window();
261
262 Gtk::manage(window); // will self-destruct
263}
264
265} // namespace Inkscape::UI::Dialog
266
267/*
268 Local Variables:
269 mode:c++
270 c-file-style:"stroustrup"
271 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
272 indent-tabs-mode:nil
273 fill-column:99
274 End:
275*/
276// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
Gtk::Picture * _viewer2
Definition about.cpp:85
std::vector< std::string > _about_screens
Definition about.cpp:78
Gtk::Picture * _viewer1
Definition about.cpp:84
Gtk::Box * _footer
Definition about.cpp:81
constexpr int SLIDESHOW_DELAY_sec
Definition about.cpp:34
Gtk::AspectFrame * _frame
Definition about.cpp:87
Glib::RefPtr< Glib::TimeoutSource > _timer
Definition about.cpp:83
sigc::scoped_connection _refresh
Definition about.cpp:86
bool _tick
Definition about.cpp:80
size_t _about_index
Definition about.cpp:79
Glib::RefPtr< Gtk::CssProvider > _footer_style
Definition about.cpp:82
A dialog for the about screen.
Gtk builder utilities.
Colors::Color ink_cairo_surface_average_color(cairo_surface_t *surface, cairo_surface_t *mask)
Get the average color from the given surface.
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:124
A labelled text box, with spin buttons and optional icon, for entering arbitrary number values.
Definition random.h:26
RectangularCluster rd
Editable view implementation.
std::unique_ptr< Magick::Image > image
Consolidates version info for Inkscape, its various dependencies and the OS we're running on.
Inkscape - An SVG editor.
Glib::ustring label
void copy(InkscapeApplication *app)
Color make_theme_color(Color const &orig, bool dark)
Make a themed dark or light color based on a previous shade, returns RGB color.
Definition utils.cpp:148
std::string get_filename(Type type, char const *filename, bool localized, bool silent)
Definition resource.cpp:170
void get_filenames_from_path(std::vector< std::string > &files, std::string const &path, std::vector< const char * > const &extensions, std::vector< const char * > const &exclusions)
Definition resource.cpp:331
Low-level IO code.
Dialog code.
Definition desktop.h:117
static auto get_shuffled_lines(std::string const &filename, Random &&random)
Definition about.cpp:179
static constexpr int height
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
static void append(std::vector< T > &target, std::vector< T > &&source)
unsigned short int inkscape_build_year()
Return build year as 4 digit.
std::string debug_info()
Return full debug info.
std::string inkscape_version()
Return Inkscape version string.
Inkscape::IO::Resource - simple resource API.
double width
Gtk <themes> helper code.
Glib::RefPtr< Gtk::Builder > builder
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.
Definition util.cpp:495
void reveal_widget(Gtk::Widget *widget, bool show)
Show widget, if the widget has a Gtk::Reveal parent, reveal instead.
Definition util.cpp:68