]> www.fi.muni.cz Git - evince.git/blob - shell/ev-utils.c
Simplify image format selection on save.
[evince.git] / shell / ev-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  *  Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
4  *
5  *  This program 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 2, or (at your option)
8  *  any later version.
9  *
10  *  This program 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 this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20
21 #include <config.h>
22
23 #include "ev-utils.h"
24 #include "ev-file-helpers.h"
25
26 #include <string.h>
27 #include <math.h>
28 #include <glib/gi18n.h>
29
30 #define PRINT_CONFIG_FILENAME   "ev-print-config.xml"
31
32 typedef struct
33 {
34   int size;
35   double *data;
36 } ConvFilter;
37
38 static double
39 gaussian (double x, double y, double r)
40 {
41     return ((1 / (2 * M_PI * r)) *
42             exp ((- (x * x + y * y)) / (2 * r * r)));
43 }
44
45 static ConvFilter *
46 create_blur_filter (int radius)
47 {
48   ConvFilter *filter;
49   int x, y;
50   double sum;
51   
52   filter = g_new0 (ConvFilter, 1);
53   filter->size = radius * 2 + 1;
54   filter->data = g_new (double, filter->size * filter->size);
55
56   sum = 0.0;
57   
58   for (y = 0 ; y < filter->size; y++)
59     {
60       for (x = 0 ; x < filter->size; x++)
61         {
62           sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1),
63                                                                 y - (filter->size >> 1),
64                                                                 radius);
65         }
66     }
67
68   for (y = 0; y < filter->size; y++)
69     {
70       for (x = 0; x < filter->size; x++)
71         {
72           filter->data[y * filter->size + x] /= sum;
73         }
74     }
75
76   return filter;
77   
78 }
79
80 static GdkPixbuf *
81 create_shadow (GdkPixbuf *src, int blur_radius,
82                int x_offset, int y_offset, double opacity)
83 {
84   int x, y, i, j;
85   int width, height;
86   GdkPixbuf *dest;
87   static ConvFilter *filter = NULL;
88   int src_rowstride, dest_rowstride;
89   int src_bpp, dest_bpp;
90   
91   guchar *src_pixels, *dest_pixels;
92
93   if (!filter)
94     filter = create_blur_filter (blur_radius);
95
96   if (x_offset < 0)
97           x_offset = (blur_radius * 4) / 5;
98   
99   if (y_offset < 0)
100           y_offset = (blur_radius * 4) / 5;
101
102   
103   width = gdk_pixbuf_get_width (src) + blur_radius * 2 + x_offset;
104   height = gdk_pixbuf_get_height (src) + blur_radius * 2 + y_offset;
105
106   dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), TRUE,
107                          gdk_pixbuf_get_bits_per_sample (src),
108                          width, height);
109   gdk_pixbuf_fill (dest, 0);  
110   src_pixels = gdk_pixbuf_get_pixels (src);
111   src_rowstride = gdk_pixbuf_get_rowstride (src);
112   src_bpp = gdk_pixbuf_get_has_alpha (src) ? 4 : 3;
113   
114   dest_pixels = gdk_pixbuf_get_pixels (dest);
115   dest_rowstride = gdk_pixbuf_get_rowstride (dest);
116   dest_bpp = gdk_pixbuf_get_has_alpha (dest) ? 4 : 3;
117   
118   for (y = 0; y < height; y++)
119     {
120       for (x = 0; x < width; x++)
121         {
122           int sumr = 0, sumg = 0, sumb = 0, suma = 0;
123
124           for (i = 0; i < filter->size; i++)
125             {
126               for (j = 0; j < filter->size; j++)
127                 {
128                   int src_x, src_y;
129
130                   src_y = -(blur_radius + x_offset) + y - (filter->size >> 1) + i;
131                   src_x = -(blur_radius + y_offset) + x - (filter->size >> 1) + j;
132
133                   if (src_y < 0 || src_y > gdk_pixbuf_get_height (src) ||
134                       src_x < 0 || src_x > gdk_pixbuf_get_width (src))
135                     continue;
136
137                   sumr += src_pixels [src_y * src_rowstride +
138                                       src_x * src_bpp + 0] *
139                     filter->data [i * filter->size + j];
140                   sumg += src_pixels [src_y * src_rowstride +
141                                       src_x * src_bpp + 1] * 
142                     filter->data [i * filter->size + j];
143
144                   sumb += src_pixels [src_y * src_rowstride +
145                                       src_x * src_bpp + 2] * 
146                     filter->data [i * filter->size + j];
147                   
148                   if (src_bpp == 4)
149                     suma += src_pixels [src_y * src_rowstride +
150                                         src_x * src_bpp + 3] *
151                     filter->data [i * filter->size + j];
152                   else
153                           suma += 0xff;
154                     
155                 }
156             }
157
158           if (dest_bpp == 4)
159             dest_pixels [y * dest_rowstride +
160                          x * dest_bpp + 3] = (suma * opacity) / (filter->size * filter->size);
161
162         }
163     }
164   
165   return dest;
166 }
167
168 GdkPixbuf *
169 ev_pixbuf_add_shadow (GdkPixbuf *src, int size,
170                       int x_offset, int y_offset, double opacity)
171 {
172   GdkPixbuf *dest;
173   
174   dest = create_shadow (src, size, x_offset, y_offset, opacity);
175
176   gdk_pixbuf_composite (src, dest,
177                         size, size,
178                         gdk_pixbuf_get_width (src),
179                         gdk_pixbuf_get_height (src),
180                         size, size,
181                         1.0, 1.0,
182                         GDK_INTERP_NEAREST, 255);
183
184   return dest;
185 }
186
187
188 /* Simple function to output the contents of a region.  Used solely for testing
189  * the region code.
190  */
191 void
192 ev_print_region_contents (GdkRegion *region)
193 {
194         GdkRectangle *rectangles = NULL;
195         gint n_rectangles, i;
196
197         if (region == NULL) {
198                 g_print ("<empty region>\n");
199                 return;
200         }
201
202         g_print ("<region %p>\n", region);
203         gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
204         for (i = 0; i < n_rectangles; i++) {
205                 g_print ("\t(%d %d, %d %d) [%dx%d]\n",
206                          rectangles[i].x,
207                          rectangles[i].y,
208                          rectangles[i].x + rectangles[i].width,
209                          rectangles[i].y + rectangles[i].height,
210                          rectangles[i].width,
211                          rectangles[i].height);
212         }
213         g_free (rectangles);
214 }
215
216 #ifdef WITH_GNOME_PRINT
217 gboolean
218 using_pdf_printer (GnomePrintConfig *config)
219 {
220         const guchar *driver;
221
222         driver = gnome_print_config_get (
223                 config, (const guchar *)"Settings.Engine.Backend.Driver");
224
225         if (driver) {
226                 if (!strcmp ((const gchar *)driver, "gnome-print-pdf"))
227                         return TRUE;
228                 else
229                         return FALSE;
230         }
231
232         return FALSE;
233 }
234
235 gboolean
236 using_postscript_printer (GnomePrintConfig *config)
237 {
238         const guchar *driver;
239         const guchar *transport;
240
241         driver = gnome_print_config_get (
242                 config, (const guchar *)"Settings.Engine.Backend.Driver");
243
244         transport = gnome_print_config_get (
245                 config, (const guchar *)"Settings.Transport.Backend");
246
247         if (driver) {
248                 if (!strcmp ((const gchar *)driver, "gnome-print-ps"))
249                         return TRUE;
250                 else
251                         return FALSE;
252         } else  if (transport) { /* these transports default to PostScript */
253                 if (!strcmp ((const gchar *)transport, "CUPS"))
254                         return TRUE;
255                 else if (!strcmp ((const gchar *)transport, "LPD"))
256                         return TRUE;
257                 else if (!strcmp ((const gchar *)transport, "PAPI"))
258                         return TRUE;
259         }
260
261         return FALSE;
262 }
263
264 GnomePrintConfig *
265 load_print_config_from_file (void)
266 {
267         GnomePrintConfig *print_config = NULL;
268         char *file_name, *contents = NULL;
269
270         file_name = g_build_filename (ev_dot_dir (), PRINT_CONFIG_FILENAME,
271                                       NULL);
272
273         if (g_file_get_contents (file_name, &contents, NULL, NULL)) {
274                 print_config = gnome_print_config_from_string (contents, 0);
275                 g_free (contents);
276         }
277
278         if (print_config == NULL) {
279                 print_config = gnome_print_config_default ();
280         }
281
282         g_free (file_name);
283
284         return print_config;
285 }
286
287 void
288 save_print_config_to_file (GnomePrintConfig *config)
289 {
290         char *file_name, *str;
291
292         g_return_if_fail (config != NULL);
293
294         str = gnome_print_config_to_string (config, 0);
295         if (str == NULL) return;
296
297         file_name = g_build_filename (ev_dot_dir (),
298                                       PRINT_CONFIG_FILENAME,
299                                       NULL);
300
301         g_file_set_contents (file_name, str, -1, NULL);
302
303         g_free (file_name);
304         g_free (str);
305 }
306 #endif /* WITH_GNOME_PRINT */
307
308 static void
309 ev_gui_sanitise_popup_position (GtkMenu *menu,
310                                 GtkWidget *widget,
311                                 gint *x,
312                                 gint *y)
313 {
314         GdkScreen *screen = gtk_widget_get_screen (widget);
315         gint monitor_num;
316         GdkRectangle monitor;
317         GtkRequisition req;
318
319         g_return_if_fail (widget != NULL);
320
321         gtk_widget_size_request (GTK_WIDGET (menu), &req);
322
323         monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
324         gtk_menu_set_monitor (menu, monitor_num);
325         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
326
327         *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
328         *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
329 }
330
331 void
332 ev_gui_menu_position_tree_selection (GtkMenu   *menu,
333                                      gint      *x,
334                                      gint      *y,
335                                      gboolean  *push_in,
336                                      gpointer  user_data)
337 {
338         GtkTreeSelection *selection;
339         GList *selected_rows;
340         GtkTreeModel *model;
341         GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
342         GtkWidget *widget = GTK_WIDGET (user_data);
343         GtkRequisition req;
344         GdkRectangle visible;
345
346         gtk_widget_size_request (GTK_WIDGET (menu), &req);
347         gdk_window_get_origin (widget->window, x, y);
348
349         *x += (widget->allocation.width - req.width) / 2;
350
351         /* Add on height for the treeview title */
352         gtk_tree_view_get_visible_rect (tree_view, &visible);
353         *y += widget->allocation.height - visible.height;
354
355         selection = gtk_tree_view_get_selection (tree_view);
356         selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
357         if (selected_rows)
358         {
359                 GdkRectangle cell_rect;
360
361                 gtk_tree_view_get_cell_area (tree_view, selected_rows->data,
362                                              NULL, &cell_rect);
363
364                 *y += CLAMP (cell_rect.y + cell_rect.height, 0, visible.height);
365
366                 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
367                 g_list_free (selected_rows);
368         }
369
370         ev_gui_sanitise_popup_position (menu, widget, x, y);
371 }
372
373 /**
374  * get_num_monitors: Get the number of user monitors.
375  * @window: optional GtkWindow to look at.
376  *
377  * Returns: Number of monitors, -1 if uncertain situation (like multiple screens)
378  */
379 gint 
380 get_num_monitors (GtkWindow *window)
381 {
382         GdkDisplay *display; 
383         GdkScreen *screen;
384         gint num_screen;
385         
386         display = gdk_display_get_default ();
387         num_screen = gdk_display_get_n_screens (display);
388         
389         if (num_screen != 1)
390                 return -1;
391         
392         if (window)
393                 screen = gtk_window_get_screen (window);
394         else
395                 screen = gdk_display_get_screen (display, 0);
396
397         return gdk_screen_get_n_monitors (screen);
398 }
399
400 gdouble
401 get_screen_dpi (GtkWindow *window)
402 {
403         GdkScreen *screen;
404         gdouble    xdpi, ydpi;
405
406         screen = gtk_window_get_screen (window);
407
408         xdpi = 25.4 * gdk_screen_get_width (screen) / gdk_screen_get_width_mm (screen);
409         ydpi = 25.4 * gdk_screen_get_height (screen) / gdk_screen_get_height_mm (screen);
410         
411         return (xdpi + ydpi) / 2.0;
412 }
413
414
415 void           
416 file_chooser_dialog_add_writable_pixbuf_formats (GtkFileChooser *chooser)
417 {
418         GSList *pixbuf_formats = NULL;
419         GSList *iter;
420         GtkFileFilter *filter;
421         int i;
422   
423         filter = gtk_file_filter_new();
424         gtk_file_filter_set_name (filter, _("By extension"));
425         g_object_set_data (G_OBJECT(filter), "pixbuf-format", NULL);
426         gtk_file_chooser_add_filter (chooser, filter);
427
428         pixbuf_formats = gdk_pixbuf_get_formats ();
429
430         for (iter = pixbuf_formats; iter; iter = iter->next) {
431                 GdkPixbufFormat *format = iter->data;
432
433                 gchar *description, *name, *extensions;
434                 gchar **extension_list, **mime_types;
435
436                 if (gdk_pixbuf_format_is_disabled (format) ||
437                     !gdk_pixbuf_format_is_writable (format))
438                             continue;
439
440                 name = gdk_pixbuf_format_get_description (format);
441                 extension_list = gdk_pixbuf_format_get_extensions (format);
442                 extensions = g_strjoinv (", ", extension_list);
443                 g_strfreev (extension_list);
444                 description = g_strdup_printf ("%s (%s)", name, extensions);
445
446                 filter = gtk_file_filter_new ();
447                 gtk_file_filter_set_name (filter, description);
448                 g_object_set_data (G_OBJECT (filter), "pixbuf-format", format);
449                 gtk_file_chooser_add_filter (chooser, filter);
450
451                 g_free (description);
452                 g_free (extensions);
453                 g_free (name);
454
455                 mime_types = gdk_pixbuf_format_get_mime_types (format);
456                 for (i = 0; mime_types[i] != 0; i++)
457                         gtk_file_filter_add_mime_type (filter, mime_types[i]);
458                 g_strfreev (mime_types);
459         }
460
461         g_slist_free (pixbuf_formats);
462 }
463
464 GdkPixbufFormat*
465 get_gdk_pixbuf_format_by_extension (gchar *uri)
466 {
467         GSList *pixbuf_formats = NULL;
468         GSList *iter;
469         int i;
470
471         pixbuf_formats = gdk_pixbuf_get_formats ();
472
473         for (iter = pixbuf_formats; iter; iter = iter->next) {
474                 gchar **extension_list;
475                 GdkPixbufFormat *format = iter->data;
476                 
477                 if (gdk_pixbuf_format_is_disabled (format) ||
478                     !gdk_pixbuf_format_is_writable (format))
479                             continue;
480
481                 extension_list = gdk_pixbuf_format_get_extensions (format);
482
483                 for (i = 0; extension_list[i] != 0; i++) {
484                         if (g_str_has_suffix (uri, extension_list[i])) {
485                                 g_slist_free (pixbuf_formats);
486                                 g_strfreev (extension_list);
487                                 return format;
488                         }
489                 }
490                 g_strfreev (extension_list);
491         }
492
493         g_slist_free (pixbuf_formats);
494         return NULL;
495 }
496