Compile modules based on HAVE_* features
[geeqie.git] / src / bar-comment.cc
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "bar-comment.h"
23
24 #include <cstring>
25
26 #include <config.h>
27
28 #include <gdk/gdk.h>
29 #include <glib-object.h>
30 #if HAVE_SPELL
31 #  include <gspell/gspell.h>
32 #endif
33
34 #include "bar.h"
35 #include "compat.h"
36 #include "debug.h"
37 #include "filedata.h"
38 #include "intl.h"
39 #include "layout.h"
40 #include "main-defines.h"
41 #include "metadata.h"
42 #include "options.h"
43 #include "rcfile.h"
44 #include "typedefs.h"
45 #include "ui-menu.h"
46 #include "ui-misc.h"
47
48 static void bar_pane_comment_changed(GtkTextBuffer *buffer, gpointer data);
49
50 /*
51  *-------------------------------------------------------------------
52  * keyword / comment utils
53  *-------------------------------------------------------------------
54  */
55
56
57
58 struct PaneCommentData
59 {
60         PaneData pane;
61         GtkWidget *widget;
62         GtkWidget *comment_view;
63         FileData *fd;
64         gchar *key;
65         gint height;
66 #if HAVE_SPELL
67         GspellTextView *gspell_view;
68 #endif
69 };
70
71
72 static void bar_pane_comment_write(PaneCommentData *pcd)
73 {
74         gchar *comment;
75
76         if (!pcd->fd) return;
77
78         comment = text_widget_text_pull(pcd->comment_view);
79
80         metadata_write_string(pcd->fd, pcd->key, comment);
81         g_free(comment);
82 }
83
84
85 static void bar_pane_comment_update(PaneCommentData *pcd)
86 {
87         gchar *comment = nullptr;
88         gchar *orig_comment = nullptr;
89         const gchar *comment_not_null;
90         gshort rating;
91         GtkTextBuffer *comment_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pcd->comment_view));
92
93         orig_comment = text_widget_text_pull(pcd->comment_view);
94         if (g_strcmp0(pcd->key, "Xmp.xmp.Rating") == 0)
95                 {
96                 rating = metadata_read_int(pcd->fd, pcd->key, 0);
97                 comment = g_strdup_printf("%d", rating);
98                 }
99         else
100                 {
101                 comment = metadata_read_string(pcd->fd, pcd->key, METADATA_PLAIN);
102                 }
103         comment_not_null = (comment) ? comment : "";
104
105         if (strcmp(orig_comment, comment_not_null) != 0)
106                 {
107                 g_signal_handlers_block_by_func(comment_buffer, (gpointer)bar_pane_comment_changed, pcd);
108                 gtk_text_buffer_set_text(comment_buffer, comment_not_null, -1);
109                 g_signal_handlers_unblock_by_func(comment_buffer, (gpointer)bar_pane_comment_changed, pcd);
110                 }
111         g_free(comment);
112         g_free(orig_comment);
113
114         gtk_widget_set_sensitive(pcd->comment_view, (pcd->fd != nullptr));
115 }
116
117 static void bar_pane_comment_set_selection(PaneCommentData *pcd, gboolean append)
118 {
119         GList *list = nullptr;
120         GList *work;
121         gchar *comment = nullptr;
122
123         comment = text_widget_text_pull(pcd->comment_view);
124
125         list = layout_selection_list(pcd->pane.lw);
126         list = file_data_process_groups_in_selection(list, FALSE, nullptr);
127
128         work = list;
129         while (work)
130                 {
131                 auto fd = static_cast<FileData *>(work->data);
132                 work = work->next;
133                 if (fd == pcd->fd) continue;
134
135                 if (append)
136                         {
137                         metadata_append_string(fd, pcd->key, comment);
138                         }
139                 else
140                         {
141                         metadata_write_string(fd, pcd->key, comment);
142                         }
143                 }
144
145         filelist_free(list);
146         g_free(comment);
147 }
148
149 static void bar_pane_comment_sel_add_cb(GtkWidget *, gpointer data)
150 {
151         auto pcd = static_cast<PaneCommentData *>(data);
152
153         bar_pane_comment_set_selection(pcd, TRUE);
154 }
155
156 static void bar_pane_comment_sel_replace_cb(GtkWidget *, gpointer data)
157 {
158         auto pcd = static_cast<PaneCommentData *>(data);
159
160         bar_pane_comment_set_selection(pcd, FALSE);
161 }
162
163
164 static void bar_pane_comment_set_fd(GtkWidget *bar, FileData *fd)
165 {
166         PaneCommentData *pcd;
167
168         pcd = static_cast<PaneCommentData *>(g_object_get_data(G_OBJECT(bar), "pane_data"));
169         if (!pcd) return;
170
171         file_data_unref(pcd->fd);
172         pcd->fd = file_data_ref(fd);
173
174         bar_pane_comment_update(pcd);
175 }
176
177 static gint bar_pane_comment_event(GtkWidget *bar, GdkEvent *event)
178 {
179         PaneCommentData *pcd;
180
181         pcd = static_cast<PaneCommentData *>(g_object_get_data(G_OBJECT(bar), "pane_data"));
182         if (!pcd) return FALSE;
183
184         if (gtk_widget_has_focus(pcd->comment_view)) return gtk_widget_event(pcd->comment_view, event);
185
186         return FALSE;
187 }
188
189 static void bar_pane_comment_write_config(GtkWidget *pane, GString *outstr, gint indent)
190 {
191         PaneCommentData *pcd;
192         gint w;
193         gint h;
194
195         pcd = static_cast<PaneCommentData *>(g_object_get_data(G_OBJECT(pane), "pane_data"));
196         if (!pcd) return;
197
198         gtk_widget_get_size_request(GTK_WIDGET(pane), &w, &h);
199
200         if (!g_strcmp0(pcd->pane.id, "title"))
201                 {
202                 pcd->height = h;
203                 }
204         if (!g_strcmp0(pcd->pane.id, "comment"))
205                 {
206                 pcd->height = h;
207                 }
208         if (!g_strcmp0(pcd->pane.id, "rating"))
209                 {
210                 pcd->height = h;
211                 }
212         if (!g_strcmp0(pcd->pane.id, "headline"))
213                 {
214                 pcd->height = h;
215                 }
216
217         WRITE_NL(); WRITE_STRING("<pane_comment ");
218         write_char_option(outstr, indent, "id", pcd->pane.id);
219         write_char_option(outstr, indent, "title", gtk_label_get_text(GTK_LABEL(pcd->pane.title)));
220         WRITE_BOOL(pcd->pane, expanded);
221         WRITE_CHAR(*pcd, key);
222         WRITE_INT(*pcd, height);
223         WRITE_STRING("/>");
224 }
225
226 static void bar_pane_comment_notify_cb(FileData *fd, NotifyType type, gpointer data)
227 {
228         auto pcd = static_cast<PaneCommentData *>(data);
229         if ((type & (NOTIFY_REREAD | NOTIFY_CHANGE | NOTIFY_METADATA)) && fd == pcd->fd)
230                 {
231                 DEBUG_1("Notify pane_comment: %s %04x", fd->path, type);
232
233                 bar_pane_comment_update(pcd);
234                 }
235 }
236
237 static void bar_pane_comment_changed(GtkTextBuffer *, gpointer data)
238 {
239         auto pcd = static_cast<PaneCommentData *>(data);
240
241         bar_pane_comment_write(pcd);
242 }
243
244
245 static void bar_pane_comment_populate_popup(GtkTextView *, GtkMenu *menu, gpointer data)
246 {
247         auto pcd = static_cast<PaneCommentData *>(data);
248
249         menu_item_add_divider(GTK_WIDGET(menu));
250         menu_item_add_icon(GTK_WIDGET(menu), _("Add text to selected files"), GQ_ICON_ADD, G_CALLBACK(bar_pane_comment_sel_add_cb), pcd);
251         menu_item_add_icon(GTK_WIDGET(menu), _("Replace existing text in selected files"), GQ_ICON_REPLACE, G_CALLBACK(bar_pane_comment_sel_replace_cb), data);
252 }
253
254 static void bar_pane_comment_destroy(GtkWidget *, gpointer data)
255 {
256         auto pcd = static_cast<PaneCommentData *>(data);
257
258         file_data_unregister_notify_func(bar_pane_comment_notify_cb, pcd);
259 #if HAVE_SPELL
260         gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(pcd->comment_view)), pcd->comment_view);
261 #endif
262         file_data_unref(pcd->fd);
263         g_free(pcd->key);
264
265         g_free(pcd->pane.id);
266
267         g_free(pcd);
268 }
269
270
271 static GtkWidget *bar_pane_comment_new(const gchar *id, const gchar *title, const gchar *key, gboolean expanded, gint height)
272 {
273         PaneCommentData *pcd;
274         GtkWidget *scrolled;
275         GtkTextBuffer *buffer;
276
277         pcd = g_new0(PaneCommentData, 1);
278
279         pcd->pane.pane_set_fd = bar_pane_comment_set_fd;
280         pcd->pane.pane_event = bar_pane_comment_event;
281         pcd->pane.pane_write_config = bar_pane_comment_write_config;
282         pcd->pane.title = bar_pane_expander_title(title);
283         pcd->pane.id = g_strdup(id);
284         pcd->pane.type = PANE_COMMENT;
285 #if HAVE_SPELL
286         pcd->gspell_view = nullptr;
287 #endif
288         pcd->pane.expanded = expanded;
289
290         pcd->key = g_strdup(key);
291         pcd->height = height;
292
293         scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
294
295         pcd->widget = scrolled;
296         g_object_set_data(G_OBJECT(pcd->widget), "pane_data", pcd);
297         g_signal_connect(G_OBJECT(pcd->widget), "destroy",
298                          G_CALLBACK(bar_pane_comment_destroy), pcd);
299
300         gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
301         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
302                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
303
304         gtk_widget_set_size_request(pcd->widget, -1, height);
305         gtk_widget_show(scrolled);
306
307         pcd->comment_view = gtk_text_view_new();
308         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(pcd->comment_view), GTK_WRAP_WORD);
309         gq_gtk_container_add(GTK_WIDGET(scrolled), pcd->comment_view);
310         g_signal_connect(G_OBJECT(pcd->comment_view), "populate-popup",
311                          G_CALLBACK(bar_pane_comment_populate_popup), pcd);
312         gtk_widget_show(pcd->comment_view);
313
314 #if HAVE_SPELL
315         if (g_strcmp0(key, "Xmp.xmp.Rating") != 0)
316                 {
317                 if (options->metadata.check_spelling)
318                         {
319                         pcd->gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(pcd->comment_view));
320                         gspell_text_view_basic_setup(pcd->gspell_view);
321                         }
322         }
323 #endif
324
325         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pcd->comment_view));
326         g_signal_connect(G_OBJECT(buffer), "changed",
327                          G_CALLBACK(bar_pane_comment_changed), pcd);
328
329
330         file_data_register_notify_func(bar_pane_comment_notify_cb, pcd, NOTIFY_PRIORITY_LOW);
331
332         return pcd->widget;
333 }
334
335 GtkWidget *bar_pane_comment_new_from_config(const gchar **attribute_names, const gchar **attribute_values)
336 {
337         gchar *title = nullptr;
338         gchar *key = g_strdup(COMMENT_KEY);
339         gboolean expanded = TRUE;
340         gint height = 50;
341         gchar *id = g_strdup("comment");
342         GtkWidget *ret;
343
344         while (*attribute_names)
345                 {
346                 const gchar *option = *attribute_names++;
347                 const gchar *value = *attribute_values++;
348
349                 if (READ_CHAR_FULL("title", title)) continue;
350                 if (READ_CHAR_FULL("key", key)) continue;
351                 if (READ_BOOL_FULL("expanded", expanded)) continue;
352                 if (READ_INT_FULL("height", height)) continue;
353                 if (READ_CHAR_FULL("id", id)) continue;
354
355
356                 log_printf("unknown attribute %s = %s\n", option, value);
357                 }
358
359         if (!g_strcmp0(id, "title"))
360                 {
361                 options->info_title.height = height;
362                 }
363         if (!g_strcmp0(id, "comment"))
364                 {
365                 options->info_comment.height = height;
366                 }
367         if (!g_strcmp0(id, "rating"))
368                 {
369                 options->info_rating.height = height;
370                 }
371         if (!g_strcmp0(id, "headline"))
372                 {
373                 options->info_headline.height = height;
374                 }
375
376         bar_pane_translate_title(PANE_COMMENT, id, &title);
377         ret = bar_pane_comment_new(id, title, key, expanded, height);
378         g_free(title);
379         g_free(key);
380         g_free(id);
381         return ret;
382 }
383
384 void bar_pane_comment_update_from_config(GtkWidget *pane, const gchar **attribute_names, const gchar **attribute_values)
385 {
386         PaneCommentData *pcd;
387
388         pcd = static_cast<PaneCommentData *>(g_object_get_data(G_OBJECT(pane), "pane_data"));
389         if (!pcd) return;
390
391         gchar *title = nullptr;
392
393         while (*attribute_names)
394                 {
395                 const gchar *option = *attribute_names++;
396                 const gchar *value = *attribute_values++;
397
398                 if (READ_CHAR_FULL("title", title)) continue;
399                 if (READ_CHAR_FULL("key", pcd->key)) continue;
400                 if (READ_BOOL_FULL("expanded", pcd->pane.expanded)) continue;
401                 if (READ_INT_FULL("height", pcd->height)) continue;
402                 if (READ_CHAR_FULL("id", pcd->pane.id)) continue;
403
404
405                 log_printf("unknown attribute %s = %s\n", option, value);
406                 }
407
408         if (title)
409                 {
410                 bar_pane_translate_title(PANE_COMMENT, pcd->pane.id, &title);
411                 gtk_label_set_text(GTK_LABEL(pcd->pane.title), title);
412                 g_free(title);
413                 }
414         gtk_widget_set_size_request(pcd->widget, -1, pcd->height);
415         bar_update_expander(pane);
416         bar_pane_comment_update(pcd);
417 }
418
419 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */