1 /*
2 * Copyright (C) 2008 Igalia
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors:
18 * Pablo Sanxiao <psanxiao@gmail.com>
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include "gtr-application.h"
26 #include "gtr-po.h"
27 #include "gtr-translation-memory.h"
28 #include "gtr-translation-memory-ui.h"
29 #include "gtr-tab.h"
30 #include "gtr-utils.h"
31 #include "gtr-window.h"
32 #include "gtr-debug.h"
33
34 #include <string.h>
35 #include <glib.h>
36 #include <glib/gi18n.h>
37 #include <glib-object.h>
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
40
41 #define MAX_ELEMENTS 9
42
43 typedef struct
44 {
45 GtrTranslationMemory *translation_memory;
46 GtkWidget *tree_view;
47 GtrTab *tab;
48
49 gchar **tm_list;
50 gint *tm_list_id;
51
52 GtkWidget *popup_menu;
53 GtrMsg *msg;
54 } GtrTranslationMemoryUiPrivate;
55
56
57 G_DEFINE_TYPE_WITH_PRIVATE (GtrTranslationMemoryUi, gtr_translation_memory_ui, GTK_TYPE_SCROLLED_WINDOW)
58
59 enum
60 {
61 SHORTCUT_COLUMN,
62 LEVEL_COLUMN,
63 STRING_COLUMN,
64 N_COLUMNS
65 };
66
67 static void
68 tree_view_size_cb (GtkWidget * widget,
69 GtkAllocation * allocation,
70 gpointer user_data);
71
72 static void
choose_translation(GtrTranslationMemoryUi * tm_ui,const gchar * translation)73 choose_translation (GtrTranslationMemoryUi *tm_ui, const gchar *translation)
74 {
75 GtrView *view;
76 GtkTextBuffer *buffer;
77 GtrPo *po;
78 GList *current_msg = NULL;
79 GtrMsg *msg;
80 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
81
82 view = gtr_tab_get_active_view (priv->tab);
83 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
84
85 po = gtr_tab_get_po (priv->tab);
86 current_msg = gtr_po_get_current_message (po);
87
88 msg = GTR_MSG (current_msg->data);
89
90 gtr_msg_set_msgstr (msg, translation);
91
92 gtk_text_buffer_begin_user_action (buffer);
93 gtk_text_buffer_set_text (buffer, translation, -1);
94 gtk_text_buffer_end_user_action (buffer);
95
96 gtr_po_set_state (po, GTR_PO_STATE_MODIFIED);
97 }
98
99 static void
free_match(gpointer data)100 free_match (gpointer data)
101 {
102 GtrTranslationMemoryMatch *match = (GtrTranslationMemoryMatch *) data;
103
104 g_free (match->match);
105 g_slice_free (GtrTranslationMemoryMatch, match);
106 }
107
108 static void
showed_message_cb(GtrTab * tab,GtrMsg * msg,GtrTranslationMemoryUi * tm_ui)109 showed_message_cb (GtrTab *tab, GtrMsg *msg, GtrTranslationMemoryUi *tm_ui)
110 {
111 GtkListStore *model;
112 GtkTreeIter iter;
113 GtkTreeViewColumn *level_column;
114 const gchar *msgid;
115 gint i;
116 GList *tm_list = NULL;
117 GList *l = NULL;
118 GList *renderers_list = NULL;
119 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
120
121 model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
122
123 g_signal_connect (priv->tree_view,
124 "size_allocate",
125 G_CALLBACK (tree_view_size_cb), priv->tree_view);
126
127 if (priv->msg)
128 g_object_unref (priv->msg);
129 priv->msg = g_object_ref (msg);
130
131 msgid = gtr_msg_get_msgid (msg);
132
133 tm_list = gtr_translation_memory_lookup (priv->translation_memory, msgid);
134 g_strfreev (priv->tm_list);
135
136 gtk_list_store_clear (model);
137 priv->tm_list = g_new (gchar *, MAX_ELEMENTS + 1);
138 priv->tm_list_id = g_new (gint, MAX_ELEMENTS + 1);
139
140 i = 0;
141 for (l = tm_list; l && i < MAX_ELEMENTS; l = l->next)
142 {
143 GtrTranslationMemoryMatch *match = (GtrTranslationMemoryMatch *) l->data;
144
145 priv->tm_list_id[i] = match->id;
146 priv->tm_list[i] = g_strdup (match->match);
147 level_column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->tree_view), 0);
148 renderers_list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (level_column));
149
150 g_object_set (renderers_list->data,
151 "accel-mods", GDK_CONTROL_MASK, NULL);
152 g_list_free (renderers_list);
153
154 gtk_list_store_append (model, &iter);
155 gtk_list_store_set (model, &iter,
156 SHORTCUT_COLUMN, GDK_KEY_1 + i,
157 STRING_COLUMN, match->match,
158 LEVEL_COLUMN, match->level,
159 -1);
160 i++;
161 }
162
163 /* Ensure last element is NULL */
164 priv->tm_list[i] = NULL;
165
166 g_list_free_full (tm_list, free_match);
167 }
168
169 static void
tree_view_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GtrTranslationMemoryUi * tm_ui)170 tree_view_row_activated (GtkTreeView *tree_view,
171 GtkTreePath *path,
172 GtkTreeViewColumn *column,
173 GtrTranslationMemoryUi *tm_ui)
174 {
175 GtkTreeModel *model;
176 GtkTreeIter iter;
177 gchar *translation;
178
179 model = gtk_tree_view_get_model (tree_view);
180
181 if (!gtk_tree_model_get_iter (model, &iter, path))
182 return;
183
184 gtk_tree_model_get (model, &iter,
185 STRING_COLUMN, &translation,
186 -1);
187
188 choose_translation (tm_ui, translation);
189
190 g_free (translation);
191 }
192
193 static void
popup_menu_translation_activate(GtkMenuItem * menuitem,GtrTranslationMemoryUi * tm_ui)194 popup_menu_translation_activate (GtkMenuItem *menuitem,
195 GtrTranslationMemoryUi *tm_ui)
196 {
197 GtkTreeSelection *selection;
198 GtkTreeModel *model;
199 GtkTreeIter iter;
200 gchar *translation;
201 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
202
203 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
204 if (!selection || !gtk_tree_selection_get_selected (selection, &model, &iter))
205 return;
206
207 gtk_tree_model_get (model, &iter,
208 STRING_COLUMN, &translation,
209 -1);
210
211 choose_translation (tm_ui, translation);
212
213 g_free (translation);
214 }
215
216 static void
popup_menu_remove_from_memory(GtkMenuItem * menuitem,GtrTranslationMemoryUi * tm_ui)217 popup_menu_remove_from_memory (GtkMenuItem *menuitem,
218 GtrTranslationMemoryUi *tm_ui)
219 {
220 GtkTreeSelection *selection;
221 GtkTreeModel *model;
222 GtkTreeIter iter;
223 gint i;
224 gchar *translation;
225 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
226
227 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
228 if (!selection || !gtk_tree_selection_get_selected (selection, &model, &iter))
229 return;
230
231 gtk_tree_model_get (model, &iter,
232 STRING_COLUMN, &translation,
233 -1);
234
235 for (i = 0; priv->tm_list[i]; i++)
236 if (!strcmp (priv->tm_list[i], translation))
237 break;
238
239 gtr_translation_memory_remove (priv->translation_memory, priv->tm_list_id[i]);
240
241 g_free (translation);
242
243 /* update list */
244 showed_message_cb (priv->tab, priv->msg, tm_ui);
245 }
246
247 static GtkWidget *
create_tree_popup_menu(GtrTranslationMemoryUi * self)248 create_tree_popup_menu (GtrTranslationMemoryUi *self)
249 {
250 GtkWidget *menu;
251 GtkWidget *item;
252
253 menu = gtk_menu_new ();
254
255 item = gtk_menu_item_new_with_mnemonic (_("_Use this translation"));
256 g_signal_connect (item, "activate",
257 G_CALLBACK (popup_menu_translation_activate), self);
258 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
259
260 item = gtk_menu_item_new_with_mnemonic (_("_Remove"));
261 g_signal_connect (item, "activate",
262 G_CALLBACK (popup_menu_remove_from_memory), self);
263 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
264 gtk_widget_set_sensitive (item, TRUE);
265
266 gtk_widget_show_all (menu);
267
268 return menu;
269 }
270
271 static void
tree_popup_menu_detach(GtrTranslationMemoryUi * self,GtkMenu * menu)272 tree_popup_menu_detach (GtrTranslationMemoryUi *self, GtkMenu * menu)
273 {
274 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (self);
275 priv->popup_menu = NULL;
276 }
277
278 static void
gtr_translation_memory_ui_show_menu(GtrTranslationMemoryUi * self,GdkEventButton * event)279 gtr_translation_memory_ui_show_menu (GtrTranslationMemoryUi *self,
280 GdkEventButton * event)
281 {
282 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (self);
283 if (priv->popup_menu)
284 gtk_widget_destroy (priv->popup_menu);
285
286 priv->popup_menu = create_tree_popup_menu (self);
287
288 gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
289 GTK_WIDGET (self),
290 (GtkMenuDetachFunc) tree_popup_menu_detach);
291
292 if (event != NULL)
293 gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), (GdkEvent*)event);
294 else
295 {
296 GdkEvent *ev = gtk_get_current_event ();
297 gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), ev);
298 }
299 }
300
301 static void
tree_view_size_cb(GtkWidget * widget,GtkAllocation * allocation,gpointer user_data)302 tree_view_size_cb (GtkWidget * widget,
303 GtkAllocation * allocation,
304 gpointer user_data)
305 {
306 GtkTreeView *treeview;
307 GtkTreeViewColumn *column;
308 GList *renderers_list = NULL;
309 gint size;
310
311 treeview = GTK_TREE_VIEW (user_data);
312
313 column = gtk_tree_view_get_column (treeview, 2);
314 renderers_list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
315
316 size = gtk_tree_view_column_get_width (column);
317
318 g_object_set (renderers_list->data, "wrap-width", MAX(1, size - 10), NULL);
319
320 g_list_free (renderers_list);
321 }
322
323 static gboolean
tree_view_button_press_event(GtkTreeView * tree,GdkEventButton * event,GtrTranslationMemoryUi * tm_ui)324 tree_view_button_press_event (GtkTreeView *tree,
325 GdkEventButton *event,
326 GtrTranslationMemoryUi *tm_ui)
327 {
328 GtkTreePath *path;
329
330 if (GDK_BUTTON_PRESS != event->type || 3 != event->button)
331 return FALSE;
332
333 if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y,
334 &path, NULL, NULL, NULL))
335 return FALSE;
336
337 gtk_widget_grab_focus (GTK_WIDGET (tree));
338 gtk_tree_view_set_cursor (tree, path, NULL, FALSE);
339
340 gtr_translation_memory_ui_show_menu (tm_ui, event);
341 return TRUE;
342 }
343
344 static gboolean
tree_view_popup_menu(GtkTreeView * tree,GtrTranslationMemoryUi * tm_ui)345 tree_view_popup_menu (GtkTreeView *tree, GtrTranslationMemoryUi *tm_ui)
346 {
347 gtr_translation_memory_ui_show_menu (tm_ui, NULL);
348 return TRUE;
349 }
350
351 static void
gtr_translation_memory_ui_init(GtrTranslationMemoryUi * tm_ui)352 gtr_translation_memory_ui_init (GtrTranslationMemoryUi * tm_ui)
353 {
354 GtkListStore *model;
355 GtkCellRenderer *level_renderer, *string_renderer, *shortcut_renderer;
356 GtkTreeViewColumn *shortcut, *string, *level;
357 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
358
359 priv->tm_list = NULL;
360 priv->popup_menu = NULL;
361 priv->msg = NULL;
362
363 priv->tree_view = gtk_tree_view_new ();
364 gtk_widget_show (priv->tree_view);
365
366 model = gtk_list_store_new (N_COLUMNS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
367 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),
368 GTK_TREE_MODEL (model));
369
370 shortcut_renderer = gtk_cell_renderer_accel_new ();
371 shortcut = gtk_tree_view_column_new_with_attributes (_("Shortcut"),
372 shortcut_renderer,
373 "accel-key",
374 SHORTCUT_COLUMN, NULL);
375 g_object_set (shortcut_renderer, "width", 80, NULL);
376
377 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), shortcut);
378
379 level_renderer = gtk_cell_renderer_progress_new ();
380 level = gtk_tree_view_column_new_with_attributes (_("Level"),
381 level_renderer,
382 "value", LEVEL_COLUMN,
383 NULL);
384 g_object_set (level_renderer, "width", 80, NULL);
385
386 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), level);
387
388 string_renderer = gtk_cell_renderer_text_new ();
389 string = gtk_tree_view_column_new_with_attributes (_("String"),
390 string_renderer,
391 "text", STRING_COLUMN,
392 NULL);
393 gtk_tree_view_column_set_sizing (string, GTK_TREE_VIEW_COLUMN_FIXED);
394
395 g_object_set (string_renderer,
396 "ypad", 0,
397 "xpad", 5,
398 "yalign", 0.0,
399 "wrap-mode", PANGO_WRAP_WORD_CHAR,
400 NULL);
401
402 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), string);
403
404 g_signal_connect (priv->tree_view, "button-press-event",
405 G_CALLBACK (tree_view_button_press_event), tm_ui);
406
407 g_signal_connect (priv->tree_view, "popup-menu",
408 G_CALLBACK (tree_view_popup_menu), tm_ui);
409
410 g_signal_connect (priv->tree_view, "row-activated",
411 G_CALLBACK (tree_view_row_activated), tm_ui);
412 }
413
414 static void
gtr_translation_memory_ui_dispose(GObject * object)415 gtr_translation_memory_ui_dispose (GObject * object)
416 {
417 GtrTranslationMemoryUi *tm_ui = GTR_TRANSLATION_MEMORY_UI (object);
418 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
419
420 DEBUG_PRINT ("Dispose translation memory ui");
421
422 g_clear_object (&priv->msg);
423
424 G_OBJECT_CLASS (gtr_translation_memory_ui_parent_class)->dispose (object);
425 }
426
427 static void
gtr_translation_memory_ui_finalize(GObject * object)428 gtr_translation_memory_ui_finalize (GObject * object)
429 {
430 GtrTranslationMemoryUi *tm_ui = GTR_TRANSLATION_MEMORY_UI (object);
431 GtrTranslationMemoryUiPrivate *priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
432
433 DEBUG_PRINT ("Finalize translation memory ui");
434
435 g_strfreev (priv->tm_list);
436
437 G_OBJECT_CLASS (gtr_translation_memory_ui_parent_class)->finalize (object);
438 }
439
440 static void
gtr_translation_memory_ui_class_init(GtrTranslationMemoryUiClass * klass)441 gtr_translation_memory_ui_class_init (GtrTranslationMemoryUiClass * klass)
442 {
443 GObjectClass *object_class = G_OBJECT_CLASS (klass);
444
445 object_class->dispose = gtr_translation_memory_ui_dispose;
446 object_class->finalize = gtr_translation_memory_ui_finalize;
447 }
448
449 GtkWidget *
gtr_translation_memory_ui_new(GtkWidget * tab,GtrTranslationMemory * translation_memory)450 gtr_translation_memory_ui_new (GtkWidget *tab,
451 GtrTranslationMemory *translation_memory)
452 {
453 GtrTranslationMemoryUi *tm_ui;
454 GtrTranslationMemoryUiPrivate *priv;
455 tm_ui = g_object_new (GTR_TYPE_TRANSLATION_MEMORY_UI, NULL);
456
457 priv = gtr_translation_memory_ui_get_instance_private (tm_ui);
458 priv->tab = GTR_TAB (tab);
459 priv->translation_memory = translation_memory;
460
461 g_signal_connect (tab,
462 "showed-message", G_CALLBACK (showed_message_cb), tm_ui);
463
464 /* Scrolledwindow needs to be realized to add a widget */
465 gtk_container_add (GTK_CONTAINER (tm_ui), priv->tree_view);
466
467 return GTK_WIDGET (tm_ui);
468 }
469