gtr-tab.c 47.1 KB
Newer Older
1
/*
2 3 4 5 6 7
 * Copyright (C) 2007  Ignacio Casal Quinteiro <nacho.resa@gmail.com>
 *                     Fatih Demir <kabalak@kabalak.net>
 * 		       Ross Golder <ross@golder.org>
 * 		       Gediminas Paulauskas <menesis@kabalak.net>
 * 		       homas Ziehmer <thomas@kabalak.net>
 *               2008  Igalia
8
 *
9 10 11 12
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
13
 * 
14 15 16 17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
18
 * 
19 20
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22 23 24 25 26 27 28
 * Authors:
 *   Ignacio Casal Quinteiro <nacho.resa@gmail.com>
 *   Pablo Sanxiao <psanxiao@gmail.com>
 *   Fatih Demir <kabalak@kabalak.net>
 *   Ross Golder <ross@golder.org>
 *   Gediminas Paulauskas <menesis@kabalak.net>
 *   Thomas Ziehmer <thomas@kabalak.net>
29 30 31 32 33 34
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

35 36 37 38 39
#include "gtr-application.h"
#include "gtr-context.h"
#include "gtr-io-error-info-bar.h"
#include "gtr-message-table.h"
#include "gtr-msg.h"
40
#include "gtr-tab-activatable.h"
41 42
#include "gtr-tab.h"
#include "gtr-po.h"
43
#include "gtr-settings.h"
44
#include "gtr-view.h"
45
#include "gtr-dirs.h"
46
#include "gtr-debug.h"
47
#include "gtr-window.h"
48
#include "gtr-progress.h"
49 50 51 52 53

#include <glib.h>
#include <glib-object.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
Ignacio Casal Quinteiro's avatar
Ignacio Casal Quinteiro committed
54
#include <gtksourceview/gtksource.h>
55

56 57
#define MAX_PLURALS 6

58
#define GTR_TAB_KEY "GtrTabFromDocument"
59

60
typedef struct
61
{
62
  GSettings *ui_settings;
63 64 65 66
  GSettings *files_settings;
  GSettings *editor_settings;
  GSettings *state_settings;

67 68 69 70 71 72 73 74 75
  GtkWidget *progress_eventbox;
  GtkWidget *progress_box;
  GtkWidget *progress_revealer;
  GtkWidget *progress_percentage;
  GtkWidget *progress_trans;
  GtkWidget *progress_fuzzy;
  GtkWidget *progress_untrans;

  GtrProgress *progress;
76
  gboolean find_replace_flag;
77

78 79
  GtrPo *po;

80
  GtkWidget *dock;
81

82
  GtkWidget *message_table;
83 84 85 86 87 88 89 90 91
  GtkWidget *context;

  /*Info bar */
  GtkWidget *infobar;

  /*Original text */
  GtkWidget *text_msgid;
  GtkWidget *text_plural_scroll;
  GtkWidget *text_msgid_plural;
92
  GtkWidget *msgid_tags;
93
  GtkWidget *msgid_ctxt;
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

  /*Translated text */
  GtkWidget *msgstr_label;
  GtkWidget *trans_notebook;
  GtkWidget *trans_msgstr[MAX_PLURALS];

  /*Status widgets */
  GtkWidget *translated;
  GtkWidget *fuzzy;
  GtkWidget *untranslated;

  /* Autosave */
  GTimer *timer;
  gint autosave_interval;
  guint autosave_timeout;
  guint autosave : 1;

  /*Blocking movement */
  guint blocking : 1;
113 114

  guint tab_realized : 1;
115
  guint dispose_has_run : 1;
116 117 118
} GtrTabPrivate;

G_DEFINE_TYPE_WITH_PRIVATE (GtrTab, gtr_tab, GTK_TYPE_BOX)
119 120 121 122 123 124

enum
{
  SHOWED_MESSAGE,
  MESSAGE_CHANGED,
  MESSAGE_EDITION_FINISHED,
125
  SELECTION_CHANGED,
126 127 128 129 130 131
  LAST_SIGNAL
};

enum
{
  PROP_0,
132
  PROP_NAME,
133 134 135 136 137 138 139 140
  PROP_AUTOSAVE,
  PROP_AUTOSAVE_INTERVAL
};

static guint signals[LAST_SIGNAL];

static gboolean gtr_tab_autosave (GtrTab * tab);

141 142 143 144 145 146 147 148 149 150 151 152 153
static gboolean
show_hide_revealer (GtkWidget *widget, GdkEvent *ev, GtrTab *tab)
{
  GtrTabPrivate *priv;
  GtkRevealer *rev;

  priv = gtr_tab_get_instance_private (tab);
  rev = GTK_REVEALER (priv->progress_revealer);
  gtk_revealer_set_reveal_child (rev, !gtk_revealer_get_reveal_child (rev));

  return TRUE;
}

154
static gboolean
155 156 157 158 159
msg_grab_focus (GtrTab *tab)
{
  GtrTabPrivate *priv;
  priv = gtr_tab_get_instance_private (tab);
  gtk_widget_grab_focus (priv->trans_msgstr[0]);
160
  return FALSE;
161 162
}

163 164
static void
install_autosave_timeout (GtrTab * tab)
165
{
166
  GtrTabPrivate *priv;
167
  gint timeout;
168

169 170 171 172
  priv = gtr_tab_get_instance_private (tab);
  g_return_if_fail (priv->autosave_timeout <= 0);
  g_return_if_fail (priv->autosave);
  g_return_if_fail (priv->autosave_interval > 0);
173

174
  /* Add a new timeout */
175
  timeout = g_timeout_add (priv->autosave_interval * 1000 * 60,
176
                           (GSourceFunc) gtr_tab_autosave, tab);
177

178
  priv->autosave_timeout = timeout;
179 180 181
}

static gboolean
182
install_autosave_timeout_if_needed (GtrTab * tab)
183
{
184
  GtrTabPrivate *priv;
185

186 187 188 189 190
  priv = gtr_tab_get_instance_private (tab);
  g_return_val_if_fail (priv->autosave_timeout <= 0, FALSE);

  if (priv->autosave)
  {
191 192 193
      install_autosave_timeout (tab);

      return TRUE;
194
  }
195 196

  return FALSE;
197 198 199
}

static gboolean
200
gtr_tab_autosave (GtrTab * tab)
201
{
202
  GError *error = NULL;
203
  GtrTabPrivate *priv;
204

205
  priv = gtr_tab_get_instance_private (tab);
206
  if (!(gtr_po_get_state (priv->po) == GTR_PO_STATE_MODIFIED))
207 208
    return TRUE;

209
  gtr_po_save_file (priv->po, &error);
210 211 212 213 214 215 216
  if (error)
    {
      g_warning ("%s", error->message);
      g_error_free (error);
    }

  return TRUE;
217 218 219
}

static void
220
remove_autosave_timeout (GtrTab * tab)
221
{
222 223 224 225
  GtrTabPrivate *priv;

  priv = gtr_tab_get_instance_private (tab);
  g_return_if_fail (priv->autosave_timeout > 0);
226

227 228
  g_source_remove (priv->autosave_timeout);
  priv->autosave_timeout = 0;
229 230
}

231
static void
232
gtr_tab_edition_finished (GtrTab * tab, GtrMsg * msg)
233
{
234
  gchar *message_error;
235
  GtkWidget *infobar;
236 237 238 239

  /*
   * Checking message
   */
240
  message_error = gtr_msg_check (msg);
241 242 243

  if (message_error != NULL)
    {
244
      gtr_tab_block_movement (tab);
245

246 247
      infobar = create_error_info_bar (_("There is an error in the message:"),
                                       message_error);
248
      gtr_tab_set_info_bar (tab, infobar);
249 250 251 252
      g_free (message_error);
    }
  else
    {
253 254
      gtr_tab_unblock_movement (tab);
      gtr_tab_set_info_bar (tab, NULL);
255
    }
256 257
}

258 259 260 261
/*
 * Write the change back to the gettext PO instance in memory and
 * mark the page dirty
 */
262
static void
263
gtr_message_translation_update (GtkTextBuffer * textbuffer, GtrTab * tab)
264
{
265
  GtrHeader *header;
266 267 268
  GtkTextIter start, end;
  GtkTextBuffer *buf;
  GList *msg_aux;
269
  GtrMsg *msg;
270
  GtrTabPrivate *priv;
271 272
  const gchar *check;
  gchar *translation;
273
  gboolean unmark_fuzzy;
274 275
  gint i;

276
  priv = gtr_tab_get_instance_private (tab);
277

278 279
  /* Work out which message this is associated with */
  header = gtr_po_get_header (priv->po);
280

281
  msg_aux = gtr_po_get_current_message (priv->po);
282
  msg = msg_aux->data;
283 284
  buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->trans_msgstr[0]));
  unmark_fuzzy = g_settings_get_boolean (priv->editor_settings,
285
                                         GTR_SETTINGS_UNMARK_FUZZY_WHEN_CHANGED);
286

287
  unmark_fuzzy = unmark_fuzzy && !priv->find_replace_flag;
288
  if (gtr_msg_is_fuzzy (msg) && unmark_fuzzy)
289
    gtr_msg_set_fuzzy (msg, FALSE);
290 291 292 293 294 295 296 297 298 299

  if (textbuffer == buf)
    {
      /* Get message as UTF-8 buffer */
      gtk_text_buffer_get_bounds (textbuffer, &start, &end);
      translation = gtk_text_buffer_get_text (textbuffer, &start, &end, TRUE);

      /* TODO: convert to file's own encoding if not UTF-8 */

      /* Write back to PO file in memory */
300
      if (!(check = gtr_msg_get_msgid_plural (msg)))
301 302 303
        {
          gtr_msg_set_msgstr (msg, translation);
        }
304
      else
305 306 307 308
        {
          gtr_msg_set_msgstr_plural (msg, 0, translation);
          //free(check);
        }
309 310 311 312
      g_free (translation);
      return;
    }
  i = 1;
313
  while (i < gtr_header_get_nplurals (header))
314 315
    {
      /* Know when to break out of the loop */
316
      if (!priv->trans_msgstr[i])
317 318 319
        {
          break;
        }
320 321 322

      /* Have we reached the one we want yet? */
      buf =
323
        gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->trans_msgstr[i]));
