]> www.fi.muni.cz Git - evince.git/blob - shell/ev-utils.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 typedef struct
31 {
32   int size;
33   double *data;
34 } ConvFilter;
35
36 static double
37 gaussian (double x, double y, double r)
38 {
39     return ((1 / (2 * M_PI * r)) *
40             exp ((- (x * x + y * y)) / (2 * r * r)));
41 }
42
43 static ConvFilter *
44 create_blur_filter (int radius)
45 {
46   ConvFilter *filter;
47   int x, y;
48   double sum;
49   
50   filter = g_new0 (ConvFilter, 1);
51   filter->size = radius * 2 + 1;
52   filter->data = g_new (double, filter->size * filter->size);
53
54   sum = 0.0;
55   
56   for (y = 0 ; y < filter->size; y++)
57     {
58       for (x = 0 ; x < filter->size; x++)
59         {
60           sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1),
61                                                                 y - (filter->size >> 1),
62                                                                 radius);
63         }
64     }
65
66   for (y = 0; y < filter->size; y++)
67     {
68       for (x = 0; x < filter->size; x++)
69         {
70           filter->data[y * filter->size + x] /= sum;
71         }
72     }
73
74   return filter;
75   
76 }
77
78 static GdkPixbuf *
79 create_shadow (GdkPixbuf *src, int blur_radius,
80                int x_offset, int y_offset, double opacity)
81 {
82   int x, y, i, j;
83   int width, height;
84   GdkPixbuf *dest;
85   static ConvFilter *filter = NULL;
86   int src_rowstride, dest_rowstride;
87   int src_bpp, dest_bpp;
88   
89   guchar *src_pixels, *dest_pixels;
90
91   if (!filter)
92     filter = create_blur_filter (blur_radius);
93
94   if (x_offset < 0)
95           x_offset = (blur_radius * 4) / 5;
96   
97   if (y_offset < 0)
98           y_offset = (blur_radius * 4) / 5;
99
100   
101   width = gdk_pixbuf_get_width (src) + blur_radius * 2 + x_offset;
102   height = gdk_pixbuf_get_height (src) + blur_radius * 2 + y_offset;
103
104   dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), TRUE,
105                          gdk_pixbuf_get_bits_per_sample (src),
106                          width, height);
107   gdk_pixbuf_fill (dest, 0);  
108   src_pixels = gdk_pixbuf_get_pixels (src);
109   src_rowstride = gdk_pixbuf_get_rowstride (src);
110   src_bpp = gdk_pixbuf_get_has_alpha (src) ? 4 : 3;
111   
112   dest_pixels = gdk_pixbuf_get_pixels (dest);
113   dest_rowstride = gdk_pixbuf_get_rowstride (dest);
114   dest_bpp = gdk_pixbuf_get_has_alpha (dest) ? 4 : 3;
115   
116   for (y = 0; y < height; y++)
117     {
118       for (x = 0; x < width; x++)
119         {
120           int sumr = 0, sumg = 0, sumb = 0, suma = 0;
121
122           for (i = 0; i < filter->size; i++)
123             {
124               for (j = 0; j < filter->size; j++)
125                 {
126                   int src_x, src_y;
127
128                   src_y = -(blur_radius + x_offset) + y - (filter->size >> 1) + i;
129                   src_x = -(blur_radius + y_offset) + x - (filter->size >> 1) + j;
130
131                   if (src_y < 0 || src_y > gdk_pixbuf_get_height (src) ||
132                       src_x < 0 || src_x > gdk_pixbuf_get_width (src))
133                     continue;
134
135                   sumr += src_pixels [src_y * src_rowstride +
136                                       src_x * src_bpp + 0] *
137                     filter->data [i * filter->size + j];
138                   sumg += src_pixels [src_y * src_rowstride +
139                                       src_x * src_bpp + 1] * 
140                     filter->data [i * filter->size + j];
141
142                   sumb += src_pixels [src_y * src_rowstride +
143                                       src_x * src_bpp + 2] * 
144                     filter->data [i * filter->size + j];
145                   
146                   if (src_bpp == 4)
147                     suma += src_pixels [src_y * src_rowstride +
148                                         src_x * src_bpp + 3] *
149                     filter->data [i * filter->size + j];
150                   else
151                           suma += 0xff;
152                     
153                 }
154             }
155
156           if (dest_bpp == 4)
157             dest_pixels [y * dest_rowstride +
158                          x * dest_bpp + 3] = (suma * opacity) / (filter->size * filter->size);
159
160         }
161     }
162   
163   return dest;
164 }
165
166 GdkPixbuf *
167 ev_pixbuf_add_shadow (GdkPixbuf *src, int size,
168                       int x_offset, int y_offset, double opacity)
169 {
170   GdkPixbuf *dest;
171   
172   dest = create_shadow (src, size, x_offset, y_offset, opacity);
173
174   gdk_pixbuf_composite (src, dest,
175                         size, size,
176                         gdk_pixbuf_get_width (src),
177                         gdk_pixbuf_get_height (src),
178                         size, size,
179                         1.0, 1.0,
180                         GDK_INTERP_NEAREST, 255);
181
182   return dest;
183 }
184
185
186 /* Simple function to output the contents of a region.  Used solely for testing
187  * the region code.
188  */
189 void
190 ev_print_region_contents (cairo_region_t *region)
191 {
192         gint n_rectangles, i;
193
194         if (region == NULL) {
195                 g_print ("<empty region>\n");
196                 return;
197         }
198
199         g_print ("<region %p>\n", region);
200         n_rectangles = cairo_region_num_rectangles (region);
201         for (i = 0; i < n_rectangles; i++) {
202                 GdkRectangle rect;
203
204                 cairo_region_get_rectangle (region, i, &rect);
205                 g_print ("\t(%d %d, %d %d) [%dx%d]\n",
206                          rect.x,
207                          rect.y,
208                          rect.x + rect.width,
209                          rect.y + rect.height,
210                          rect.width,
211                          rect.height);
212         }
213 }
214
215 static void
216 ev_gui_sanitise_popup_position (GtkMenu *menu,
217                                 GtkWidget *widget,
218                                 gint *x,
219                                 gint *y)
220 {
221         GdkScreen *screen = gtk_widget_get_screen (widget);
222         gint monitor_num;
223         GdkRectangle monitor;
224         GtkRequisition req;
225
226         g_return_if_fail (widget != NULL);
227
228         gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
229
230         monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
231         gtk_menu_set_monitor (menu, monitor_num);
232         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
233
234         *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
235         *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
236 }
237
238 void
239 ev_gui_menu_position_tree_selection (GtkMenu   *menu,
240                                      gint      *x,
241                                      gint      *y,
242                                      gboolean  *push_in,
243                                      gpointer  user_data)
244 {
245         GtkTreeSelection *selection;
246         GList *selected_rows;
247         GtkTreeModel *model;
248         GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
249         GtkWidget *widget = GTK_WIDGET (user_data);
250         GtkRequisition req;
251         GtkAllocation allocation;
252         GdkRectangle visible;
253
254         gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
255         gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
256         gtk_widget_get_allocation (widget, &allocation);
257
258         *x += (allocation.width - req.width) / 2;
259
260         /* Add on height for the treeview title */
261         gtk_tree_view_get_visible_rect (tree_view, &visible);
262         *y += allocation.height - visible.height;
263
264         selection = gtk_tree_view_get_selection (tree_view);
265         selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
266         if (selected_rows)
267         {
268                 GdkRectangle cell_rect;
269
270                 gtk_tree_view_get_cell_area (tree_view, selected_rows->data,
271                                              NULL, &cell_rect);
272
273                 *y += CLAMP (cell_rect.y + cell_rect.height, 0, visible.height);
274
275                 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
276                 g_list_free (selected_rows);
277         }
278
279         ev_gui_sanitise_popup_position (menu, widget, x, y);
280 }
281
282 /**
283  * get_num_monitors: Get the number of user monitors.
284  * @window: optional GtkWindow to look at.
285  *
286  * Returns: Number of monitors, -1 if uncertain situation (like multiple screens)
287  */
288 gint 
289 get_num_monitors (GtkWindow *window)
290 {
291         GdkDisplay *display; 
292         GdkScreen *screen;
293         gint num_screen;
294         
295         display = gdk_display_get_default ();
296         num_screen = gdk_display_get_n_screens (display);
297         
298         if (num_screen != 1)
299                 return -1;
300         
301         if (window)
302                 screen = gtk_window_get_screen (window);
303         else
304                 screen = gdk_display_get_screen (display, 0);
305
306         return gdk_screen_get_n_monitors (screen);
307 }
308
309 void           
310 file_chooser_dialog_add_writable_pixbuf_formats (GtkFileChooser *chooser)
311 {
312         GSList *pixbuf_formats = NULL;
313         GSList *iter;
314         GtkFileFilter *filter;
315         int i;
316   
317         filter = gtk_file_filter_new();
318         gtk_file_filter_set_name (filter, _("By extension"));
319         g_object_set_data (G_OBJECT(filter), "pixbuf-format", NULL);
320         gtk_file_chooser_add_filter (chooser, filter);
321
322         pixbuf_formats = gdk_pixbuf_get_formats ();
323
324         for (iter = pixbuf_formats; iter; iter = iter->next) {
325                 GdkPixbufFormat *format = iter->data;
326
327                 gchar *description, *name, *extensions;
328                 gchar **extension_list, **mime_types;
329
330                 if (gdk_pixbuf_format_is_disabled (format) ||
331                     !gdk_pixbuf_format_is_writable (format))
332                             continue;
333
334                 name = gdk_pixbuf_format_get_description (format);
335                 extension_list = gdk_pixbuf_format_get_extensions (format);
336                 extensions = g_strjoinv (", ", extension_list);
337                 g_strfreev (extension_list);
338                 description = g_strdup_printf ("%s (%s)", name, extensions);
339
340                 filter = gtk_file_filter_new ();
341                 gtk_file_filter_set_name (filter, description);
342                 g_object_set_data (G_OBJECT (filter), "pixbuf-format", format);
343                 gtk_file_chooser_add_filter (chooser, filter);
344
345                 g_free (description);
346                 g_free (extensions);
347                 g_free (name);
348
349                 mime_types = gdk_pixbuf_format_get_mime_types (format);
350                 for (i = 0; mime_types[i] != 0; i++)
351                         gtk_file_filter_add_mime_type (filter, mime_types[i]);
352                 g_strfreev (mime_types);
353         }
354
355         g_slist_free (pixbuf_formats);
356 }
357
358 GdkPixbufFormat*
359 get_gdk_pixbuf_format_by_extension (gchar *uri)
360 {
361         GSList *pixbuf_formats = NULL;
362         GSList *iter;
363         int i;
364
365         pixbuf_formats = gdk_pixbuf_get_formats ();
366
367         for (iter = pixbuf_formats; iter; iter = iter->next) {
368                 gchar **extension_list;
369                 GdkPixbufFormat *format = iter->data;
370                 
371                 if (gdk_pixbuf_format_is_disabled (format) ||
372                     !gdk_pixbuf_format_is_writable (format))
373                             continue;
374
375                 extension_list = gdk_pixbuf_format_get_extensions (format);
376
377                 for (i = 0; extension_list[i] != 0; i++) {
378                         if (g_str_has_suffix (uri, extension_list[i])) {
379                                 g_slist_free (pixbuf_formats);
380                                 g_strfreev (extension_list);
381                                 return format;
382                         }
383                 }
384                 g_strfreev (extension_list);
385         }
386
387         g_slist_free (pixbuf_formats);
388         return NULL;
389 }