1 /*
2     This file is part of darktable,
3     Copyright (C) 2011-2020 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "common/darktable.h"
20 #include "common/debug.h"
21 #include "common/file_location.h"
22 #include "common/image_cache.h"
23 #include "control/conf.h"
24 #include "control/control.h"
25 #include "develop/develop.h"
26 #include "dtgtk/thumbtable.h"
27 #include "gui/gtk.h"
28 #include "libs/lib.h"
29 #include "libs/lib_api.h"
30 #ifdef GDK_WINDOWING_QUARTZ
31 #include "osx/osx.h"
32 #endif
33 
34 #include <librsvg/rsvg.h>
35 // ugh, ugly hack. why do people break stuff all the time?
36 #ifndef RSVG_CAIRO_H
37 #include <librsvg/rsvg-cairo.h>
38 #endif
39 
40 DT_MODULE(1)
41 
42 
43 typedef struct dt_lib_darktable_t
44 {
45   // logo
46   cairo_surface_t *image;
47   guint8 *image_buffer;
48   int image_width, image_height;
49   // text with logo font
50   cairo_surface_t *text;
51   int text_width, text_height;
52 } dt_lib_darktable_t;
53 
54 
55 /* expose function for darktable module */
56 static gboolean _lib_darktable_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data);
57 /* button press callback */
58 static gboolean _lib_darktable_button_press_callback(GtkWidget *widget, GdkEventButton *event,
59                                                      gpointer user_data);
60 /* show the about dialog */
61 static void _lib_darktable_show_about_dialog();
62 
name(dt_lib_module_t * self)63 const char *name(dt_lib_module_t *self)
64 {
65   return _("darktable");
66 }
67 
views(dt_lib_module_t * self)68 const char **views(dt_lib_module_t *self)
69 {
70   static const char *v[] = {"*", NULL};
71   return v;
72 }
73 
container(dt_lib_module_t * self)74 uint32_t container(dt_lib_module_t *self)
75 {
76   return DT_UI_CONTAINER_PANEL_TOP_LEFT;
77 }
78 
expandable(dt_lib_module_t * self)79 int expandable(dt_lib_module_t *self)
80 {
81   return 0;
82 }
83 
position()84 int position()
85 {
86   return 1001;
87 }
88 
gui_init(dt_lib_module_t * self)89 void gui_init(dt_lib_module_t *self)
90 {
91   /* initialize ui widgets */
92   dt_lib_darktable_t *d = (dt_lib_darktable_t *)g_malloc0(sizeof(dt_lib_darktable_t));
93   self->data = (void *)d;
94 
95   /* create drawing area */
96   self->widget = gtk_event_box_new();
97 
98   /* connect callbacks */
99   g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(_lib_darktable_draw_callback), self);
100   g_signal_connect(G_OBJECT(self->widget), "button-press-event",
101                    G_CALLBACK(_lib_darktable_button_press_callback), self);
102 
103   /* create a cairo surface of dt icon */
104 
105   // first we try the SVG
106   d->image = dt_util_get_logo(DT_PIXEL_APPLY_DPI(-1.0));
107   if(d->image)
108     d->image_buffer = cairo_image_surface_get_data(d->image);
109   else
110   {
111     // let's fall back to the PNG
112     char *logo;
113     char datadir[PATH_MAX] = { 0 };
114 
115     dt_loc_get_datadir(datadir, sizeof(datadir));
116     const dt_logo_season_t season = dt_util_get_logo_season();
117     if(season != DT_LOGO_SEASON_NONE)
118       logo = g_strdup_printf("idbutton-%d.png", (int)season);
119     else
120       logo = g_strdup("idbutton.png");
121     char *filename = g_build_filename(datadir, "pixmaps", logo, NULL);
122 
123     cairo_surface_t *surface = cairo_image_surface_create_from_png(filename);
124     g_free(logo);
125     if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
126     {
127       fprintf(stderr, "warning: can't load darktable logo from PNG file `%s'\n", filename);
128       goto done;
129     }
130     const int png_width = cairo_image_surface_get_width(surface),
131               png_height = cairo_image_surface_get_height(surface);
132 
133     // blow up the PNG. Ugly, but at least it has the correct size afterwards :-/
134     const int width = DT_PIXEL_APPLY_DPI(png_width) * darktable.gui->ppd,
135               height = DT_PIXEL_APPLY_DPI(png_height) * darktable.gui->ppd;
136     const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
137 
138     d->image_buffer = (guint8 *)calloc((size_t)stride * height, sizeof(guint8));
139     d->image
140         = dt_cairo_image_surface_create_for_data(d->image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
141     if(cairo_surface_status(d->image) != CAIRO_STATUS_SUCCESS)
142     {
143       fprintf(stderr, "warning: can't load darktable logo from PNG file `%s'\n", filename);
144       free(d->image_buffer);
145       d->image_buffer = NULL;
146       cairo_surface_destroy(d->image);
147       d->image = NULL;
148       goto done;
149     }
150 
151     cairo_t *cr = cairo_create(d->image);
152     cairo_rectangle(cr, 0, 0, width, height);
153     cairo_scale(cr, darktable.gui->dpi_factor, darktable.gui->dpi_factor);
154     cairo_set_source_surface(cr, surface, 0, 0);
155     cairo_fill(cr);
156     cairo_destroy(cr);
157     cairo_surface_flush(d->image);
158 
159 done:
160     cairo_surface_destroy(surface);
161     g_free(filename);
162   }
163 
164   d->image_width = d->image ? dt_cairo_image_surface_get_width(d->image) : 0;
165   d->image_height = d->image ? dt_cairo_image_surface_get_height(d->image) : 0;
166 
167   /* try to load program name as svg */
168   d->text = dt_util_get_logo_text(DT_PIXEL_APPLY_DPI(-1.0));
169   /* no png fallback, we'll use text */
170   d->text_width = d->text ? dt_cairo_image_surface_get_width(d->text) : 0;
171   d->text_height = d->text ? dt_cairo_image_surface_get_height(d->text) : 0;
172 
173   /* set size of drawing area */
174   gtk_widget_set_size_request(self->widget, d->image_width + (int)DT_PIXEL_APPLY_DPI(180),
175                               d->image_height + (int)DT_PIXEL_APPLY_DPI(8));
176 }
177 
gui_cleanup(dt_lib_module_t * self)178 void gui_cleanup(dt_lib_module_t *self)
179 {
180   dt_lib_darktable_t *d = (dt_lib_darktable_t *)self->data;
181   cairo_surface_destroy(d->image);
182   free(d->image_buffer);
183 
184   // don't leak mem via text logo surface data
185   guint8 *text_img_buffer = NULL;
186   if(d->text)
187     text_img_buffer = cairo_image_surface_get_data(d->text);
188   cairo_surface_destroy(d->text);
189   free(text_img_buffer);
190   g_free(self->data);
191   self->data = NULL;
192 }
193 
194 
195 
_lib_darktable_draw_callback(GtkWidget * widget,cairo_t * cr,gpointer user_data)196 static gboolean _lib_darktable_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data)
197 {
198   dt_lib_module_t *self = (dt_lib_module_t *)user_data;
199   dt_lib_darktable_t *d = (dt_lib_darktable_t *)self->data;
200 
201   GtkStyleContext *context = gtk_widget_get_style_context(widget);
202 
203   GtkAllocation allocation;
204   gtk_widget_get_allocation(widget, &allocation);
205   gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
206 
207   // Get the normal foreground color from the CSS stylesheet
208   GdkRGBA *tmpcolor;
209   gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, "color", &tmpcolor, NULL);
210 
211   GtkStateFlags state = gtk_widget_get_state_flags(widget);
212 
213   PangoFontDescription *font_desc = NULL;
214   gtk_style_context_get(context, state, "font", &font_desc, NULL);
215 
216   /* paint icon image */
217   if(d->image)
218   {
219     cairo_set_source_surface(cr, d->image, 0, (int)DT_PIXEL_APPLY_DPI(7));
220     cairo_rectangle(cr, 0, 0, d->image_width + (int)DT_PIXEL_APPLY_DPI(8),
221                     d->image_height + (int)DT_PIXEL_APPLY_DPI(8));
222     cairo_fill(cr);
223   }
224 
225   /* create a pango layout and print fancy name/version string */
226   PangoLayout *layout;
227   layout = gtk_widget_create_pango_layout(widget, NULL);
228 
229   /* try to use logo text in svg */
230   if(d->text)
231   {
232     cairo_set_source_surface(cr, d->text, d->image_width + (int)DT_PIXEL_APPLY_DPI(5),
233                              (int)DT_PIXEL_APPLY_DPI(12));
234     cairo_rectangle(cr, 0, 0, d->image_width + d->text_width + (int)DT_PIXEL_APPLY_DPI(11),
235                     d->text_height + (int)DT_PIXEL_APPLY_DPI(13));
236     cairo_fill(cr);
237   }
238   else
239   {
240     /* fallback using normal text */
241     pango_font_description_set_weight(font_desc, PANGO_WEIGHT_BOLD);
242     pango_font_description_set_absolute_size(font_desc, DT_PIXEL_APPLY_DPI(25) * PANGO_SCALE);
243 
244     pango_layout_set_font_description(layout, font_desc);
245 
246     pango_layout_set_text(layout, PACKAGE_NAME, -1);
247     cairo_set_source_rgba(cr, tmpcolor->red, tmpcolor->green, tmpcolor->blue, 0.7);
248     cairo_move_to(cr, d->image_width + DT_PIXEL_APPLY_DPI(3.0), DT_PIXEL_APPLY_DPI(5.0));
249     pango_cairo_show_layout(cr, layout);
250   }
251 
252   /* print version */
253   pango_font_description_set_absolute_size(font_desc, DT_PIXEL_APPLY_DPI(10) * PANGO_SCALE);
254   pango_layout_set_font_description(layout, font_desc);
255   pango_layout_set_text(layout, darktable_package_version, -1);
256   cairo_move_to(cr, d->image_width + DT_PIXEL_APPLY_DPI(4.0), DT_PIXEL_APPLY_DPI(32.0));
257   cairo_set_source_rgba(cr, tmpcolor->red, tmpcolor->green, tmpcolor->blue, 0.3);
258   pango_cairo_show_layout(cr, layout);
259 
260   /* cleanup */
261   gdk_rgba_free(tmpcolor);
262   g_object_unref(layout);
263   pango_font_description_free(font_desc);
264 
265   return TRUE;
266 }
267 
_lib_darktable_button_press_callback(GtkWidget * widget,GdkEventButton * event,gpointer user_data)268 static gboolean _lib_darktable_button_press_callback(GtkWidget *widget, GdkEventButton *event,
269                                                      gpointer user_data)
270 {
271   /* show about box */
272   _lib_darktable_show_about_dialog();
273   return TRUE;
274 }
275 
_lib_darktable_show_about_dialog()276 static void _lib_darktable_show_about_dialog()
277 {
278   GtkWidget *dialog = gtk_about_dialog_new();
279   gtk_widget_set_name (dialog, "about_dialog");
280 #ifdef GDK_WINDOWING_QUARTZ
281   dt_osx_disallow_fullscreen(dialog);
282 #endif
283   gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME);
284   gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), darktable_package_version);
285   char *copyright = g_strdup_printf(_("copyright (c) the authors 2009-%s"), darktable_last_commit_year);
286   gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
287   g_free(copyright);
288   gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
289                                 _("organize and develop images from digital cameras"));
290   gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "https://www.darktable.org/");
291   gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), "website");
292   dt_logo_season_t season = dt_util_get_logo_season();
293   char *icon;
294   if(season != DT_LOGO_SEASON_NONE)
295     icon = g_strdup_printf("darktable-%d", (int)season);
296   else
297     icon = g_strdup("darktable");
298   gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), icon);
299   g_free(icon);
300 
301   const char *str = _("all those of you that made previous releases possible");
302 
303 #include "tools/darktable_authors.h"
304 
305   const char *final[] = {str, NULL };
306   gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG(dialog), _("and..."), final);
307 
308   gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), _("translator-credits"));
309 
310   gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));
311   gtk_dialog_run(GTK_DIALOG(dialog));
312   gtk_widget_destroy(dialog);
313 }
314 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
315 // vim: shiftwidth=2 expandtab tabstop=2 cindent
316 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
317