324
      if (textbuffer != buf)
325 326 327 328
        {
          i++;
          continue;
        }
329

330 331 332 333 334 335 336
      /* Get message as UTF-8 buffer */
      gtk_text_buffer_get_bounds (textbuffer, &start, &end);
      translation = gtk_text_buffer_get_text (textbuffer, &start, &end, TRUE);

      /* TODO: convert to file's own encoding if not UTF-8 */

      /* Write back to PO file in memory */
337
      gtr_msg_set_msgstr_plural (msg, i, translation);
338 339 340 341 342
      return;
    }

  /* Shouldn't get here */
  g_return_if_reached ();
343 344 345
}

static GtkWidget *
346
gtr_tab_append_msgstr_page (const gchar * tab_label,
347 348
                            GtkWidget * box, gboolean spellcheck,
                            GtrTab *tab)
349
{
350 351 352
  GtkWidget *scroll;
  GtkWidget *label;
  GtkWidget *widget;
353
  GtrTabPrivate *priv;
354 355 356 357 358 359

  label = gtk_label_new (tab_label);

  scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scroll);

360
  widget = gtr_view_new ();
361 362
  gtk_widget_show (widget);

363 364
  priv = gtr_tab_get_instance_private (tab);

365
  if (spellcheck &&
366
      g_settings_get_boolean (priv->editor_settings,
367
                              GTR_SETTINGS_SPELLCHECK))
368 369 370
    {
      gtr_view_enable_spellcheck (GTR_VIEW (widget), spellcheck);
    }
371 372 373 374

  gtk_container_add (GTK_CONTAINER (scroll), widget);

  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
375
                                       GTK_SHADOW_IN);
376 377 378 379

  gtk_notebook_append_page (GTK_NOTEBOOK (box), scroll, label);

  return widget;
380 381 382
}

static void
383
gtr_message_plural_forms (GtrTab * tab, GtrMsg * msg)
384
{
385
  GtrHeader *header;
386
  GtkTextBuffer *buf;
387
  GtrTabPrivate *priv;
388 389 390 391 392 393
  const gchar *msgstr_plural;
  gint i;

  g_return_if_fail (tab != NULL);
  g_return_if_fail (msg != NULL);

394 395 396
  priv = gtr_tab_get_instance_private (tab);

  header = gtr_po_get_header (priv->po);
397

398
  for (i = 0; i < gtr_header_get_nplurals (header); i++)
399
    {
400
      msgstr_plural = gtr_msg_get_msgstr_plural (msg, i);
401
      if (msgstr_plural)
402 403 404
        {
          buf =
            gtk_text_view_get_buffer (GTK_TEXT_VIEW
405
                                      (priv->trans_msgstr[i]));
406 407 408 409 410
          gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER
                                                       (buf));
          gtk_text_buffer_set_text (buf, (gchar *) msgstr_plural, -1);
          gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (buf));
        }
411
    }
412 413 414
}

/*
415
 * gtr_tab_show_message:
416
 * @tab: a #GtranslationTab
417
 * @msg: a #GtrMsg
418 419 420 421 422
 * 
 * Shows the @msg in the @tab TextViews
 *
 */
static void
423
gtr_tab_show_message (GtrTab * tab, GtrMsg * msg)
424
{
425
  GtrTabPrivate *priv;
426
  GtrPo *po;
427 428
  GtkTextBuffer *buf;
  const gchar *msgid, *msgid_plural;
429
  const gchar *msgstr;
430
  const gchar *msgctxt;
431 432 433

  g_return_if_fail (GTR_IS_TAB (tab));

434
  priv = gtr_tab_get_instance_private (tab);
435
  gtk_label_set_text (GTK_LABEL (priv->msgid_tags), "");
436

437 438 439
  msgctxt = gtr_msg_get_msgctxt (msg);
  if (msgctxt)
   {
440 441
     gtk_label_set_text (GTK_LABEL (priv->msgid_ctxt), msgctxt);
     gtk_widget_show (priv->msgid_ctxt);
442 443 444
   }
  else
   {
445
     gtk_widget_hide (priv->msgid_ctxt);
446 447
   }

448
  po = priv->po;
449 450
  gtr_po_update_current_message (po, msg);
  msgid = gtr_msg_get_msgid (msg);
451 452 453 454 455 456
  if (msgid)
    {
      buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_msgid));
      gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (buf));
      gtk_text_buffer_set_text (buf, (gchar *) msgid, -1);
      gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (buf));
457 458 459

      if (gtr_msg_is_fuzzy (msg))
        gtk_label_set_text (GTK_LABEL (priv->msgid_tags), _("fuzzy"));
460
    }
461
  msgid_plural = gtr_msg_get_msgid_plural (msg);
462 463
  if (!msgid_plural)
    {
464
      msgstr = gtr_msg_get_msgstr (msg);
465 466 467 468 469 470 471
      /*
       * Disable notebook tabs and hide widgets
       */
      gtk_widget_hide (priv->text_plural_scroll);
      gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->trans_notebook), FALSE);
      gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->trans_notebook), 0);
      if (msgstr)
472 473 474 475 476 477 478 479 480 481
        {
          buf =
            gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->trans_msgstr[0]));
          gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER
                                                       (buf));
          gtk_text_buffer_set_text (buf, (gchar *) msgstr, -1);
          gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (buf));
          gtk_label_set_mnemonic_widget (GTK_LABEL (priv->msgstr_label),
                                         priv->trans_msgstr[0]);
        }
482 483 484 485
    }
  else
    {
      gtk_widget_show (priv->text_plural_scroll);
486
      gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->trans_notebook),
487
                                  TRUE);
488
      buf =
489
        gtk_text_view_get_buffer (GTK_TEXT_VIEW
490
                                  (priv->text_msgid_plural));
491
      gtk_text_buffer_set_text (buf, (gchar *) msgid_plural, -1);
492
      gtr_message_plural_forms (tab, msg);
493
    }
494 495 496
}

static void
497
emit_message_changed_signal (GtkTextBuffer * buf, GtrTab * tab)
498
{
499
  GList *msg;
500
  GtrTabPrivate *priv;
501

502 503
  priv = gtr_tab_get_instance_private (tab);
  msg = gtr_po_get_current_message (priv->po);
504 505

  g_signal_emit (G_OBJECT (tab), signals[MESSAGE_CHANGED], 0, msg->data);
506 507
}

508 509 510 511 512 513
static void
emit_selection_changed (GtkTextBuffer * buf, GParamSpec * spec, GtrTab * tab)
{
  g_signal_emit (G_OBJECT (tab), signals[SELECTION_CHANGED], 0);
}

514
static void
515
update_status (GtrTab * tab, GtrMsg * msg, gpointer useless)
516
{
517 518
  GtrMsgStatus status;
  GtrPoState po_state;
519
  GtrTabPrivate *priv;
520 521
  gboolean fuzzy, translated;

522 523
  priv = gtr_tab_get_instance_private (tab);

524
  status = gtr_msg_get_status (msg);
525
  po_state = gtr_po_get_state (priv->po);
526

527 528
  fuzzy = gtr_msg_is_fuzzy (msg);
  translated = gtr_msg_is_translated (msg);
529

530
  if ((status == GTR_MSG_STATUS_FUZZY) && !fuzzy && !priv->find_replace_flag)
531
    {
532
      _gtr_po_increase_decrease_fuzzy (priv->po, FALSE);
533
      if (translated)
534 535
        {
          status = GTR_MSG_STATUS_TRANSLATED;
536
          _gtr_po_increase_decrease_translated (priv->po, TRUE);
537
        }
538
      else
539 540 541
        {
          status = GTR_MSG_STATUS_UNTRANSLATED;
        }
542 543 544 545
    }
  else if ((status == GTR_MSG_STATUS_TRANSLATED) && !translated)
    {
      status = GTR_MSG_STATUS_UNTRANSLATED;
546
      _gtr_po_increase_decrease_translated (priv->po, FALSE);
547 548 549 550
    }
  else if ((status == GTR_MSG_STATUS_TRANSLATED) && fuzzy)
    {
      status = GTR_MSG_STATUS_FUZZY;
551 552
      _gtr_po_increase_decrease_translated (priv->po, FALSE);
      _gtr_po_increase_decrease_fuzzy (priv->po, TRUE);
553 554 555 556
    }
  else if ((status == GTR_MSG_STATUS_UNTRANSLATED) && translated)
    {
      if (fuzzy)
557 558
        {
          status = GTR_MSG_STATUS_FUZZY;
559
          _gtr_po_increase_decrease_fuzzy (priv->po, TRUE);
560
        }
561
      else
562 563
        {
          status = GTR_MSG_STATUS_TRANSLATED;
564
          _gtr_po_increase_decrease_translated (priv->po, TRUE);
565
        }
566 567
    }

568 569 570 571 572 573
  if (status != gtr_msg_get_status (msg))
    {
      gtr_msg_set_status (msg, status);
      g_signal_emit (G_OBJECT (tab), signals[MESSAGE_CHANGED], 0, msg);
    }

574 575 576 577
  if (gtr_msg_is_fuzzy (msg))
    gtk_label_set_text (GTK_LABEL (priv->msgid_tags), _("fuzzy"));
  else
    gtk_label_set_text (GTK_LABEL (priv->msgid_tags), "");
578

579 580
  /* We need to update the tab state too if is neccessary */
  if (po_state != GTR_PO_STATE_MODIFIED)
581
    gtr_po_set_state (priv->po, GTR_PO_STATE_MODIFIED);
582 583
}

584
static void
585
gtr_tab_add_msgstr_tabs (GtrTab * tab)
586
{
587
  GtrHeader *header;
588
  GtrTabPrivate *priv;
589 590 591
  gchar *label;
  GtkTextBuffer *buf;
  gint i = 0;
592
  gchar *lang_code = NULL;
593

594 595
  priv = gtr_tab_get_instance_private (tab);

596 597 598
  /*
   * We get the header of the po file
   */
599
  header = gtr_po_get_header (priv->po);
600
  lang_code = gtr_header_get_language_code (header);
601 602 603

  do
    {
604

605
      label = g_strdup_printf (_("Plural %d"), i);
606
      priv->trans_msgstr[i] = gtr_tab_append_msgstr_page (label,
607
                                                          priv->trans_notebook,
608 609
                                                          TRUE,
                                                          tab);
610 611 612

      gtr_view_set_language (GTR_VIEW (priv->trans_msgstr[i]), lang_code);

613 614
      buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->trans_msgstr[i]));
      g_signal_connect (buf, "end-user-action",
615
                        G_CALLBACK (gtr_message_translation_update), tab);
616 617

      g_signal_connect_after (buf, "end_user_action",
618
                              G_CALLBACK (emit_message_changed_signal), tab);
619 620
      g_signal_connect (buf, "notify::has-selection",
                        G_CALLBACK (emit_selection_changed), tab);
621 622 623
      i++;
      g_free (label);
    }
624
  while (i < gtr_header_get_nplurals (header));
625
  g_free (lang_code);
626 627
}

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
static void
on_location_notify (GtrPo      *po,
                    GParamSpec *pspec,
                    GtrTab     *tab)
{
  g_object_notify (G_OBJECT (tab), "name");
}

static void
on_state_notify (GtrPo      *po,
                 GParamSpec *pspec,
                 GtrTab     *tab)
{
  g_object_notify (G_OBJECT (tab), "name");
}

644 645
static void
gtr_tab_init (GtrTab * tab)
646
{
647 648
  GtrTabPrivate *priv;

649
  priv = gtr_tab_get_instance_private (tab);
650

651 652
  gtk_widget_init_template (GTK_WIDGET (tab));

653 654 655 656 657 658 659
  priv->ui_settings = g_settings_new ("org.gnome.gtranslator.preferences.ui");
  priv->files_settings = g_settings_new ("org.gnome.gtranslator.preferences.files");
  priv->editor_settings = g_settings_new ("org.gnome.gtranslator.preferences.editor");
  priv->state_settings = g_settings_new ("org.gnome.gtranslator.state.window");

  g_signal_connect (tab, "message-changed", G_CALLBACK (update_status), NULL);

660
  /* Manage auto save data */
661 662 663
  priv->autosave = g_settings_get_boolean (priv->files_settings,
                                           GTR_SETTINGS_AUTO_SAVE);
  priv->autosave = (priv->autosave != FALSE);
664

665 666 667 668
  priv->autosave_interval = g_settings_get_int (priv->files_settings,
                                                GTR_SETTINGS_AUTO_SAVE_INTERVAL);
  if (priv->autosave_interval <= 0)
    priv->autosave_interval = 1;
669

670
  priv->find_replace_flag = FALSE;
671 672 673 674 675 676
  priv->progress = gtr_progress_new ();
  gtk_widget_show (GTK_WIDGET (priv->progress));
  gtk_container_add (GTK_CONTAINER (priv->progress_box), GTK_WIDGET (priv->progress));

  g_signal_connect (priv->progress_eventbox, "button-press-event",
                    G_CALLBACK (show_hide_revealer), tab);
677 678 679
}

static void
680
gtr_tab_finalize (GObject * object)
681
{
682
  GtrTab *tab = GTR_TAB (object);
683
  GtrTabPrivate *priv;
684

685
  priv = gtr_tab_get_instance_private (tab);
686

687 688
  if (priv->timer != NULL)
    g_timer_destroy (priv->timer);
689

690
  if (priv->autosave_timeout > 0)
691 692
    remove_autosave_timeout (tab);

693
  G_OBJECT_CLASS (gtr_tab_parent_class)->finalize (object);
694 695
}

696 697 698
static void
gtr_tab_dispose (GObject * object)
{
699
  GtrTabPrivate *priv;
700

701 702
  priv = gtr_tab_get_instance_private (GTR_TAB (object));

703 704 705 706 707
  g_clear_object (&priv->po);
  g_clear_object (&priv->ui_settings);
  g_clear_object (&priv->files_settings);
  g_clear_object (&priv->editor_settings);
  g_clear_object (&priv->state_settings);
708 709 710 711

  G_OBJECT_CLASS (gtr_tab_parent_class)->dispose (object);
}

712
static void
713
gtr_tab_get_property (GObject * object,
714
                      guint prop_id, GValue * value, GParamSpec * pspec)
715
{
716
  GtrTab *tab = GTR_TAB (object);
717 718 719 720

  switch (prop_id)
    {
    case PROP_AUTOSAVE:
721
      g_value_set_boolean (value, gtr_tab_get_autosave_enabled (tab));
722 723
      break;
    case PROP_AUTOSAVE_INTERVAL:
724
      g_value_set_int (value, gtr_tab_get_autosave_interval (tab));
725 726 727 728 729
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
730 731 732
}

static void
733
gtr_tab_set_property (GObject * object,
734
                      guint prop_id, const GValue * value, GParamSpec * pspec)
735
{
736
  GtrTab *tab = GTR_TAB (object);
737 738 739 740

  switch (prop_id)
    {
    case PROP_AUTOSAVE:
741
      gtr_tab_set_autosave_enabled (tab, g_value_get_boolean (value));
742 743
      break;
    case PROP_AUTOSAVE_INTERVAL:
744
      gtr_tab_set_autosave_interval (tab, g_value_get_int (value));
745 746 747 748 749
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
750 751
}

752 753 754
static void
gtr_tab_realize (GtkWidget *widget)
{
755
  GTK_WIDGET_CLASS (gtr_tab_parent_class)->realize (widget);
756 757
}

758
static void
759
gtr_tab_class_init (GtrTabClass * klass)
760
{
761
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
762
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
763

764
  object_class->finalize = gtr_tab_finalize;
765
  object_class->dispose = gtr_tab_dispose;
766 767
  object_class->set_property = gtr_tab_set_property;
  object_class->get_property = gtr_tab_get_property;
768 769 770

  widget_class->realize = gtr_tab_realize;

771
  klass->message_edition_finished = gtr_tab_edition_finished;
772 773 774 775

  /* Signals */
  signals[SHOWED_MESSAGE] =
    g_signal_new ("showed-message",
776 777 778 779
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtrTabClass, showed_message),
                  NULL, NULL,
780 781
                  g_cclosure_marshal_VOID__OBJECT,
                  G_TYPE_NONE, 1, GTR_TYPE_MSG);
782 783 784

  signals[MESSAGE_CHANGED] =
    g_signal_new ("message-changed",
785 786 787 788
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtrTabClass, message_changed),
                  NULL, NULL,
789 790
                  g_cclosure_marshal_VOID__OBJECT,
                  G_TYPE_NONE, 1, GTR_TYPE_MSG);
791 792 793

  signals[MESSAGE_EDITION_FINISHED] =
    g_signal_new ("message-edition-finished",
794 795
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
796 797 798 799
                  G_STRUCT_OFFSET (GtrTabClass, message_edition_finished),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__OBJECT,
                  G_TYPE_NONE, 1, GTR_TYPE_MSG);
800 801 802 803 804 805 806 807
  signals[SELECTION_CHANGED] =
    g_signal_new ("selection-changed",
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtrTabClass, selection_changed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
808 809

  /* Properties */
810 811 812 813 814 815 816 817 818
  g_object_class_install_property (object_class,
                                   PROP_NAME,
                                   g_param_spec_string ("name",
                                                        "Name",
                                                        "The tab's name",
                                                        NULL,
                                                        G_PARAM_READABLE |
                                                        G_PARAM_STATIC_STRINGS));

819
  g_object_class_install_property (object_class,
820 821 822 823 824 825 826
                                   PROP_AUTOSAVE,
                                   g_param_spec_boolean ("autosave",
                                                         "Autosave",
                                                         "Autosave feature",
                                                         TRUE,
                                                         G_PARAM_READWRITE |
                                                         G_PARAM_STATIC_STRINGS));
827 828

  g_object_class_install_property (object_class,
829 830 831 832 833 834 835 836 837
                                   PROP_AUTOSAVE_INTERVAL,
                                   g_param_spec_int ("autosave-interval",
                                                     "AutosaveInterval",
                                                     "Time between two autosaves",
                                                     0,
                                                     G_MAXINT,
                                                     0,
                                                     G_PARAM_READWRITE |
                                                     G_PARAM_STATIC_STRINGS));
838 839

  gtk_widget_class_set_template_from_resource (widget_class,
Seán de Búrca's avatar
Seán de Búrca committed
840
                                               "/org/gnome/translator/gtr-tab.ui");
841 842 843

  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, message_table);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, text_msgid);
844
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, msgid_tags);
845
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, msgid_ctxt);
846 847
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, text_plural_scroll);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, text_msgid_plural);
848
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, msgstr_label);
849 850
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, trans_notebook);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, context);
851

852 853 854 855 856 857 858 859
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_eventbox);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_box);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_revealer);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_trans);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_fuzzy);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_untrans);
  gtk_widget_class_bind_template_child_private (widget_class, GtrTab, progress_percentage);

860 861 862
  g_type_ensure (gtr_view_get_type ());
  g_type_ensure (gtr_context_panel_get_type ());
  g_type_ensure (gtr_message_table_get_type ());
863 864 865 866 867
}

/***************************** Public funcs ***********************************/

/**
868
 * gtr_tab_new:
869
 * @po: a #GtrPo
870
 * @window: a #GtkWindow
871
 * 
872
 * Creates a new #GtrTab.
873
 * 
874
 * Return value: a new #GtrTab object
875
 **/
876
GtrTab *
877 878
gtr_tab_new (GtrPo * po,
             GtkWindow *window)
879
{
880
  GtrTab *tab;
881
  GtrTabPrivate *priv;
882 883 884 885 886

  g_return_val_if_fail (po != NULL, NULL);

  tab = g_object_new (GTR_TYPE_TAB, NULL);

887
  priv = gtr_tab_get_instance_private (tab);
888 889
  gtr_context_init_tm (GTR_CONTEXT_PANEL (priv->context),
                       gtr_window_get_tm (GTR_WINDOW (window)));
890

891
  /* FIXME: make the po a property */
892
  priv->po = po;
893
  g_object_set_data (G_OBJECT (po), GTR_TAB_KEY, tab);
894 895 896 897 898 899 900

  g_signal_connect (po, "notify::location",
                    G_CALLBACK (on_location_notify), tab);

  g_signal_connect (po, "notify::state",
                    G_CALLBACK (on_state_notify), tab);

901 902
  install_autosave_timeout_if_needed (tab);

903
  /* Now we have to initialize the number of msgstr tabs */
904
  gtr_tab_add_msgstr_tabs (tab);
905

906 907
  gtr_message_table_populate (GTR_MESSAGE_TABLE (priv->message_table),
                              GTR_MESSAGE_CONTAINER (priv->po));
908 909 910

  gtk_widget_show (GTK_WIDGET (tab));
  return tab;
911 912 913
}

/**
914
 * gtr_tab_get_po:
915
 * @tab: a #GtrTab
916
 *
917
 * Return value: (transfer none): the #GtrPo stored in the #GtrTab
918
**/
919
GtrPo *
920
gtr_tab_get_po (GtrTab * tab)
921
{
922 923 924 925
  GtrTabPrivate *priv;

  priv = gtr_tab_get_instance_private (tab);
  return priv->po;
926 927
}

928
/**
929
 * gtr_tab_get_active_trans_tab:
930 931 932 933 934
 * @tab: a #GtranslationTab
 * 
 * Return value: the number of the active translation notebook.
 **/
gint
935
gtr_tab_get_active_trans_tab (GtrTab * tab)
936
{
937 938 939
  GtrTabPrivate *priv;

  priv = gtr_tab_get_instance_private (tab);
940
  return
941
    gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->trans_notebook));
942 943 944
}

/**
945
 * gtr_tab_get_context_panel:
946
 * @tab: a #GtrTab
947
 *
948
 * Return value: (transfer none): the #GtranslaorContextPanel
949
 */
950
GtrContextPanel *
951
gtr_tab_get_context_panel (GtrTab * tab)
952
{
953 954 955 956
  GtrTabPrivate *priv;

  priv = gtr_tab_get_instance_private (tab);
  return GTR_CONTEXT_PANEL (priv->context);
957 958 959
}

/**
960
 * gtr_tab_get_active_view:
961 962
 * @tab: a #GtranslationTab
 *
963
 * Return value: (transfer none): the active page of the translation notebook.
964
**/
965
GtrView *
966
gtr_tab_get_active_view (GtrTab * tab)
967
{
968
  GtrTabPrivate *priv;
969 970
  gint num;

971 972
  priv = gtr_tab_get_instance_private (tab);

973
  num =
974 975
    gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->trans_notebook));
  return GTR_VIEW (priv->trans_msgstr[num]);
976 977 978
}

/**
979
 * gtr_tab_get_all_views:
980 981 982 983 984 985
 * @tab: the #GtranslationTab
 * @original: TRUE if you want original TextViews.
 * @translated: TRUE if you want tranlated TextViews.
 *
 * Returns all the views currently present in #GtranslationTab
 *
986 987
 * Returns: (transfer container) (element-type Gtranslator.View):
 *          a newly allocated list of #GtranslationTab objects
988 989
 */
GList *
990
gtr_tab_get_all_views (GtrTab * tab, gboolean original, gboolean translated)
991
{
992
  GList *ret = NULL;
993
  GtrTabPrivate *priv;
994 995 996 997
  gint i = 0;

  g_return_val_if_fail (GTR_IS_TAB (tab), NULL);

998
  priv = gtr_tab_get_instance_private (tab);
999 1000
  if (original)
    {
1001 1002
      ret = g_list_append (ret, priv->text_msgid);
      ret = g_list_append (ret, priv->text_msgid_plural);
1003 1004 1005 1006 1007
    }

  if (translated)
    {
      while (i < MAX_PLURALS)
1008
        {
1009 1010
          if (priv->trans_msgstr[i])
            ret = g_list_append (ret, priv->trans_msgstr[i]);
1011 1012 1013 1014
          else
            break;
          i++;
        }
1015 1016 1017
    }

  return ret;
1018 1019 1020
}

/**
1021
 * gtr_tab_message_go_to:
1022 1023
 * @tab: a #GtrTab
 * @to_go: the #GtrMsg you want to jump
1024
 * @searching: TRUE if we are searching in the message list
1025 1026
 *
 * Jumps to the specific @to_go pointer message and show the message
1027
 * in the #GtrView.
1028
**/
1029
void
1030
gtr_tab_message_go_to (GtrTab * tab,
1031
                       GtrMsg * to_go, gboolean searching, GtrTabMove move)
1032
{
1033
  static gboolean first_msg = TRUE;
1034
  GtrTabPrivate *priv;
1035 1036

  g_return_if_fail (tab != NULL);
1037
  g_return_if_fail (GTR_IS_MSG (to_go));
1038

1039 1040
  priv = gtr_tab_get_instance_private (tab);

1041
  if (!priv->blocking || first_msg)
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    {
      gboolean plurals;
      gint current_page, n_pages;
      /*
       * If the current message is plural and we press next/prev
       * we have to change to the next/prev plural tab in case is not
       * the last
       * To implement that:
       * if the tabs are showed then we check if we want prev or 
       * next and then if we need to change the tab we change it 
       * in other case we show the message
       * 
       * I don't like too much this implementation so if anybody can
       * rewrite this is a better way would be great.
       */
      plurals =
1058
        gtk_notebook_get_show_tabs (GTK_NOTEBOOK (priv->trans_notebook));
1059
      current_page =
1060
        gtk_notebook_get_current_page (GTK_NOTEBOOK
1061
                                       (priv->trans_notebook));
1062
      n_pages =
1063
        gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->trans_notebook));
