No space between function name and first parenthesis, it eases greping (see CODING).
[geeqie.git] / src / bar_histogram.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: Vladimir Nadvornik
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "bar_histogram.h"
16
17 #include "bar.h"
18 #include "metadata.h"
19 #include "filedata.h"
20 #include "menu.h"
21 #include "ui_menu.h"
22 #include "ui_misc.h"
23 #include "histogram.h"
24 #include "rcfile.h"
25
26 /*
27  *-------------------------------------------------------------------
28  * keyword / comment utils
29  *-------------------------------------------------------------------
30  */
31
32
33
34 typedef struct _PaneHistogramData PaneHistogramData;
35 struct _PaneHistogramData
36 {
37         PaneData pane;
38         GtkWidget *widget;
39         GtkWidget *drawing_area;
40         Histogram *histogram;
41         gint histogram_width;
42         gint histogram_height;
43         GdkPixbuf *pixbuf;
44         FileData *fd;
45         gboolean need_update;
46         gint idle_id;
47 };
48
49 static gboolean bar_pane_histogram_update_cb(gpointer data);
50
51
52 static void bar_pane_histogram_update(PaneHistogramData *phd)
53 {
54         if (phd->pixbuf) g_object_unref(phd->pixbuf);
55         phd->pixbuf = NULL;
56
57         gtk_label_set_text(GTK_LABEL(phd->pane.title), histogram_label(phd->histogram));
58
59         if (!phd->histogram_width || !phd->histogram_height || !phd->fd) return;
60
61         /* histmap_get is relatively expensive, run it only when we really need it
62            and with lower priority than pixbuf_renderer 
63            FIXME: this does not work for fullscreen*/
64         if (GTK_WIDGET_DRAWABLE(phd->drawing_area))
65                 {
66                 if (phd->idle_id == -1) phd->idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, bar_pane_histogram_update_cb, phd, NULL);
67                 }
68         else
69                 {
70                 phd->need_update = TRUE;
71                 }
72 }
73
74 static gboolean bar_pane_histogram_update_cb(gpointer data)
75 {
76         const HistMap *histmap;
77         PaneHistogramData *phd = data;
78
79         phd->idle_id = -1;
80         phd->need_update = FALSE;
81         
82         gtk_widget_queue_draw_area(GTK_WIDGET(phd->drawing_area), 0, 0, phd->histogram_width, phd->histogram_height);
83         
84         histmap = histmap_get(phd->fd);
85         
86         if (!histmap) return FALSE;
87         
88         phd->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, phd->histogram_width, phd->histogram_height);
89         gdk_pixbuf_fill(phd->pixbuf, 0xffffffff);
90         histogram_draw(phd->histogram, histmap, phd->pixbuf, 0, 0, phd->histogram_width, phd->histogram_height);
91
92         return FALSE;
93 }
94
95
96 static void bar_pane_histogram_set_fd(GtkWidget *pane, FileData *fd)
97 {
98         PaneHistogramData *phd;
99
100         phd = g_object_get_data(G_OBJECT(pane), "pane_data");
101         if (!phd) return;
102
103         file_data_unref(phd->fd);
104         phd->fd = file_data_ref(fd);
105
106         bar_pane_histogram_update(phd);
107 }
108
109 static void bar_pane_histogram_write_config(GtkWidget *pane, GString *outstr, gint indent)
110 {
111         PaneHistogramData *phd;
112
113         phd = g_object_get_data(G_OBJECT(pane), "pane_data");
114         if (!phd) return;
115
116         WRITE_STRING("<pane_histogram\n");
117         indent++;
118         write_char_option(outstr, indent, "pane.title", gtk_label_get_text(GTK_LABEL(phd->pane.title)));
119         WRITE_BOOL(*phd, pane.expanded);
120         WRITE_INT(*phd->histogram, histogram_channel);
121         WRITE_INT(*phd->histogram, histogram_mode);
122         indent--;
123         WRITE_STRING("/>\n");
124 }
125
126
127 static void bar_pane_histogram_notify_cb(FileData *fd, NotifyType type, gpointer data)
128 {
129         PaneHistogramData *phd = data;
130         if (fd == phd->fd) bar_pane_histogram_update(phd);
131 }
132
133 static gboolean bar_pane_histogram_expose_event_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
134 {
135         PaneHistogramData *phd = data;
136         if (!phd) return TRUE;
137         
138         if (phd->need_update)
139                 {
140                 bar_pane_histogram_update(phd);
141                 }
142         
143         if (!phd->pixbuf) return TRUE;
144         
145         gdk_draw_pixbuf(widget->window,
146                         widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
147                         phd->pixbuf,
148                         0, 0,
149                         0, 0,
150                         -1, -1,
151                         GDK_RGB_DITHER_NORMAL, 0, 0);
152         return TRUE;
153 }
154
155 static void bar_pane_histogram_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
156 {
157         PaneHistogramData *phd = data;
158
159         phd->histogram_width = allocation->width;
160         phd->histogram_height = allocation->height;
161         bar_pane_histogram_update(phd);
162 }
163
164 static void bar_pane_histogram_close(GtkWidget *pane)
165 {
166         PaneHistogramData *phd;
167
168         phd = g_object_get_data(G_OBJECT(pane), "pane_data");
169         if (!phd) return;
170
171         gtk_widget_destroy(phd->widget);
172 }
173
174 static void bar_pane_histogram_destroy(GtkWidget *widget, gpointer data)
175 {
176         PaneHistogramData *phd = data;
177         
178         g_source_remove(phd->idle_id);
179         file_data_unregister_notify_func(bar_pane_histogram_notify_cb, phd);
180
181         file_data_unref(phd->fd);
182         histogram_free(phd->histogram);
183         if (phd->pixbuf) g_object_unref(phd->pixbuf);
184
185         g_free(phd);
186 }
187
188 static void bar_pane_histogram_popup_channels_cb(GtkWidget *widget, gpointer data)
189 {
190         PaneHistogramData *phd;
191         gint channel;
192
193         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
194
195         phd = submenu_item_get_data(widget);
196
197         if (!phd) return;
198
199         channel = GPOINTER_TO_INT(data);
200         if (channel == histogram_get_channel(phd->histogram)) return;
201
202         histogram_set_channel(phd->histogram, channel);
203         bar_pane_histogram_update(phd);
204 }
205
206 static void bar_pane_histogram_popup_logmode_cb(GtkWidget *widget, gpointer data)
207 {
208         PaneHistogramData *phd;
209         gint logmode;
210         
211         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
212
213         phd = submenu_item_get_data(widget);
214
215         if (!phd) return;
216
217         logmode = GPOINTER_TO_INT(data);
218         if (logmode == histogram_get_mode(phd->histogram)) return;
219
220         histogram_set_mode(phd->histogram, logmode);
221         bar_pane_histogram_update(phd);
222 }
223
224 static GtkWidget *bar_pane_histogram_add_radio(GtkWidget *menu, GtkWidget *parent,
225                                         const gchar *label,
226                                         GCallback func, gint value,
227                                         gboolean show_current, gint current_value)
228 {
229         GtkWidget *item;
230
231         if (show_current)
232                 {
233                 item = menu_item_add_radio(menu, parent,
234                                            label, (value == current_value),
235                                            func, GINT_TO_POINTER((gint)value));
236                 }
237         else
238                 {
239                 item = menu_item_add(menu, label,
240                                      func, GINT_TO_POINTER((gint)value));
241                 }
242
243         return item;
244 }
245
246 GtkWidget *bar_pane_histogram_add_channels(GtkWidget *menu, GCallback func, gpointer data,
247                                            gboolean show_current, gint current_value)
248 {
249         GtkWidget *submenu;
250         GtkWidget *parent;
251
252         submenu = gtk_menu_new();
253         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
254
255         parent = bar_pane_histogram_add_radio(submenu, NULL, _("_Red"), func, HCHAN_R, show_current, current_value);
256         bar_pane_histogram_add_radio(submenu, parent, _("_Green"), func, HCHAN_G, show_current, current_value);
257         bar_pane_histogram_add_radio(submenu, parent, _("_Blue"),func, HCHAN_B, show_current, current_value);
258         bar_pane_histogram_add_radio(submenu, parent, _("_RGB"),func, HCHAN_RGB, show_current, current_value);
259         bar_pane_histogram_add_radio(submenu, parent, _("_Value"),func, HCHAN_MAX, show_current, current_value);
260
261         if (menu)
262                 {
263                 GtkWidget *item;
264
265                 item = menu_item_add(menu, _("Channels"), NULL, NULL);
266                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
267                 return item;
268                 }
269
270         return submenu;
271 }
272 GtkWidget *bar_pane_histogram_add_logmode(GtkWidget *menu, GCallback func, gpointer data,
273                                            gboolean show_current, gint current_value)
274 {
275         GtkWidget *submenu;
276         GtkWidget *parent;
277
278         submenu = gtk_menu_new();
279         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
280
281         parent = bar_pane_histogram_add_radio(submenu, NULL, _("_Linear"), func, 0, show_current, current_value);
282         bar_pane_histogram_add_radio(submenu, parent, _("Lo_garithmical"), func, 1, show_current, current_value);
283
284         if (menu)
285                 {
286                 GtkWidget *item;
287
288                 item = menu_item_add(menu, _("Mode"), NULL, NULL);
289                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
290                 return item;
291                 }
292
293         return submenu;
294 }
295
296
297 static GtkWidget *bar_pane_histogram_menu(PaneHistogramData *phd)
298 {
299         GtkWidget *menu;
300         static gboolean show_current = TRUE;
301
302         menu = popup_menu_short_lived();
303         bar_pane_histogram_add_channels(menu, G_CALLBACK(bar_pane_histogram_popup_channels_cb), phd,
304                                         show_current, histogram_get_channel(phd->histogram));
305         bar_pane_histogram_add_logmode(menu, G_CALLBACK(bar_pane_histogram_popup_logmode_cb), phd,
306                                        show_current, histogram_get_mode(phd->histogram));
307
308         return menu;
309 }
310
311 static gboolean bar_pane_histogram_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
312 {
313         PaneHistogramData *phd = data;
314
315         if (bevent->button == MOUSE_BUTTON_RIGHT)
316                 {
317                 GtkWidget *menu;
318
319                 menu = bar_pane_histogram_menu(phd);
320                 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
321                 return TRUE;
322         }
323
324         return FALSE;
325 }
326
327
328 GtkWidget *bar_pane_histogram_new(const gchar *title, gint height, gboolean expanded, gint histogram_channel, gint histogram_mode)
329 {
330         PaneHistogramData *phd;
331
332         phd = g_new0(PaneHistogramData, 1);
333         
334         phd->pane.pane_set_fd = bar_pane_histogram_set_fd;
335         phd->pane.pane_write_config = bar_pane_histogram_write_config;
336         phd->pane.title = gtk_label_new(title);
337         phd->pane.expanded = expanded;
338         phd->idle_id = -1;
339         
340         phd->histogram = histogram_new();
341
342         histogram_set_channel(phd->histogram, histogram_channel);
343         histogram_set_mode(phd->histogram, histogram_mode);
344
345         phd->widget = gtk_vbox_new(FALSE, PREF_PAD_GAP);
346
347         g_object_set_data(G_OBJECT(phd->widget), "pane_data", phd);
348         g_signal_connect(G_OBJECT(phd->widget), "destroy",
349                          G_CALLBACK(bar_pane_histogram_destroy), phd);
350         
351
352         gtk_widget_set_size_request(GTK_WIDGET(phd->widget), -1, height);
353
354         phd->drawing_area = gtk_drawing_area_new();
355         g_signal_connect_after(G_OBJECT(phd->drawing_area), "size_allocate",
356                                G_CALLBACK(bar_pane_histogram_size_cb), phd);
357
358         g_signal_connect(G_OBJECT(phd->drawing_area), "expose_event",  
359                          G_CALLBACK(bar_pane_histogram_expose_event_cb), phd);
360                          
361         gtk_box_pack_start(GTK_BOX(phd->widget), phd->drawing_area, TRUE, TRUE, 0);
362         gtk_widget_show(phd->drawing_area);
363         gtk_widget_add_events(phd->drawing_area, GDK_BUTTON_PRESS_MASK);
364
365         g_signal_connect(G_OBJECT(phd->drawing_area), "button_press_event", G_CALLBACK(bar_pane_histogram_press_cb), phd);
366
367         gtk_widget_show(phd->widget);
368
369         file_data_register_notify_func(bar_pane_histogram_notify_cb, phd, NOTIFY_PRIORITY_LOW);
370
371         return phd->widget;
372 }
373
374 GtkWidget *bar_pane_histogram_new_from_config(const gchar **attribute_names, const gchar **attribute_values)
375 {
376         gchar *title = g_strdup(_("NoName"));
377         gboolean expanded = TRUE;
378         gint height = 80;
379         gint histogram_channel = HCHAN_RGB;
380         gint histogram_mode = 0;
381
382         while (*attribute_names)
383                 {
384                 const gchar *option = *attribute_names++;
385                 const gchar *value = *attribute_values++;
386
387                 if (READ_CHAR_FULL("pane.title", title)) continue;
388                 if (READ_BOOL_FULL("pane.expanded", expanded)) continue;
389                 if (READ_INT_FULL("histogram_channel", histogram_channel)) continue;
390                 if (READ_INT_FULL("histogram_mode", histogram_mode)) continue;
391
392                 
393                 DEBUG_1("unknown attribute %s = %s", option, value);
394                 }
395         
396         return bar_pane_histogram_new(title, height, expanded, histogram_channel, histogram_mode);
397 }
398
399
400 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */