1 /*
2 * Copyright (C) 2007 Ignacio Casal Quinteiro <nacho.resa@gmail.com>
3 * Fatih Demir <kabalak@kabalak.net>
4 * Ross Golder <ross@golder.org>
5 * Gediminas Paulauskas <menesis@kabalak.net>
6 * 2008 Pablo Sanxiao <psanxiao@gmail.com>
7 * Igalia
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * Authors:
23 * Pablo Sanxiao <psanxiao@gmail.com>
24 * Ignacio Casal Quinteiro <nacho.resa@gmail.com>
25 * Fatih Demir <kabalak@kabalak.net>
26 * Ross Golder <ross@golder.org>
27 * Gediminas Paulauskas <menesis@kabalak.net>
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include "gtr-application.h"
35 #include "gtr-debug.h"
36 #include "gtr-file-dialogs.h"
37 #include "gtr-po.h"
38 #include "gtr-msg.h"
39 #include "gtr-enum-types.h"
40 #include "gtr-profile.h"
41 #include "gtr-utils.h"
42 #include "gtr-message-container.h"
43
44 #include <string.h>
45 #include <errno.h>
46
47 #include <glib.h>
48 #include <glib-object.h>
49 #include <glib/gi18n.h>
50 #include <gtk/gtk.h>
51 #include <gettext-po.h>
52 #include <gio/gio.h>
53
54 static void gtr_po_message_container_init (GtrMessageContainerInterface *iface);
55
56 typedef struct
57 {
58 /* The location of the file to open */
59 GFile *location;
60
61 /* Gettext's file handle */
62 po_file_t gettext_po_file;
63
64 /* Message iter */
65 po_message_iterator_t iter;
66
67 /* The message domains in this file */
68 GList *domains;
69
70 /* Parsed list of GtrMsgs for the current domains' messagelist */
71 GList *messages;
72
73 /* A pointer to the currently displayed message */
74 GList *current;
75
76 /* The obsolete messages are stored within this gchar. */
77 gchar *obsolete;
78
79 /* Is the file write-permitted? (read-only) */
80 gboolean no_write_perms;
81
82 /* Translated entries count */
83 guint translated;
84
85 /* Fuzzy entries count */
86 guint fuzzy;
87
88 /* Autosave timeout timer */
89 guint autosave_timeout;
90
91 /* Header object */
92 GtrHeader *header;
93
94 GtrPoState state;
95
96 /* Damned Lies(DL) teams are stored here */
97 gchar *dl_team;
98
99 /* DL modules */
100 gchar *dl_module;
101
102 /* DL branches */
103 gchar *dl_branch;
104
105 /* DL domains */
106 gchar *dl_domain;
107
108 /* The state of a DL module */
109 gchar *dl_state;
110
111 /* Marks if the file was changed; */
112 guint file_changed : 1;
113 } GtrPoPrivate;
114
115
116 G_DEFINE_TYPE_WITH_CODE (GtrPo, gtr_po, G_TYPE_OBJECT,
117 G_ADD_PRIVATE (GtrPo)
118 G_IMPLEMENT_INTERFACE (GTR_TYPE_MESSAGE_CONTAINER,
119 gtr_po_message_container_init))
120
121 enum
122 {
123 PROP_0,
124 PROP_LOCATION,
125 PROP_STATE
126 };
127
128 static gchar *message_error = NULL;
129
130 static void
gtr_header_set_field(GtrHeader * header,const gchar * field,const gchar * data)131 gtr_header_set_field (GtrHeader * header,
132 const gchar * field, const gchar * data)
133 {
134 gchar *msgstr;
135
136 g_return_if_fail (GTR_IS_HEADER (header));
137 g_return_if_fail (data != NULL);
138
139 msgstr = po_header_set_field (gtr_msg_get_msgstr (GTR_MSG (header)),
140 field, data);
141 gtr_msg_set_msgstr (GTR_MSG (header), msgstr);
142
143 g_free (msgstr);
144 }
145
146 static void
gtr_po_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)147 gtr_po_set_property (GObject *object,
148 guint prop_id,
149 const GValue *value,
150 GParamSpec *pspec)
151 {
152 GtrPo *po = GTR_PO (object);
153
154 switch (prop_id)
155 {
156 case PROP_LOCATION:
157 gtr_po_set_location (po, G_FILE (g_value_get_object (value)));
158 break;
159 default:
160 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161 break;
162 }
163 }
164
165 static void
gtr_po_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)166 gtr_po_get_property (GObject * object,
167 guint prop_id, GValue * value, GParamSpec * pspec)
168 {
169 GtrPo *po = GTR_PO (object);
170
171 switch (prop_id)
172 {
173 case PROP_LOCATION:
174 g_value_take_object (value, gtr_po_get_location (po));
175 break;
176 case PROP_STATE:
177 g_value_set_enum (value, gtr_po_get_state (po));
178 break;
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181 break;
182 }
183 }
184
185 /*
186 * A helper function simply increments the "translated" variable of the
187 * po-file.
188 */
189 static void
determine_translation_status(GtrMsg * msg,GtrPo * po)190 determine_translation_status (GtrMsg * msg, GtrPo * po)
191 {
192 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
193 if (gtr_msg_is_fuzzy (msg))
194 priv->fuzzy++;
195 else if (gtr_msg_is_translated (msg))
196 priv->translated++;
197 }
198
199 /*
200 * Update the count of the completed translated entries.
201 */
202 static void
gtr_po_update_translated_count(GtrPo * po)203 gtr_po_update_translated_count (GtrPo * po)
204 {
205 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
206 priv->translated = 0;
207 priv->fuzzy = 0;
208 g_list_foreach (priv->messages,
209 (GFunc) determine_translation_status, po);
210 }
211
212 static void
gtr_po_init(GtrPo * po)213 gtr_po_init (GtrPo * po)
214 {
215 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
216
217 priv->location = NULL;
218 priv->gettext_po_file = NULL;
219 priv->dl_team = NULL;
220 priv->dl_module = NULL;
221 priv->dl_branch = NULL;
222 priv->dl_domain = NULL;
223 priv->dl_state = NULL;
224 }
225
226 static void
gtr_po_finalize(GObject * object)227 gtr_po_finalize (GObject * object)
228 {
229 GtrPo *po = GTR_PO (object);
230 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
231
232 g_list_free_full (priv->messages, g_object_unref);
233 g_list_free_full (priv->domains, g_free);
234 g_free (priv->obsolete);
235
236 if (priv->gettext_po_file)
237 po_file_free (priv->gettext_po_file);
238
239 if (priv->dl_team)
240 g_free (priv->dl_team);
241 if (priv->dl_module)
242 g_free (priv->dl_module);
243 if (priv->dl_branch)
244 g_free (priv->dl_branch);
245 if (priv->dl_domain)
246 g_free (priv->dl_domain);
247 if (priv->dl_state)
248 g_free (priv->dl_state);
249
250 G_OBJECT_CLASS (gtr_po_parent_class)->finalize (object);
251 }
252
253 static void
gtr_po_dispose(GObject * object)254 gtr_po_dispose (GObject * object)
255 {
256 GtrPo *po = GTR_PO (object);
257 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
258
259 g_clear_object (&priv->location);
260
261 G_OBJECT_CLASS (gtr_po_parent_class)->dispose (object);
262 }
263
264 static GtrMsg *
gtr_po_message_container_get_message(GtrMessageContainer * container,gint number)265 gtr_po_message_container_get_message (GtrMessageContainer *container,
266 gint number)
267 {
268 GtrPo *po = GTR_PO (container);
269 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
270
271 return g_list_nth_data (priv->messages, number);
272 }
273
274 static gint
gtr_po_message_container_get_message_number(GtrMessageContainer * container,GtrMsg * msg)275 gtr_po_message_container_get_message_number (GtrMessageContainer * container,
276 GtrMsg * msg)
277 {
278 GtrPo *po = GTR_PO (container);
279 GList *list;
280 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
281
282 list = g_list_find (priv->messages, msg);
283 return g_list_position (priv->messages, list);
284 }
285
286 static gint
gtr_po_message_container_get_count(GtrMessageContainer * container)287 gtr_po_message_container_get_count (GtrMessageContainer * container)
288 {
289 GtrPo *po = GTR_PO (container);
290 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
291
292 return g_list_length (priv->messages);
293 }
294
295 static void
gtr_po_message_container_init(GtrMessageContainerInterface * iface)296 gtr_po_message_container_init (GtrMessageContainerInterface * iface)
297 {
298 iface->get_message = gtr_po_message_container_get_message;
299 iface->get_message_number = gtr_po_message_container_get_message_number;
300 iface->get_count = gtr_po_message_container_get_count;
301 }
302
303 static void
gtr_po_class_init(GtrPoClass * klass)304 gtr_po_class_init (GtrPoClass * klass)
305 {
306 GObjectClass *object_class = G_OBJECT_CLASS (klass);
307
308 object_class->finalize = gtr_po_finalize;
309 object_class->dispose = gtr_po_dispose;
310 object_class->get_property = gtr_po_get_property;
311 object_class->set_property = gtr_po_set_property;
312
313 g_object_class_install_property (object_class,
314 PROP_LOCATION,
315 g_param_spec_object ("location",
316 "Location",
317 "The po's location",
318 G_TYPE_FILE,
319 G_PARAM_READWRITE));
320
321 g_object_class_install_property (object_class,
322 PROP_STATE,
323 g_param_spec_enum ("state",
324 "State",
325 "The po's state",
326 GTR_TYPE_PO_STATE,
327 GTR_PO_STATE_SAVED,
328 G_PARAM_READABLE));
329 }
330
331 /*
332 * Functions for errors handling.
333 */
334 GQuark
gtr_po_error_quark(void)335 gtr_po_error_quark (void)
336 {
337 static GQuark quark = 0;
338 if (!quark)
339 quark = g_quark_from_static_string ("gtr_po_parser_error");
340 return quark;
341 }
342
343 static void
on_gettext_po_xerror(gint severity,po_message_t message,const gchar * filename,size_t lineno,size_t column,gint multiline_p,const gchar * message_text)344 on_gettext_po_xerror (gint severity,
345 po_message_t message,
346 const gchar * filename, size_t lineno, size_t column,
347 gint multiline_p, const gchar * message_text)
348 {
349 message_error = g_strdup (message_text);
350 }
351
352 static void
on_gettext_po_xerror2(gint severity,po_message_t message1,const gchar * filename1,size_t lineno1,size_t column1,gint multiline_p1,const gchar * message_text1,po_message_t message2,const gchar * filename2,size_t lineno2,size_t column2,gint multiline_p2,const gchar * message_text2)353 on_gettext_po_xerror2 (gint severity,
354 po_message_t message1,
355 const gchar * filename1, size_t lineno1,
356 size_t column1, gint multiline_p1,
357 const gchar * message_text1, po_message_t message2,
358 const gchar * filename2, size_t lineno2,
359 size_t column2, gint multiline_p2,
360 const gchar * message_text2)
361 {
362 message_error = g_strdup_printf ("%s.\n %s", message_text1, message_text2);
363 }
364
365 static gboolean
po_file_is_empty(po_file_t file)366 po_file_is_empty (po_file_t file)
367 {
368 const gchar *const *domains = po_file_domains (file);
369
370 for (; *domains != NULL; domains++)
371 {
372 po_message_iterator_t iter = po_message_iterator (file, *domains);
373 if (po_next_message (iter) != NULL)
374 {
375 po_message_iterator_free (iter);
376 return FALSE;
377 }
378 po_message_iterator_free (iter);
379 }
380 return TRUE;
381 }
382
383 /**
384 * is_read_only:
385 * @location: a GFile Object that represents the file to check
386 *
387 * This method is copied from gedit, file gedit-commands-file.c
388 *
389 * Returns: False if file is writeable. True if file doesn't exists, is read-only or read-only attribute can't be check
390 */
391 static gboolean
is_read_only(const gchar * filename)392 is_read_only (const gchar * filename)
393 {
394 gboolean ret = TRUE; /* default to read only */
395 GFileInfo *info;
396 GFile *location;
397
398 location = g_file_new_for_path (filename);
399
400 if (!g_file_query_exists (location, NULL))
401 return FALSE;
402
403 info = g_file_query_info (location,
404 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
405 G_FILE_QUERY_INFO_NONE, NULL, NULL);
406 g_object_unref (location);
407
408 if (info != NULL)
409 {
410 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
411 {
412 ret = !g_file_info_get_attribute_boolean (info,
413 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
414 }
415
416 g_object_unref (info);
417 }
418
419 return ret;
420 }
421
422 /***************************** Public funcs ***********************************/
423
424 /**
425 * gtr_po_new:
426 *
427 * Creates a new #GtrPo.
428 *
429 * Returns: a new #GtrPo object
430 **/
431 GtrPo *
gtr_po_new(void)432 gtr_po_new (void)
433 {
434 GtrPo *po;
435
436 po = g_object_new (GTR_TYPE_PO, NULL);
437
438 return po;
439 }
440
441 static gboolean
_gtr_po_load(GtrPo * po,GFile * location,GError ** error)442 _gtr_po_load (GtrPo * po, GFile * location, GError ** error)
443 {
444 struct po_xerror_handler handler;
445 po_message_iterator_t iter;
446 po_message_t message;
447 const gchar *msgid;
448 gchar *filename;
449 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
450
451 /*
452 * Initialize the handler error.
453 */
454 handler.xerror = &on_gettext_po_xerror;
455 handler.xerror2 = &on_gettext_po_xerror2;
456
457 if (message_error != NULL)
458 {
459 g_free (message_error);
460 message_error = NULL;
461 }
462
463 filename = g_file_get_path (location);
464
465 if (priv->gettext_po_file)
466 po_file_free (priv->gettext_po_file);
467
468 if (priv->header)
469 {
470 g_object_unref (priv->header);
471 priv->header = NULL;
472 }
473
474 if (priv->iter)
475 {
476 po_message_iterator_free (priv->iter);
477 priv->iter = NULL;
478 }
479
480 priv->gettext_po_file = po_file_read (filename, &handler);
481 g_free (filename);
482
483 if (po_file_is_empty (priv->gettext_po_file))
484 {
485 g_set_error (error,
486 GTR_PO_ERROR,
487 GTR_PO_ERROR_FILE_EMPTY, _("The file is empty"));
488 return FALSE;
489 }
490
491 if (!priv->gettext_po_file)
492 {
493 g_set_error (error,
494 GTR_PO_ERROR,
495 GTR_PO_ERROR_FILENAME,
496 _("Failed opening file “%s”: %s"),
497 filename, g_strerror (errno));
498 g_free (filename);
499 return FALSE;
500 }
501
502 iter = po_message_iterator (priv->gettext_po_file, NULL);
503 message = po_next_message (iter);
504 msgid = po_message_msgid (message);
505
506 if (*msgid == '\0')
507 priv->header = gtr_header_new (iter, message);
508 else
509 {
510 po_message_iterator_free (iter);
511 iter = po_message_iterator (priv->gettext_po_file, NULL);
512
513 message = po_message_create ();
514 po_message_set_msgid (message, "");
515 po_message_set_msgstr (message, "");
516 po_message_insert (iter, message);
517
518 priv->header = gtr_header_new (iter, message);
519 }
520
521 priv->iter = iter;
522
523 return TRUE;
524 }
525
526 static gboolean
_gtr_po_load_ensure_utf8(GtrPo * po,GError ** error)527 _gtr_po_load_ensure_utf8 (GtrPo * po, GError ** error)
528 {
529 GMappedFile *mapped;
530 const gchar *content;
531 gboolean utf8_valid;
532 gchar *filename;
533 gsize size;
534 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
535
536 filename = g_file_get_path (priv->location);
537 mapped = g_mapped_file_new (filename, FALSE, error);
538 g_free (filename);
539
540 if (!mapped)
541 return FALSE;
542
543 content = g_mapped_file_get_contents (mapped);
544 size = g_mapped_file_get_length (mapped);
545
546 utf8_valid = g_utf8_validate (content, size, NULL);
547
548 if (!_gtr_po_load (po, priv->location, error))
549 {
550 g_mapped_file_unref (mapped);
551 return FALSE;
552 }
553
554 if (!utf8_valid &&
555 priv->header)
556 {
557 gchar *charset = NULL;
558
559 if (priv->header)
560 charset = gtr_header_get_charset (priv->header);
561
562 if (charset && *charset && strcmp (charset, "UTF-8") != 0)
563 {
564 GOutputStream *converter_stream, *stream;
565 GCharsetConverter *converter;
566 GIOStream *iostream;
567 GFile *tmp;
568
569 /* Store UTF-8 converted file in $TMP */
570 converter = g_charset_converter_new ("UTF-8", charset, NULL);
571
572 if (!converter)
573 {
574 g_set_error (error,
575 GTR_PO_ERROR,
576 GTR_PO_ERROR_ENCODING,
577 _("Could not convert from charset “%s” to UTF-8"),
578 charset);
579 g_mapped_file_unref (mapped);
580 g_free (charset);
581 return FALSE;
582 }
583
584 g_free (charset);
585 tmp = g_file_new_tmp ("gtranslator-XXXXXX.po",
586 (GFileIOStream **) &iostream,
587 NULL);
588
589 if (!tmp)
590 {
591 g_set_error (error,
592 GTR_PO_ERROR,
593 GTR_PO_ERROR_ENCODING,
594 _("Could not store temporary "
595 "file for encoding conversion"));
596 g_mapped_file_unref (mapped);
597 g_object_unref (converter);
598 return FALSE;
599 }
600
601 stream = g_io_stream_get_output_stream (iostream);
602 converter_stream =
603 g_converter_output_stream_new (stream,
604 G_CONVERTER (converter));
605
606
607 if (!g_output_stream_write_all (converter_stream,
608 content, size, NULL,
609 NULL, NULL))
610 {
611 g_set_error (error,
612 GTR_PO_ERROR,
613 GTR_PO_ERROR_ENCODING,
614 _("Could not store temporary "
615 "file for encoding conversion"));
616 g_object_unref (converter_stream);
617 g_object_unref (iostream);
618 g_object_unref (converter);
619 g_mapped_file_unref (mapped);
620 return FALSE;
621 }
622
623 g_object_unref (converter_stream);
624 g_object_unref (iostream);
625 g_object_unref (converter);
626
627 /* Now load again the converted file */
628 if (!_gtr_po_load (po, tmp, error))
629 {
630 g_mapped_file_unref (mapped);
631 return FALSE;
632 }
633
634 /* Ensure Content-Type is set correctly
635 * in the header as per the content
636 */
637 if (priv->header)
638 gtr_header_set_charset (priv->header, "UTF-8");
639
640 utf8_valid = TRUE;
641 }
642 }
643
644 g_mapped_file_unref (mapped);
645
646 if (!utf8_valid)
647 {
648 g_set_error (error,
649 GTR_PO_ERROR,
650 GTR_PO_ERROR_ENCODING,
651 _("All attempt to convert the file to UTF-8 has failed, "
652 "use the msgconv or iconv command line tools before "
653 "opening this file with GNOME Translation Editor"));
654 return FALSE;
655 }
656
657 return TRUE;
658 }
659
660 /**
661 * gtr_po_parse:
662 * @po: a #GtrPo
663 * @location: the file to open
664 * @error: a variable to store the errors
665 *
666 * Parses all things related to the #GtrPo and initilizes all neccessary
667 * variables.
668 **/
669 gboolean
gtr_po_parse(GtrPo * po,GFile * location,GError ** error)670 gtr_po_parse (GtrPo * po, GFile * location, GError ** error)
671 {
672 GtrMsg *msg;
673 po_message_t message;
674 po_message_iterator_t iter;
675 const gchar *const *domains;
676 gint i = 0;
677 gint pos = 1;
678 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
679
680 g_return_val_if_fail (GTR_IS_PO (po), FALSE);
681 g_return_val_if_fail (location != NULL, FALSE);
682
683 if (message_error != NULL)
684 {
685 g_free (message_error);
686 message_error = NULL;
687 }
688
689 /*
690 * Get filename path.
691 */
692 priv->location = g_file_dup (location);
693
694 if (!_gtr_po_load_ensure_utf8 (po, error))
695 {
696 g_object_unref (po);
697 return FALSE;
698 }
699
700 /*
701 * No need to return; this can be corrected by the user
702 */
703 if (message_error != NULL)
704 {
705 g_set_error (error,
706 GTR_PO_ERROR, GTR_PO_ERROR_RECOVERY, "%s", message_error);
707 }
708
709 /*
710 * Determine the message domains to track
711 */
712 if (!(domains = po_file_domains (priv->gettext_po_file)))
713 {
714 if (*error != NULL)
715 g_clear_error (error);
716 g_set_error (error,
717 GTR_PO_ERROR,
718 GTR_PO_ERROR_GETTEXT,
719 _("Gettext returned a null message domain list."));
720 g_object_unref (po);
721 return FALSE;
722 }
723 while (domains[i])
724 {
725 priv->domains = g_list_append (priv->domains, g_strdup (domains[i]));
726 i++;
727 }
728
729 /*
730 * Determine whether first message is the header or not, and
731 * if so, process it seperately. Otherwise, treat as a normal
732 * message.
733 */
734 priv->messages = NULL;
735 iter = priv->iter;
736
737 /* Post-process these into a linked list of GtrMsgs. */
738 while ((message = po_next_message (iter)))
739 {
740 /*FIXME: We have to change this:
741 * we have to add a gtr_msg_is_obsolete fund msg.c
742 * and detect if we want obsoletes messages in show message
743 */
744 if (!po_message_is_obsolete (message))
745 {
746 /* Unpack into a GtrMsg */
747 msg = _gtr_msg_new (iter, message);
748
749 /* Set position in PO file */
750 gtr_msg_set_po_position (msg, pos++);
751
752 /* Build up messages */
753 priv->messages = g_list_prepend (priv->messages, msg);
754 }
755 }
756
757 if (priv->messages == NULL)
758 {
759 if (*error != NULL)
760 g_clear_error (error);
761 g_set_error (error,
762 GTR_PO_ERROR,
763 GTR_PO_ERROR_OTHER,
764 _("No messages obtained from parser."));
765 g_object_unref (po);
766 return FALSE;
767 }
768
769 priv->messages = g_list_reverse (priv->messages);
770
771 /*
772 * Set the current message to the first message.
773 */
774 priv->current = g_list_first (priv->messages);
775
776 gtr_po_update_translated_count (po);
777
778 /* Initialize Tab state */
779 priv->state = GTR_PO_STATE_SAVED;
780 return TRUE;
781 }
782
783 /**
784 * gtr_po_save_file:
785 * @po: a #GtrPo
786 * @error: a GError to manage the exceptions
787 *
788 * It saves the po file and if there are any problem it stores the error
789 * in @error.
790 **/
791 void
gtr_po_save_file(GtrPo * po,GError ** error)792 gtr_po_save_file (GtrPo * po, GError ** error)
793 {
794 struct po_xerror_handler handler;
795 gchar *filename;
796 gchar *msg_error;
797 GtrHeader *header;
798 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
799
800 /*
801 * Initialice the handler error.
802 */
803 handler.xerror = &on_gettext_po_xerror;
804 handler.xerror2 = &on_gettext_po_xerror2;
805
806 filename = g_file_get_path (priv->location);
807
808 if (g_str_has_suffix (filename, ".pot"))
809 {
810 // Remove suffix
811 filename[strlen (filename) - 4] = '\0';
812 g_set_error (error,
813 GTR_PO_ERROR,
814 GTR_PO_ERROR_FILENAME,
815 _("You are saving a file with a .pot extension.\n"
816 "Pot files are generated by the compilation process.\n"
817 "Your file should likely be named “%s.po”."), filename);
818 g_free (filename);
819 return;
820 }
821
822
823 if (is_read_only (filename))
824 {
825 g_set_error (error,
826 GTR_PO_ERROR,
827 GTR_PO_ERROR_READONLY,
828 _("The file %s is read-only, and can not be overwritten"),
829 filename);
830 g_free (filename);
831 return;
832 }
833
834 /* Save header fields into msg */
835 header = gtr_po_get_header (po);
836 gtr_header_update_header (header);
837
838 /*
839 * Check if the file is right
840 */
841 msg_error = gtr_po_check_po_file (po);
842 if (msg_error != NULL)
843 {
844 g_set_error (error,
845 GTR_PO_ERROR,
846 GTR_PO_ERROR_GETTEXT,
847 _("There is an error in the PO file: %s"),
848 msg_error);
849 g_free (msg_error);
850 g_free (filename);
851 return;
852 }
853
854 if (!po_file_write (gtr_po_get_po_file (po), filename, &handler))
855 {
856 g_set_error (error,
857 GTR_PO_ERROR,
858 GTR_PO_ERROR_FILENAME,
859 _("There was an error writing the PO file: %s"),
860 message_error);
861 g_free (message_error);
862 g_free (filename);
863 return;
864 }
865 g_free (filename);
866
867 /* If we are here everything is ok and we can set the state as saved */
868 gtr_po_set_state (po, GTR_PO_STATE_SAVED);
869
870 /*
871 * If the warn if fuzzy option is enabled we have to show an error
872 */
873 /*if (gtr_prefs_manager_get_warn_if_fuzzy () && priv->fuzzy)
874 {
875 g_set_error (error,
876 GTR_PO_ERROR,
877 GTR_PO_ERROR_OTHER,
878 ngettext ("File %s\ncontains %d fuzzy message",
879 "File %s\ncontains %d fuzzy messages",
880 priv->fuzzy),
881 priv->fuzzy);
882 } */
883 }
884
885 /**
886 * gtr_po_get_location:
887 * @po: a #GtrPo
888 *
889 * Gets the GFile of the po file.
890 *
891 * Returns: (transfer full): the GFile associated with the @po. The returned
892 * location must be freed with g_object_unref.
893 **/
894 GFile *
gtr_po_get_location(GtrPo * po)895 gtr_po_get_location (GtrPo * po)
896 {
897 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
898 g_return_val_if_fail (GTR_IS_PO (po), NULL);
899
900 return g_file_dup (priv->location);
901 }
902
903 /**
904 * gtr_po_set_location:
905 * @po: a #GtrPo
906 * @location: The GFile to set to the #GtrPo
907 *
908 * Sets the GFile location within the #GtrPo object.
909 **/
910 void
gtr_po_set_location(GtrPo * po,GFile * location)911 gtr_po_set_location (GtrPo * po, GFile * location)
912 {
913 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
914 g_return_if_fail (GTR_IS_PO (po));
915
916 if (priv->location)
917 {
918 if (g_file_equal (priv->location, location))
919 return;
920 g_object_unref (priv->location);
921 }
922
923 priv->location = g_file_dup (location);
924
925 g_object_notify (G_OBJECT (po), "location");
926 }
927
928 /**
929 * gtr_po_get_state:
930 * @po: a #GtrPo
931 *
932 * Return value: the #GtrPoState value of the @po.
933 */
934 GtrPoState
gtr_po_get_state(GtrPo * po)935 gtr_po_get_state (GtrPo * po)
936 {
937 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
938 g_return_val_if_fail (GTR_IS_PO (po), 0);
939
940 return priv->state;
941 }
942
943 /**
944 * gtr_po_set_state:
945 * @po: a #GtrPo
946 * @state: a #GtrPoState
947 *
948 * Sets the state for a #GtrPo
949 */
950 void
gtr_po_set_state(GtrPo * po,GtrPoState state)951 gtr_po_set_state (GtrPo * po, GtrPoState state)
952 {
953 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
954 g_return_if_fail (GTR_IS_PO (po));
955
956 priv->state = state;
957
958 g_object_notify (G_OBJECT (po), "state");
959 }
960
gtr_po_set_dl_info(GtrPo * po,gchar * team,gchar * module_name,gchar * branch,gchar * domain,gchar * module_state)961 void gtr_po_set_dl_info (GtrPo * po, gchar * team, gchar * module_name,
962 gchar * branch, gchar * domain, gchar * module_state)
963 {
964 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
965 priv->dl_team = g_strdup (team);
966 priv->dl_module = g_strdup (module_name);
967 priv->dl_branch = g_strdup (branch);
968 priv->dl_domain = g_strdup (domain);
969 priv->dl_state = g_strdup (module_state);
970
971 gtr_header_set_field (priv->header, "X-DL-Team", team);
972 gtr_header_set_field (priv->header, "X-DL-Module", module_name);
973 gtr_header_set_field (priv->header, "X-DL-Branch", branch);
974 gtr_header_set_field (priv->header, "X-DL-Domain", domain);
975 gtr_header_set_field (priv->header, "X-DL-State", module_state);
976 }
977 /*
978 * FIXME: We are not using this func.
979 */
980 gboolean
gtr_po_get_write_perms(GtrPo * po)981 gtr_po_get_write_perms (GtrPo * po)
982 {
983 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
984 return priv->no_write_perms;
985 }
986
987 /**
988 * gtr_po_get_messages:
989 * @po: a #GtrPo
990 *
991 * Return value: (transfer container) (element-type Gtranslator.Msg):
992 * a pointer to the messages list
993 **/
994 GList *
gtr_po_get_messages(GtrPo * po)995 gtr_po_get_messages (GtrPo * po)
996 {
997 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
998 g_return_val_if_fail (GTR_IS_PO (po), NULL);
999
1000 return priv->messages;
1001 }
1002
1003 /* FIXME: this is hack, we should fix it */
1004 /**
1005 * gtr_po_set_messages:
1006 * @po: a #GtrPo
1007 * @messages: (element-type Gtranslator.Msg): a pointer to a new messages list.
1008 *
1009 * Sets an updated list of messages.
1010 **/
1011 void
gtr_po_set_messages(GtrPo * po,GList * messages)1012 gtr_po_set_messages (GtrPo * po, GList * messages)
1013 {
1014 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1015 g_return_if_fail (GTR_IS_PO (po));
1016
1017 priv->messages = messages;
1018 }
1019
1020 /**
1021 * gtr_po_get_current_message:
1022 * @po: a #GtrPo
1023 *
1024 * Return value: (transfer none) (element-type Gtranslator.Msg):
1025 * a pointer to the current message
1026 **/
1027 GList *
gtr_po_get_current_message(GtrPo * po)1028 gtr_po_get_current_message (GtrPo * po)
1029 {
1030 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1031 return priv->current;
1032 }
1033
1034 /**
1035 * gtr_po_update_current_message:
1036 * @po: a #GtrPo
1037 * @msg: the message where should point the current message.
1038 *
1039 * Sets the new current message to the message that is passed in
1040 * the argument.
1041 **/
1042 void
gtr_po_update_current_message(GtrPo * po,GtrMsg * msg)1043 gtr_po_update_current_message (GtrPo * po, GtrMsg * msg)
1044 {
1045 gint i;
1046 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1047 i = g_list_index (priv->messages, msg);
1048 priv->current = g_list_nth (priv->messages, i);
1049 }
1050
1051 /**
1052 * gtr_po_get_domains:
1053 * @po: a #GtrPo
1054 *
1055 * Return value: (transfer none) (element-type utf8):
1056 * a pointer to the domains list
1057 **/
1058 GList *
gtr_po_get_domains(GtrPo * po)1059 gtr_po_get_domains (GtrPo * po)
1060 {
1061 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1062 return priv->domains;
1063 }
1064
1065 /**
1066 * gtr_po_get_po_file: (skip)
1067 * @po: a #GtrPo
1068 *
1069 * Gets the gettext file.
1070 *
1071 * Return value: the gettext file
1072 **/
1073 po_file_t
gtr_po_get_po_file(GtrPo * po)1074 gtr_po_get_po_file (GtrPo * po)
1075 {
1076 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1077 return priv->gettext_po_file;
1078 }
1079
1080 /**
1081 * gtr_po_get_next_fuzzy:
1082 * @po: a #GtrPo
1083 *
1084 * Return value: (transfer none) (element-type Gtranslator.Msg):
1085 * a pointer to the next fuzzy message
1086 **/
1087 GList *
gtr_po_get_next_fuzzy(GtrPo * po)1088 gtr_po_get_next_fuzzy (GtrPo * po)
1089 {
1090 GList *msg;
1091 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1092
1093 msg = priv->current;
1094 while ((msg = g_list_next (msg)))
1095 {
1096 if (gtr_msg_is_fuzzy (msg->data))
1097 return msg;
1098 }
1099
1100 return NULL;
1101 }
1102
1103
1104 /**
1105 * gtr_po_get_prev_fuzzy:
1106 * @po: a #GtrPo
1107 *
1108 * Return value: (transfer none) (element-type Gtranslator.Msg):
1109 * a pointer to the previously fuzzy message
1110 **/
1111 GList *
gtr_po_get_prev_fuzzy(GtrPo * po)1112 gtr_po_get_prev_fuzzy (GtrPo * po)
1113 {
1114 GList *msg;
1115 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1116
1117 msg = priv->current;
1118 while ((msg = g_list_previous (msg)))
1119 {
1120 if (gtr_msg_is_fuzzy (msg->data))
1121 return msg;
1122 }
1123
1124 return NULL;
1125 }
1126
1127
1128 /**
1129 * gtr_po_get_next_untrans:
1130 * @po: a #GtrPo
1131 *
1132 * Return value: (transfer none) (element-type Gtranslator.Msg):
1133 * a pointer to the next untranslated message
1134 **/
1135 GList *
gtr_po_get_next_untrans(GtrPo * po)1136 gtr_po_get_next_untrans (GtrPo * po)
1137 {
1138 GList *msg;
1139 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1140
1141 msg = priv->current;
1142 while ((msg = g_list_next (msg)))
1143 {
1144 if (!gtr_msg_is_translated (msg->data))
1145 return msg;
1146 }
1147
1148 return NULL;
1149 }
1150
1151
1152 /**
1153 * gtr_po_get_prev_untrans:
1154 * @po: a #GtrPo
1155 *
1156 * Return value: (transfer none) (element-type Gtranslator.Msg):
1157 * a pointer to the previously untranslated
1158 * message or NULL if there are not previously untranslated
1159 * message.
1160 **/
1161 GList *
gtr_po_get_prev_untrans(GtrPo * po)1162 gtr_po_get_prev_untrans (GtrPo * po)
1163 {
1164 GList *msg;
1165 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1166
1167 msg = priv->current;
1168 while ((msg = g_list_previous (msg)))
1169 {
1170 if (!gtr_msg_is_translated (msg->data))
1171 return msg;
1172 }
1173
1174 return NULL;
1175 }
1176
1177 /**
1178 * gtr_po_get_next_fuzzy_or_untrans:
1179 * @po: a #GtrPo
1180 *
1181 * Return value: (transfer none) (element-type Gtranslator.Msg):
1182 * a pointer to the next fuzzy or untranslated
1183 * message or NULL if there is not next fuzzy or untranslated
1184 * message.
1185 **/
1186 GList *
gtr_po_get_next_fuzzy_or_untrans(GtrPo * po)1187 gtr_po_get_next_fuzzy_or_untrans (GtrPo * po)
1188 {
1189 GList *msg;
1190 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1191
1192 msg = priv->current;
1193 while ((msg = g_list_next (msg)))
1194 {
1195 if (gtr_msg_is_fuzzy (msg->data) || !gtr_msg_is_translated (msg->data))
1196 return msg;
1197 }
1198
1199 return NULL;
1200 }
1201
1202 /**
1203 * gtr_po_get_prev_fuzzy_or_untrans:
1204 * @po: a #GtrPo
1205 *
1206 * Return value: (transfer none) (element-type Gtranslator.Msg):
1207 * a pointer to the previously fuzzy or
1208 * untranslated message or NULL if there is not previously
1209 * fuzzy or untranslated message.
1210 **/
1211 GList *
gtr_po_get_prev_fuzzy_or_untrans(GtrPo * po)1212 gtr_po_get_prev_fuzzy_or_untrans (GtrPo * po)
1213 {
1214 GList *msg;
1215 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1216
1217 msg = priv->current;
1218 while ((msg = g_list_previous (msg)))
1219 {
1220 if (gtr_msg_is_fuzzy (msg->data) || !gtr_msg_is_translated (msg->data))
1221 return msg;
1222 }
1223
1224 return NULL;
1225 }
1226
1227 /**
1228 * gtr_po_get_msg_from_number:
1229 * @po: a #GtrPo
1230 * @number: the message to jump
1231 *
1232 * Gets the message at the given position.
1233 *
1234 * Returns: (transfer none) (element-type Gtranslator.Msg):
1235 * the message at the given position.
1236 */
1237 GList *
gtr_po_get_msg_from_number(GtrPo * po,gint number)1238 gtr_po_get_msg_from_number (GtrPo * po, gint number)
1239 {
1240 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1241 g_return_val_if_fail (GTR_IS_PO (po), NULL);
1242
1243 return g_list_nth (priv->messages, number);
1244 }
1245
1246 /**
1247 * gtr_po_get_header:
1248 * @po: a #GtrPo
1249 *
1250 * Return value: (transfer none): The #GtrHeader of the @po.
1251 **/
1252 GtrHeader *
gtr_po_get_header(GtrPo * po)1253 gtr_po_get_header (GtrPo * po)
1254 {
1255 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1256 g_return_val_if_fail (GTR_IS_PO (po), NULL);
1257
1258 return priv->header;
1259 }
1260
1261 /**
1262 * gtr_po_get_translated_count:
1263 * @po: a #GtrPo
1264 *
1265 * Return value: the count of the translated messages.
1266 **/
1267 gint
gtr_po_get_translated_count(GtrPo * po)1268 gtr_po_get_translated_count (GtrPo * po)
1269 {
1270 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1271 g_return_val_if_fail (GTR_IS_PO (po), -1);
1272
1273 return priv->translated;
1274 }
1275
1276 /*
1277 * This func decrease or increase the count of translated
1278 * messages in 1.
1279 * This funcs must not be exported.
1280 */
1281 void
_gtr_po_increase_decrease_translated(GtrPo * po,gboolean increase)1282 _gtr_po_increase_decrease_translated (GtrPo * po, gboolean increase)
1283 {
1284 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1285 g_return_if_fail (GTR_IS_PO (po));
1286
1287 if (increase)
1288 priv->translated++;
1289 else
1290 priv->translated--;
1291 }
1292
1293 /**
1294 * gtr_po_get_fuzzy_count:
1295 * @po: a #GtrPo
1296 *
1297 * Return value: the count of the fuzzy messages.
1298 **/
1299 gint
gtr_po_get_fuzzy_count(GtrPo * po)1300 gtr_po_get_fuzzy_count (GtrPo * po)
1301 {
1302 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1303 g_return_val_if_fail (GTR_IS_PO (po), -1);
1304
1305 return priv->fuzzy;
1306 }
1307
1308 /*
1309 * This func decrease or increase the count of fuzzy
1310 * messages in 1.
1311 * This funcs must not be exported.
1312 */
1313 void
_gtr_po_increase_decrease_fuzzy(GtrPo * po,gboolean increase)1314 _gtr_po_increase_decrease_fuzzy (GtrPo * po, gboolean increase)
1315 {
1316 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1317 g_return_if_fail (GTR_IS_PO (po));
1318
1319 if (increase)
1320 priv->fuzzy++;
1321 else
1322 priv->fuzzy--;
1323 }
1324
1325 /**
1326 * gtr_po_get_untranslated_count:
1327 * @po: a #GtrPo
1328 *
1329 * Return value: the count of the untranslated messages.
1330 **/
1331 gint
gtr_po_get_untranslated_count(GtrPo * po)1332 gtr_po_get_untranslated_count (GtrPo * po)
1333 {
1334 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1335 g_return_val_if_fail (GTR_IS_PO (po), -1);
1336
1337 return (g_list_length (priv->messages) - priv->translated -
1338 priv->fuzzy);
1339 }
1340
1341 /**
1342 * gtr_po_get_messages_count:
1343 * @po: a #GtrPo
1344 *
1345 * Return value: the number of messages messages.
1346 **/
1347 gint
gtr_po_get_messages_count(GtrPo * po)1348 gtr_po_get_messages_count (GtrPo * po)
1349 {
1350 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1351 g_return_val_if_fail (GTR_IS_PO (po), -1);
1352
1353 return g_list_length (priv->messages);
1354 }
1355
1356 /**
1357 * gtr_po_get_message_position:
1358 * @po: a #GtrPo
1359 *
1360 * Return value: the number of the current message.
1361 **/
1362 gint
gtr_po_get_message_position(GtrPo * po)1363 gtr_po_get_message_position (GtrPo * po)
1364 {
1365 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1366 g_return_val_if_fail (GTR_IS_PO (po), -1);
1367
1368 return gtr_msg_get_po_position (GTR_MSG (priv->current->data));
1369 }
1370
1371 /**
1372 * gtr_po_check_po_file:
1373 * @po: a #GtrPo
1374 *
1375 * Test whether an entire PO file is valid, like msgfmt does it.
1376 * Returns: If it is invalid, returns the error. The return value must be freed
1377 * with g_free.
1378 **/
1379 gchar *
gtr_po_check_po_file(GtrPo * po)1380 gtr_po_check_po_file (GtrPo * po)
1381 {
1382 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1383 struct po_xerror_handler handler;
1384
1385 g_return_val_if_fail (po != NULL, NULL);
1386
1387 handler.xerror = &on_gettext_po_xerror;
1388 handler.xerror2 = &on_gettext_po_xerror2;
1389 message_error = NULL;
1390
1391 //TODO: handle error and mark wrong msgids
1392 po_file_check_all (priv->gettext_po_file, &handler);
1393
1394 return message_error;
1395 }
1396
1397 const gchar *
gtr_po_get_dl_team(GtrPo * po)1398 gtr_po_get_dl_team (GtrPo *po)
1399 {
1400 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1401 return priv->dl_team;
1402 }
1403
1404 const gchar *
gtr_po_get_dl_module(GtrPo * po)1405 gtr_po_get_dl_module (GtrPo *po)
1406 {
1407 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1408 return priv->dl_module;
1409 }
1410
1411 const gchar *
gtr_po_get_dl_branch(GtrPo * po)1412 gtr_po_get_dl_branch (GtrPo *po)
1413 {
1414 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1415 return priv->dl_branch;
1416 }
1417
1418 const gchar *
gtr_po_get_dl_domain(GtrPo * po)1419 gtr_po_get_dl_domain (GtrPo *po)
1420 {
1421 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1422 return priv->dl_domain;
1423 }
1424
1425 const gchar *
gtr_po_get_dl_module_state(GtrPo * po)1426 gtr_po_get_dl_module_state (GtrPo *po)
1427 {
1428 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1429 return priv->dl_state;
1430 }
1431
1432 gboolean
gtr_po_can_dl_upload(GtrPo * po)1433 gtr_po_can_dl_upload (GtrPo *po)
1434 {
1435 GtrPoPrivate *priv = gtr_po_get_instance_private (po);
1436 return g_strcmp0 (priv->dl_state, "Translating") == 0;
1437 }
1438