1064
      if ((plurals == TRUE) && (move != GTR_TAB_MOVE_NONE))
1065 1066 1067 1068
        {
          if ((n_pages - 1) == current_page && move == GTR_TAB_MOVE_NEXT)
            {
              gtk_notebook_set_current_page (GTK_NOTEBOOK
1069
                                             (priv->trans_notebook), 0);
1070
              gtr_tab_show_message (tab, to_go);
1071 1072 1073 1074
            }
          else if (current_page == 0 && move == GTR_TAB_MOVE_PREV)
            {
              gtk_notebook_set_current_page (GTK_NOTEBOOK
1075
                                             (priv->trans_notebook),
1076
                                             n_pages - 1);
1077
              gtr_tab_show_message (tab, to_go);
1078 1079 1080 1081 1082
            }
          else
            {
              if (move == GTR_TAB_MOVE_NEXT)
                gtk_notebook_set_current_page (GTK_NOTEBOOK
1083
                                               (priv->trans_notebook),
1084 1085 1086
                                               current_page + 1);
              else
                gtk_notebook_set_current_page (GTK_NOTEBOOK
1087
                                               (priv->trans_notebook),
1088 1089 1090 1091
                                               current_page - 1);
              return;
            }
        }
1092
      else
1093
        gtr_tab_show_message (tab, to_go);
1094 1095 1096 1097 1098
      first_msg = FALSE;
    }
  else
    return;

1099 1100 1101 1102 1103
  // Grabbing the focus in the GtrView to edit the message
  // This is done in the idle add to avoid the focus grab from the
  // message-table
  g_idle_add((GSourceFunc)msg_grab_focus, tab);

1104 1105 1106 1107 1108
  /*
   * Emitting showed-message signal
   */
  if (!searching)
    g_signal_emit (G_OBJECT (tab), signals[SHOWED_MESSAGE], 0,
1109
                   GTR_MSG (to_go));
1110
}
1111

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
/**
 * _gtr_tab_get_name:
 * @tab: a #GtrTab 
 * 
 * Return value: a new allocated string with the name of the @tab.
 */
gchar *
_gtr_tab_get_name (GtrTab *tab)
{
  GtrHeader *header;
  GtrPoState state;
1123
  GtrTabPrivate *priv;
1124 1125 1126
  const gchar *str;
  gchar *tab_name;

1127 1128 1129 1130
  priv = gtr_tab_get_instance_private (tab);

  header = gtr_po_get_header (priv->po);
  state = gtr_po_get_state (priv->po);
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146

  str = gtr_header_get_prj_id_version (header);

  if (state == GTR_PO_STATE_MODIFIED)
    {
      tab_name = g_strdup_printf ("*%s", str);
      return tab_name;
    }

  return g_strdup (str);
}

gchar *
_gtr_tab_get_tooltips (GtrTab *tab)
{
  GFile *location;
1147
  GtrTabPrivate *priv;
1148 1149 1150
  gchar *tooltips;
  gchar *path;

1151 1152 1153
  priv = gtr_tab_get_instance_private (tab);

  location = gtr_po_get_location (priv->po);
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
  path = g_file_get_path (location);
  g_object_unref (location);

  /* Translators: Path to the document opened */
  tooltips = g_strdup_printf ("<b>%s</b> %s", _("Path:"), path);
  g_free (path);

  return tooltips;
}

1164
/**
1165
 * _gtr_tab_can_close:
1166
 * @tab: a #GtrTab
1167
 *
1168
 * Whether a #GtrTab can be closed.
1169
 *
1170
 * Returns: TRUE if the #GtrPo of the @tab is already saved
1171
 */
1172
gboolean
1173
_gtr_tab_can_close (GtrTab * tab)
1174
{
1175 1176 1177 1178
  GtrTabPrivate *priv;

  priv = gtr_tab_get_instance_private (tab);
  return gtr_po_get_state (priv->po) == GTR_PO_STATE_SAVED;
1179 1180
}

1181
/**
1182
 * gtr_tab_get_from_document:
1183
 * @po: a #GtrPo
1184
 *
1185
 * Returns the #GtrTab for a specific #GtrPo.
1186
 *
1187
 * Returns: (transfer none): the #GtrTab for a specific #GtrPo
1188
 */
1189
GtrTab *
1190
gtr_tab_get_from_document (GtrPo * po)
1191
{
1192 1193 1194 1195 1196 1197 1198
  gpointer res;

  g_return_val_if_fail (GTR_IS_PO (po), NULL);

  res = g_object_get_data (G_OBJECT (po), GTR_TAB_KEY);

  return (res != NULL) ? GTR_TAB (res) : NULL;
1199
}