Inkscape
Vector Graphics Editor
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages Concepts
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 <algorithm>
13#include <cstddef>
14#include <cairo.h>
15#include <cairomm/context.h>
16#include <cairomm/refptr.h>
17#include <cairomm/surface.h>
18#include <cstdlib>
19#include <fstream>
20#include <glibmm/miscutils.h>
21#include <glibmm/ustring.h>
22#include <gtkmm/image.h>
23#include <gtkmm/window.h>
24#include <random>
25#include <regex>
26#include <string>
27#include <utility>
28#include <vector>
29#include <glibmm/main.h>
30#include <gtkmm/aspectframe.h>
31#include <gtkmm/builder.h>
32#include <gtkmm/button.h>
33#include <gdkmm/clipboard.h>
34#include <gtkmm/label.h>
35#include <gtkmm/notebook.h>
36#include <gtkmm/textview.h>
37#include <gtkmm/window.h>
38#include <sigc++/adaptors/bind.h>
39
40#include "desktop.h"
41#include "display/cairo-utils.h"
42#include <sigc++/scoped_connection.h>
44#include "inkscape.h"
45#include "inkscape-window.h"
46#include "io/resource.h"
47#include "ui/builder-utils.h"
48#include "ui/builder-utils.h"
49#include "ui/svg-renderer.h"
50#include "ui/themes.h"
51#include "ui/util.h"
52
53// how long to show each about screen in seconds
54constexpr int SLIDESHOW_DELAY_sec = 10;
55
56using namespace Inkscape::IO;
57
58namespace Inkscape::UI::Dialog {
59namespace {
60
61class AboutWindow : public Gtk::Window {
62public:
63 AboutWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder): Gtk::Window(cobject) {
64 find_about_screens();
65 if (_about_screens.empty()) {
66 g_error("AboutWindow: Missing about screens.");
67 return;
68 }
69
70 _viewer1 = &get_widget<Gtk::Picture>(builder, "viewer1");
71 _viewer2 = &get_widget<Gtk::Picture>(builder, "viewer2");
72 _frame = &get_widget<Gtk::AspectFrame>(builder, "aspect-frame");
73 _footer = &get_widget<Gtk::Box>(builder, "dialog-footer");
74 }
75
76 void show_window() {
77 _refresh = Glib::signal_timeout().connect_seconds([this] {
78 transition();
79 return true;
81
82 // reset the stage
83 _viewer1->set_paintable({});
84 _viewer2->set_paintable({});
85 _about_index = 0;
86 _tick = false;
87 auto ctx = _viewer2->get_style_context();
88 ctx->remove_class("fade-out");
89 ctx->remove_class("fade-in");
90
91 present();
92 transition();
93 }
94
95private:
96 std::vector<std::string> _about_screens;
97 size_t _about_index = 0;
98 bool _tick = false;
99 Gtk::Box* _footer;
100 Glib::RefPtr<Gtk::CssProvider> _footer_style;
101 Glib::RefPtr<Glib::TimeoutSource> _timer;
102 Gtk::Picture *_viewer1;
103 Gtk::Picture *_viewer2;
104 sigc::scoped_connection _refresh;
105 Gtk::AspectFrame* _frame = nullptr;
106
107 void find_about_screens() {
108 auto path = Glib::build_filename(get_path_string(Resource::SYSTEM, Resource::SCREENS), "about");
110 if (_about_screens.empty()) {
111 g_warning("Error loading about screens SVGZs: no such documents in share/screen/about folder.");
112 // fall back
113 _about_screens.push_back(Resource::get_filename(Resource::SCREENS, "about.svg", true, false));
114 }
115 std::sort(_about_screens.begin(), _about_screens.end());
116 }
117
118 Cairo::RefPtr<Cairo::ImageSurface> load_next(Gtk::Picture *viewer, const Glib::ustring& fname, int device_scale) {
119 svg_renderer renderer(fname.c_str());
120 auto surface = renderer.render_surface(device_scale);
121 if (surface) {
122 auto width = renderer.get_width_px();
123 auto height = renderer.get_height_px();
124 _frame->property_ratio() = width / height;
125 viewer->set_size_request(width, height);
126 }
127 viewer->set_paintable(to_texture(surface));
128 return surface;
129 }
130
131 void set_footer_matching_color(Cairo::RefPtr<Cairo::ImageSurface> const &image)
132 {
133 if (!image) return;
134
135 auto scale = get_scale_factor();
136
137 // extract color from a strip at the bottom of the rendered about image
138 int width = image->get_width();
139 int height = 5 * scale;
140 int y = (image->get_height() - height) / scale;
141 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width, height);
142 cairo_surface_set_device_scale(surface->cobj(), scale, scale);
143 auto ctx = Cairo::Context::create(surface);
144 ctx->set_source(image, 0, -y);
145 ctx->paint();
146
147 // calculate footer color: light/dark depending on a theme
148 bool dark = INKSCAPE.themecontext->isCurrentThemeDark(this);
150
151 auto style_context = _footer->get_style_context();
152 _footer_style = Gtk::CssProvider::create();
153 _footer_style->load_from_data("box {background-color:" + foot.toString() + ";}");
154 if (_footer_style) style_context->remove_provider(_footer_style);
155 style_context->add_provider(_footer_style, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
156 }
157
158 // load next about screen
159 void transition() {
160 _tick = !_tick;
161 auto nv = _tick ? _viewer1 : _viewer2;
162 auto image = load_next(nv, _about_screens[_about_index++ % _about_screens.size()], get_scale_factor());
163
164 auto ctx = _viewer2->get_style_context();
165 if (_tick) {
166 ctx->add_class("fade-out");
167 ctx->remove_class("fade-in");
168 }
169 else {
170 ctx->remove_class("fade-out");
171 ctx->add_class("fade-in");
172 }
173
174 set_footer_matching_color(image);
175 }
176};
177
178void copy(Gtk::Button *button, Gtk::Label *label, Glib::ustring const &text)
179{
180 auto clipboard = Gdk::Display::get_default()->get_clipboard();
181 clipboard->set_text(text);
182 reveal_widget(button, false);
183 reveal_widget(label, true);
184 Glib::signal_timeout().connect_seconds(
185 sigc::track_object([=] { // disconnects on destruction
186 reveal_widget(button, true);
187 reveal_widget(label, false);
188 return false;
189 },
190 *button),
191 2);
192}
193
194} // namespace
195
196template <class Random>
197[[nodiscard]] static auto get_shuffled_lines(std::string const &filename, Random &&random)
198{
199 std::ifstream fn{Resource::get_filename(Resource::DOCS, filename.c_str())};
200 std::vector<std::string> lines;
201 std::size_t capacity = 0;
202 for (std::string line; getline(fn, line);) {
203 capacity += line.size() + 1;
204 lines.push_back(std::move(line));
205 }
206 std::shuffle(lines.begin(), lines.end(), random);
207 return std::pair{std::move(lines), capacity};
208}
209
211{
212 // Load builder file here
213 auto builder = create_builder("inkscape-about.glade");
214 auto window = &get_derived_widget<AboutWindow>(builder, "about-screen-window");
215 auto tabs = &get_widget<Gtk::Notebook>(builder, "tabs");
216 auto version = &get_widget<Gtk::Button> (builder, "version");
217 auto version_lbl = &get_widget<Gtk::Label> (builder, "version-label");
218 auto label = &get_widget<Gtk::Label> (builder, "version-copied");
219 auto debug_info = &get_widget<Gtk::Button> (builder, "debug-info");
220 auto label2 = &get_widget<Gtk::Label> (builder, "debug-info-copied");
221 auto copyright = &get_widget<Gtk::Label> (builder, "copyright");
222 auto authors = &get_widget<Gtk::TextView>(builder, "credits-authors");
223 auto translators = &get_widget<Gtk::TextView>(builder, "credits-translators");
224 auto license = &get_widget<Gtk::Label> (builder, "license-text");
225
226 auto text = Inkscape::inkscape_version();
227 version_lbl->set_label(text);
228 version->signal_clicked().connect(
229 sigc::bind(&copy, version, label, std::move(text)));
230
231 debug_info->signal_clicked().connect(
232 sigc::bind(&copy, version, label2, Inkscape::debug_info()));
233
234 copyright->set_label(
235 Glib::ustring::compose(copyright->get_label(), std::to_string(Inkscape::inkscape_build_year())));
236
237 std::random_device rd;
238 std::mt19937 g(rd());
239 auto const [authors_data, capacity] = get_shuffled_lines("AUTHORS", g);
240 std::string str_authors;
241 str_authors.reserve(capacity);
242 for (auto const &author : authors_data) {
243 str_authors.append(author).append(1, '\n');
244 }
245 authors->get_buffer()->set_text(str_authors.c_str());
246
247 auto const [translators_data, capacity2] = get_shuffled_lines("TRANSLATORS", g);
248 std::string str_translators;
249 str_translators.reserve(capacity2);
250 std::regex e("(.*?)(<.*|)");
251 for (auto const &translator : translators_data) {
252 str_translators.append(std::regex_replace(translator, e, "$1")).append(1, '\n');
253 }
254 translators->get_buffer()->set_text(str_translators.c_str());
255
256 std::ifstream fn(Resource::get_filename(Resource::DOCS, "LICENSE"));
257 std::string str((std::istreambuf_iterator<char>(fn)),
258 std::istreambuf_iterator<char>());
259 license->set_markup(str.c_str());
260
261 // Handle Esc to close the window
262 auto const controller = Gtk::EventControllerKey::create();
263 controller->signal_key_pressed().connect(
264 sigc::track_object([window] (unsigned keyval, unsigned, Gdk::ModifierType) {
265 if (keyval == GDK_KEY_Escape) {
266 window->close();
267 return true;
268 }
269 return false;
270 }, *window),
271 false);
272 window->add_controller(controller);
273
274 if (auto top = SP_ACTIVE_DESKTOP ? SP_ACTIVE_DESKTOP->getInkscapeWindow() : nullptr) {
275 window->set_transient_for(*top);
276 }
277 tabs->set_current_page(0);
278 window->show_window();
279
280 Gtk::manage(window); // will self-destruct
281}
282
283} // namespace Inkscape::UI::Dialog
284
285/*
286 Local Variables:
287 mode:c++
288 c-file-style:"stroustrup"
289 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
290 indent-tabs-mode:nil
291 fill-column:99
292 End:
293*/
294// 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:103
std::vector< std::string > _about_screens
Definition about.cpp:96
Gtk::Picture * _viewer1
Definition about.cpp:102
Gtk::Box * _footer
Definition about.cpp:99
constexpr int SLIDESHOW_DELAY_sec
Definition about.cpp:54
Gtk::AspectFrame * _frame
Definition about.cpp:105
Glib::RefPtr< Glib::TimeoutSource > _timer
Definition about.cpp:101
sigc::scoped_connection _refresh
Definition about.cpp:104
bool _tick
Definition about.cpp:98
size_t _about_index
Definition about.cpp:97
Glib::RefPtr< Gtk::CssProvider > _footer_style
Definition about.cpp:100
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:137
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:197
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:81