~ubuntu-branches/ubuntu/precise/latexila/precise

« back to all changes in this revision

Viewing changes to src/log.c

  • Committer: Bazaar Package Importer
  • Author(s): Tanguy Ortolo
  • Date: 2010-04-26 22:13:00 UTC
  • Revision ID: james.westby@ubuntu.com-20100426221300-6pa79a1yk5tino7y
Tags: upstream-0.2.0
ImportĀ upstreamĀ versionĀ 0.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is part of LaTeXila.
 
3
 *
 
4
 * Copyright Ā© 2010 SĆ©bastien Wilmet
 
5
 *
 
6
 * LaTeXila is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * LaTeXila is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <stdlib.h>
 
21
#include <string.h>
 
22
#include <gtk/gtk.h>
 
23
 
 
24
#include "main.h"
 
25
#include "log.h"
 
26
#include "utils.h"
 
27
#include "callbacks.h"
 
28
#include "print.h"
 
29
 
 
30
static void cb_action_history_changed (GtkTreeSelection *selection,
 
31
                gpointer user_data);
 
32
static GtkListStore * get_new_output_list_store (void);
 
33
static void go_to_message (gboolean next, gint message_type);
 
34
static void scroll_to_iter (GtkTreeIter *iter, gboolean force);
 
35
static gboolean output_row_selection_func (GtkTreeSelection *selection,
 
36
                GtkTreeModel *model, GtkTreePath *path,
 
37
                gboolean path_currently_selected, gpointer data);
 
38
static void select_row (GtkTreeModel *model, GtkTreeIter *iter);
 
39
static void jump_to_file (void);
 
40
static void index_messages_by_type (GtkTreeModel *model);
 
41
static void set_previous_next_actions_sensitivity (void);
 
42
static void path_free (gpointer data);
 
43
static void index_free (gpointer data);
 
44
 
 
45
static GtkTreeView *history_view;
 
46
static GtkTreeView *output_view;
 
47
 
 
48
// used to scroll to the end and to flush not everytime (because it is slow)
 
49
static gint nb_lines = 0;
 
50
 
 
51
void
 
52
init_log_zone (GtkPaned *log_hpaned, GtkWidget *log_toolbar)
 
53
{
 
54
        /* action history */
 
55
        {
 
56
                GtkListStore *history_list_store = gtk_list_store_new (N_COLS_ACTION,
 
57
                                G_TYPE_STRING, G_TYPE_POINTER);
 
58
                
 
59
                history_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
 
60
                                        GTK_TREE_MODEL (history_list_store)));
 
61
                GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
 
62
                GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (
 
63
                                _("Action history"), renderer, "text", COL_ACTION_TITLE, NULL); 
 
64
                gtk_tree_view_append_column (history_view, column);
 
65
                
 
66
                GtkTreeSelection *select = gtk_tree_view_get_selection (history_view);
 
67
                gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
 
68
                g_signal_connect (G_OBJECT (select), "changed",
 
69
                                G_CALLBACK (cb_action_history_changed), NULL);
 
70
 
 
71
                // with a scrollbar
 
72
                GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
 
73
                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
 
74
                                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
75
                gtk_container_add (GTK_CONTAINER (scrollbar), GTK_WIDGET (history_view));
 
76
                gtk_paned_add1 (log_hpaned, scrollbar);
 
77
        }
 
78
        
 
79
        /* log details */
 
80
        {
 
81
                GtkListStore *output_list_store = get_new_output_list_store ();
 
82
 
 
83
                output_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
 
84
                                        GTK_TREE_MODEL (output_list_store)));
 
85
                g_object_unref (output_list_store);
 
86
                set_previous_next_actions_sensitivity ();
 
87
 
 
88
                // we can now show some text (output_view must be initialized)
 
89
                print_output_normal (_("Welcome to LaTeXila!"));
 
90
 
 
91
                gtk_tree_view_set_headers_visible (output_view, FALSE);
 
92
                gtk_tree_view_set_tooltip_column (output_view, COL_OUTPUT_FILENAME);
 
93
 
 
94
                // one column with 3 cell renderers (basename, line number, and message)
 
95
                GtkTreeViewColumn *column = gtk_tree_view_column_new ();
 
96
                gtk_tree_view_append_column (output_view, column);
 
97
 
 
98
                // basename
 
99
                GtkCellRenderer *renderer1 = gtk_cell_renderer_text_new ();
 
100
                g_object_set (renderer1, "weight-set", TRUE, NULL);
 
101
                gtk_tree_view_column_pack_start (column, renderer1, FALSE);
 
102
                gtk_tree_view_column_set_attributes (column, renderer1,
 
103
                                "text", COL_OUTPUT_BASENAME,
 
104
                                "foreground", COL_OUTPUT_COLOR,
 
105
                                "background", COL_OUTPUT_BG_COLOR,
 
106
                                "weight", COL_OUTPUT_WEIGHT,
 
107
                                NULL);
 
108
 
 
109
                // line number
 
110
                GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new ();
 
111
                g_object_set (renderer2, "weight-set", TRUE, NULL);
 
112
                gtk_tree_view_column_pack_start (column, renderer2, FALSE);
 
113
                gtk_tree_view_column_set_attributes (column, renderer2,
 
114
                                "text", COL_OUTPUT_LINE_NUMBER,
 
115
                                "foreground", COL_OUTPUT_COLOR,
 
116
                                "background", COL_OUTPUT_BG_COLOR,
 
117
                                "weight", COL_OUTPUT_WEIGHT,
 
118
                                NULL);
 
119
 
 
120
                // message
 
121
                GtkCellRenderer *renderer3 = gtk_cell_renderer_text_new ();
 
122
                g_object_set (renderer3, "weight-set", TRUE, NULL);
 
123
                gtk_tree_view_column_pack_start (column, renderer3, FALSE);
 
124
                gtk_tree_view_column_set_attributes (column, renderer3,
 
125
                                "text", COL_OUTPUT_MESSAGE,
 
126
                                "foreground", COL_OUTPUT_COLOR,
 
127
                                "background", COL_OUTPUT_BG_COLOR,
 
128
                                "weight", COL_OUTPUT_WEIGHT,
 
129
                                NULL);
 
130
 
 
131
                // selection
 
132
                GtkTreeSelection *select = gtk_tree_view_get_selection (output_view);
 
133
                gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
 
134
 
 
135
                // If the user click on a row, output_row_selection_func() will be
 
136
                // called, but no row will be selected (the function return FALSE
 
137
                // everytime). Instead, if the row can be "selected" (if the message
 
138
                // type is an error, a warning or a badbox, and if the filename is not
 
139
                // empty), the background and foreground colors are inverted.
 
140
                // The GtkTreePath of the row selected is stored as a data in the
 
141
                // GtkTreeModel (with g_object_set_data()).
 
142
                gtk_tree_selection_set_select_function (select,
 
143
                                (GtkTreeSelectionFunc) output_row_selection_func, NULL, NULL);
 
144
 
 
145
                // with a scrollbar
 
146
                GtkWidget *scrollbar = gtk_scrolled_window_new (NULL, NULL);
 
147
                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbar),
 
148
                                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
149
                gtk_container_add (GTK_CONTAINER (scrollbar), GTK_WIDGET (output_view));
 
150
 
 
151
                /* left: output view
 
152
                 * right: log toolbar (vertical)
 
153
                 */
 
154
                GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
 
155
                gtk_box_pack_start (GTK_BOX (hbox), scrollbar, TRUE, TRUE, 0);
 
156
                gtk_box_pack_start (GTK_BOX (hbox), log_toolbar, FALSE, FALSE, 0);
 
157
                gtk_paned_add2 (log_hpaned, hbox);
 
158
        }
 
159
}
 
160
 
 
161
static void
 
162
cb_action_history_changed (GtkTreeSelection *selection, gpointer user_data)
 
163
{
 
164
        GtkTreeIter iter;
 
165
        GtkTreeModel *history_model;
 
166
        if (gtk_tree_selection_get_selected (selection, &history_model, &iter))
 
167
        {
 
168
                GtkTreeModel *output_model;
 
169
                gtk_tree_model_get (history_model, &iter,
 
170
                                COL_ACTION_OUTPUT_STORE, &output_model,
 
171
                                -1);
 
172
                gtk_tree_view_set_model (output_view, output_model);
 
173
 
 
174
                output_view_columns_autosize ();
 
175
 
 
176
                // scroll to the selected line
 
177
                GtkTreePath *path = g_object_get_data (G_OBJECT (output_model),
 
178
                                "row-selected");
 
179
                if (path != NULL)
 
180
                {
 
181
                        GtkTreeIter output_iter;
 
182
                        gtk_tree_model_get_iter (output_model, &output_iter, path);
 
183
                        scroll_to_iter (&output_iter, TRUE);
 
184
                }
 
185
 
 
186
                set_previous_next_actions_sensitivity ();
 
187
        }
 
188
}
 
189
 
 
190
static GtkListStore *
 
191
get_new_output_list_store (void)
 
192
{
 
193
        GtkListStore *output_list_store = gtk_list_store_new (N_COLS_OUTPUT,
 
194
                        G_TYPE_STRING,          // basename
 
195
                        G_TYPE_STRING,          // filename
 
196
                        G_TYPE_STRING,          // line number
 
197
                        G_TYPE_STRING,          // message
 
198
                        G_TYPE_INT,                     // message type
 
199
                        G_TYPE_STRING,          // color
 
200
                        G_TYPE_STRING,          // background color
 
201
                        G_TYPE_INT                      // weight
 
202
                        );
 
203
        return output_list_store;
 
204
}
 
205
 
 
206
void
 
207
add_action (const gchar *title, const gchar *command)
 
208
{
 
209
        static gint num = 1;
 
210
 
 
211
        GtkListStore *output_list_store = get_new_output_list_store ();
 
212
        gtk_tree_view_set_model (output_view, GTK_TREE_MODEL (output_list_store));
 
213
        set_previous_next_actions_sensitivity ();
 
214
 
 
215
        // print title and command to the new list store
 
216
        gchar *title_with_num = g_strdup_printf ("%d. %s", num, title);
 
217
        print_output_title (title_with_num);
 
218
 
 
219
    gchar *command_with_dolar = g_strdup_printf ("$ %s", command);
 
220
        print_output_info (command_with_dolar);
 
221
        g_free (command_with_dolar);
 
222
 
 
223
        // append a new entry to the history action list
 
224
        GtkTreeModel *history_tree_model = gtk_tree_view_get_model (history_view);
 
225
        GtkTreeIter iter;
 
226
        gtk_list_store_append (GTK_LIST_STORE (history_tree_model), &iter);
 
227
        gtk_list_store_set (GTK_LIST_STORE (history_tree_model), &iter,
 
228
                        COL_ACTION_TITLE, title_with_num,
 
229
                        COL_ACTION_OUTPUT_STORE, output_list_store,
 
230
                        -1);
 
231
 
 
232
        // select the new entry
 
233
        GtkTreeSelection *selection = gtk_tree_view_get_selection (history_view);
 
234
        gtk_tree_selection_select_iter (selection, &iter);
 
235
 
 
236
        // scroll to the end
 
237
        GtkTreePath *path = gtk_tree_model_get_path (history_tree_model, &iter);
 
238
        gtk_tree_view_scroll_to_cell (history_view, path, NULL, FALSE, 0, 0);
 
239
        gtk_tree_path_free (path);
 
240
 
 
241
        // delete the first entry
 
242
        if (num > 5)
 
243
        {
 
244
                GtkTreeIter first;
 
245
                gtk_tree_model_get_iter_first (history_tree_model, &first);
 
246
 
 
247
                // free the output model
 
248
                GtkTreeModel *first_output_store;
 
249
                gtk_tree_model_get (history_tree_model, &first,
 
250
                                COL_ACTION_OUTPUT_STORE, &first_output_store,
 
251
                                -1);
 
252
                g_object_unref (first_output_store);
 
253
 
 
254
                gtk_list_store_remove (GTK_LIST_STORE (history_tree_model), &first);
 
255
        }
 
256
 
 
257
        num++;
 
258
        g_free (title_with_num);
 
259
}
 
260
 
 
261
static void
 
262
go_to_message (gboolean next, gint message_type)
 
263
{
 
264
        GtkTreeModel *model = gtk_tree_view_get_model (output_view);
 
265
        messages_index_t *index = g_object_get_data (G_OBJECT (model), "index");
 
266
        if (index == NULL)
 
267
                return;
 
268
 
 
269
        // set nb_messages and messages with the data from index for the right
 
270
        // message type
 
271
        gint nb_messages;
 
272
        gint *messages;
 
273
        switch (message_type)
 
274
        {
 
275
                case MESSAGE_TYPE_ERROR:
 
276
                        nb_messages = index->nb_errors;
 
277
                        messages = index->errors;
 
278
                        break;
 
279
 
 
280
                case MESSAGE_TYPE_WARNING:
 
281
                        nb_messages = index->nb_warnings;
 
282
                        messages = index->warnings;
 
283
                        break;
 
284
 
 
285
                case MESSAGE_TYPE_BADBOX:
 
286
                        nb_messages = index->nb_badboxes;
 
287
                        messages = index->badboxes;
 
288
                        break;
 
289
 
 
290
                default:
 
291
                        return;
 
292
        }
 
293
 
 
294
        if (nb_messages == 0)
 
295
                return;
 
296
 
 
297
 
 
298
        // find the row number to select
 
299
        gint row_to_select = -1;
 
300
 
 
301
        GtkTreePath *path = g_object_get_data (G_OBJECT (model), "row-selected");
 
302
        if (path != NULL)
 
303
        {
 
304
                gint *indices = gtk_tree_path_get_indices (path);
 
305
                gint indice = indices[0];
 
306
                for (gint i = 0 ; i < nb_messages ; i++)
 
307
                {
 
308
                        if (next && messages[i] > indice)
 
309
                        {
 
310
                                row_to_select = messages[i];
 
311
                                break;
 
312
                        }
 
313
                        else if (! next && messages[i] >= indice)
 
314
                        {
 
315
                                if (i == 0)
 
316
                                        return;
 
317
                                row_to_select = messages[i-1];
 
318
                                break;
 
319
                        }
 
320
                        // the row selected is after the last row of the type message_type
 
321
                        else if (! next && i == nb_messages - 1)
 
322
                        {
 
323
                                row_to_select = messages[i];
 
324
                                break;
 
325
                        }
 
326
                }
 
327
 
 
328
                // not found
 
329
                if (row_to_select == -1)
 
330
                        return;
 
331
        }
 
332
 
 
333
        // no row is selected, take the first
 
334
        else if (path == NULL && next)
 
335
                row_to_select = messages[0];
 
336
        else
 
337
                return;
 
338
 
 
339
        GtkTreePath *new_path = gtk_tree_path_new_from_indices (row_to_select, -1);
 
340
        GtkTreeIter iter;
 
341
        if (! gtk_tree_model_get_iter (model, &iter, new_path))
 
342
                return;
 
343
 
 
344
        select_row (model, &iter);
 
345
        scroll_to_iter (&iter, TRUE);
 
346
}
 
347
 
 
348
void
 
349
cb_go_previous_latex_error (void)
 
350
{
 
351
        go_to_message (FALSE, MESSAGE_TYPE_ERROR);
 
352
}
 
353
 
 
354
void
 
355
cb_go_previous_latex_warning (void)
 
356
{
 
357
        go_to_message (FALSE, MESSAGE_TYPE_WARNING);
 
358
}
 
359
 
 
360
void
 
361
cb_go_previous_latex_badbox (void)
 
362
{
 
363
        go_to_message (FALSE, MESSAGE_TYPE_BADBOX);
 
364
}
 
365
 
 
366
void
 
367
cb_go_next_latex_error (void)
 
368
{
 
369
        go_to_message (TRUE, MESSAGE_TYPE_ERROR);
 
370
}
 
371
 
 
372
void
 
373
cb_go_next_latex_warning (void)
 
374
{
 
375
        go_to_message (TRUE, MESSAGE_TYPE_WARNING);
 
376
}
 
377
 
 
378
void
 
379
cb_go_next_latex_badbox (void)
 
380
{
 
381
        go_to_message (TRUE, MESSAGE_TYPE_BADBOX);
 
382
}
 
383
 
 
384
void
 
385
set_history_sensitivity (gboolean sensitive)
 
386
{
 
387
        gtk_widget_set_sensitive (GTK_WIDGET (history_view), sensitive);
 
388
}
 
389
 
 
390
void
 
391
output_view_columns_autosize (void)
 
392
{
 
393
        gtk_tree_view_columns_autosize (output_view);
 
394
}
 
395
 
 
396
void
 
397
print_output_title (const gchar *title)
 
398
{
 
399
        // generally, the title is the first line in a new output, and is used only
 
400
        // once, so we reset the counter...
 
401
        nb_lines = 0;
 
402
 
 
403
        GtkListStore *list_store =
 
404
                GTK_LIST_STORE (gtk_tree_view_get_model (output_view));
 
405
        GtkTreeIter iter;
 
406
        gtk_list_store_append (list_store, &iter);
 
407
        gtk_list_store_set (list_store, &iter,
 
408
                        COL_OUTPUT_BASENAME, INFO_MESSAGE,
 
409
                        COL_OUTPUT_LINE_NUMBER, "",
 
410
                        COL_OUTPUT_MESSAGE, title,
 
411
                        COL_OUTPUT_MESSAGE_TYPE, MESSAGE_TYPE_OTHER,
 
412
                        COL_OUTPUT_WEIGHT, WEIGHT_BOLD,
 
413
                        -1);
 
414
}
 
415
 
 
416
void
 
417
print_output_info (const gchar *info)
 
418
{
 
419
        nb_lines++;
 
420
 
 
421
        GtkListStore *list_store =
 
422
                GTK_LIST_STORE (gtk_tree_view_get_model (output_view));
 
423
        GtkTreeIter iter;
 
424
        gtk_list_store_append (list_store, &iter);
 
425
        gtk_list_store_set (list_store, &iter,
 
426
                        COL_OUTPUT_BASENAME, INFO_MESSAGE,
 
427
                        COL_OUTPUT_LINE_NUMBER, "",
 
428
                        COL_OUTPUT_MESSAGE, info,
 
429
                        COL_OUTPUT_MESSAGE_TYPE, MESSAGE_TYPE_OTHER,
 
430
                        COL_OUTPUT_WEIGHT, WEIGHT_NORMAL,
 
431
                        -1);
 
432
        scroll_to_iter (&iter, FALSE);
 
433
}
 
434
 
 
435
void
 
436
print_output_stats (gint nb_errors, gint nb_warnings, gint nb_badboxes)
 
437
{
 
438
        gchar *str = g_strdup_printf ("%d %s, %d %s, %d %s",
 
439
                        nb_errors, nb_errors > 1 ? "errors" : "error",
 
440
                        nb_warnings, nb_warnings > 1 ? "warnings" : "warning",
 
441
                        nb_badboxes, nb_badboxes > 1 ? "badboxes" : "badbox");
 
442
 
 
443
        print_output_info (str);
 
444
        g_free (str);
 
445
 
 
446
        // init the index
 
447
        // we wait the exit code to fill it
 
448
        messages_index_t *index = g_malloc (sizeof (messages_index_t));
 
449
        index->nb_errors = nb_errors;
 
450
        index->nb_warnings = nb_warnings;
 
451
        index->nb_badboxes = nb_badboxes;
 
452
        index->errors = g_malloc (nb_errors * sizeof (gint));
 
453
        index->warnings = g_malloc (nb_warnings * sizeof (gint));
 
454
        index->badboxes = g_malloc (nb_badboxes * sizeof (gint));
 
455
 
 
456
        GtkTreeModel *model = gtk_tree_view_get_model (output_view);
 
457
        g_object_set_data_full (G_OBJECT (model), "index", index,
 
458
                        (GDestroyNotify) index_free);
 
459
}
 
460
 
 
461
// if message != NULL the exit_code is not taken into account
 
462
void
 
463
print_output_exit (const gint exit_code, const gchar *message)
 
464
{
 
465
        nb_lines++;
 
466
 
 
467
        GtkListStore *list_store =
 
468
                GTK_LIST_STORE (gtk_tree_view_get_model (output_view));
 
469
        GtkTreeIter iter;
 
470
        gtk_list_store_append (list_store, &iter);
 
471
        gtk_list_store_set (list_store, &iter,
 
472
                        COL_OUTPUT_BASENAME, INFO_MESSAGE,
 
473
                        COL_OUTPUT_LINE_NUMBER, "",
 
474
                        COL_OUTPUT_MESSAGE_TYPE, MESSAGE_TYPE_OTHER,
 
475
                        COL_OUTPUT_WEIGHT, WEIGHT_NORMAL,
 
476
                        -1);
 
477
 
 
478
        if (message != NULL)
 
479
        {
 
480
                gtk_list_store_set (list_store, &iter,
 
481
                                COL_OUTPUT_MESSAGE, message,
 
482
                                COL_OUTPUT_COLOR, COLOR_RED,
 
483
                                -1);
 
484
        }
 
485
        else if (exit_code == 0)
 
486
        {
 
487
                gtk_list_store_set (list_store, &iter,
 
488
                                COL_OUTPUT_MESSAGE, _("Done!"),
 
489
                                COL_OUTPUT_COLOR, COLOR_GREEN,
 
490
                                -1);
 
491
        }
 
492
        else
 
493
        {
 
494
                gchar *tmp = g_strdup_printf (_("Finished with exit code %d"), exit_code);
 
495
                gtk_list_store_set (list_store, &iter,
 
496
                                COL_OUTPUT_MESSAGE, tmp,
 
497
                                COL_OUTPUT_COLOR, COLOR_RED,
 
498
                                -1);
 
499
                g_free (tmp);
 
500
        }
 
501
 
 
502
        // force the scrolling and the flush
 
503
        scroll_to_iter (&iter, TRUE);
 
504
 
 
505
        // it's normally the last line, so we can index the messages by type
 
506
        index_messages_by_type (GTK_TREE_MODEL (list_store));
 
507
}
 
508
 
 
509
void
 
510
print_output_message (const gchar *filename, const gint line_number,
 
511
                const gchar *message, gint message_type)
 
512
{
 
513
        // this function is used only in the filter, so there are less lines
 
514
        // but we want to flush more often
 
515
        nb_lines += 2;
 
516
 
 
517
        GtkListStore *list_store =
 
518
                GTK_LIST_STORE (gtk_tree_view_get_model (output_view));
 
519
 
 
520
        gchar *basename;
 
521
        if (filename != NULL)
 
522
                basename = g_path_get_basename (filename);
 
523
        else
 
524
                basename = g_strdup ("");
 
525
 
 
526
        gchar *line_number_str;
 
527
        if (line_number != NO_LINE)
 
528
                line_number_str = g_strdup_printf ("%d", line_number);
 
529
        else
 
530
                line_number_str = g_strdup ("");
 
531
 
 
532
        gchar *color;
 
533
        switch (message_type)
 
534
        {
 
535
                case MESSAGE_TYPE_ERROR:
 
536
                        color = COLOR_RED;
 
537
                        break;
 
538
                case MESSAGE_TYPE_WARNING:
 
539
                        color = COLOR_ORANGE;
 
540
                        break;
 
541
                case MESSAGE_TYPE_BADBOX:
 
542
                        color = COLOR_BROWN;
 
543
                        break;
 
544
                default:
 
545
                        color = NULL;
 
546
                        break;
 
547
        }
 
548
 
 
549
        GtkTreeIter iter;
 
550
        gtk_list_store_append (list_store, &iter);
 
551
        gtk_list_store_set (list_store, &iter,
 
552
                        COL_OUTPUT_BASENAME, basename, 
 
553
                        COL_OUTPUT_FILENAME, filename != NULL ? filename : "",
 
554
                        COL_OUTPUT_LINE_NUMBER, line_number_str,
 
555
                        COL_OUTPUT_MESSAGE, message,
 
556
                        COL_OUTPUT_MESSAGE_TYPE, message_type,
 
557
                        COL_OUTPUT_COLOR, color,
 
558
                        COL_OUTPUT_WEIGHT, WEIGHT_NORMAL,
 
559
                        -1);
 
560
        scroll_to_iter (&iter, FALSE);
 
561
 
 
562
        g_free (basename);
 
563
        g_free (line_number_str);
 
564
}
 
565
 
 
566
void
 
567
print_output_normal (const gchar *message)
 
568
{
 
569
        nb_lines++;
 
570
 
 
571
        GtkListStore *list_store =
 
572
                GTK_LIST_STORE (gtk_tree_view_get_model (output_view));
 
573
        GtkTreeIter iter;
 
574
        gtk_list_store_append (list_store, &iter);
 
575
        gtk_list_store_set (list_store, &iter,
 
576
                        COL_OUTPUT_BASENAME, "", 
 
577
                        COL_OUTPUT_LINE_NUMBER, "",
 
578
                        COL_OUTPUT_MESSAGE, message,
 
579
                        COL_OUTPUT_MESSAGE_TYPE, MESSAGE_TYPE_OTHER,
 
580
                        COL_OUTPUT_WEIGHT, WEIGHT_NORMAL,
 
581
                        -1);
 
582
        scroll_to_iter (&iter, FALSE);
 
583
}
 
584
 
 
585
static void
 
586
scroll_to_iter (GtkTreeIter *iter, gboolean force)
 
587
{
 
588
        /* Flush the queue for the 50 first lines and then every 40 lines.
 
589
         * This is for the fluidity of the output, without that the lines do not
 
590
         * appear directly and it's ugly. But it is very slow, for a command that
 
591
         * execute for example in 10 seconds, it could take 250 seconds (!) if we
 
592
         * flush the queue at each line... But with commands that take 1
 
593
         * second or so there is not a big difference.
 
594
         */
 
595
        if (force || nb_lines < 50 || nb_lines % 40 == 0)
 
596
        {
 
597
                GtkTreeModel *tree_model = gtk_tree_view_get_model (output_view);
 
598
                GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
 
599
                gtk_tree_view_scroll_to_cell (output_view, path, NULL, FALSE, 0, 0);
 
600
                gtk_tree_path_free (path);
 
601
                flush_queue ();
 
602
        }
 
603
}
 
604
 
 
605
static gboolean
 
606
output_row_selection_func (GtkTreeSelection *selection, GtkTreeModel *model,
 
607
                GtkTreePath *path, gboolean path_currently_selected, gpointer data)
 
608
{
 
609
        GtkTreeIter iter;
 
610
        if (gtk_tree_model_get_iter (model, &iter, path))
 
611
        {
 
612
                gint message_type;
 
613
                gchar *filename;
 
614
                gtk_tree_model_get (model, &iter,
 
615
                                COL_OUTPUT_FILENAME, &filename,
 
616
                                COL_OUTPUT_MESSAGE_TYPE, &message_type,
 
617
                                -1);
 
618
 
 
619
                if (message_type != MESSAGE_TYPE_OTHER && filename != NULL
 
620
                                && strlen (filename) > 0)
 
621
                        select_row (model, &iter);
 
622
 
 
623
                g_free (filename);
 
624
        }
 
625
 
 
626
        // rows will never be selected
 
627
        return FALSE;
 
628
}
 
629
 
 
630
static void
 
631
select_row (GtkTreeModel *model, GtkTreeIter *iter)
 
632
{
 
633
        GtkTreePath *current_path_selected = g_object_get_data (G_OBJECT (model),
 
634
                        "row-selected");
 
635
 
 
636
        GtkTreePath *path = gtk_tree_model_get_path (model, iter);
 
637
 
 
638
        if (current_path_selected != NULL)
 
639
        {
 
640
                // if the row to select is not the same as the row already selected,
 
641
                // we must deselect this row
 
642
                if (gtk_tree_path_compare (current_path_selected, path) != 0)
 
643
                {
 
644
                        GtkTreeIter current_iter_selected;
 
645
                        gtk_tree_model_get_iter (model, &current_iter_selected,
 
646
                                        current_path_selected);
 
647
 
 
648
                        // invert the colors
 
649
                        gchar *bg_color;
 
650
                        gtk_tree_model_get (model, &current_iter_selected,
 
651
                                        COL_OUTPUT_BG_COLOR, &bg_color,
 
652
                                        -1);
 
653
                        gtk_list_store_set (GTK_LIST_STORE (model), &current_iter_selected,
 
654
                                        COL_OUTPUT_COLOR, bg_color,
 
655
                                        COL_OUTPUT_BG_COLOR, NULL,
 
656
                                        -1);
 
657
                        g_free (bg_color);
 
658
                }
 
659
 
 
660
                // the row is already selected
 
661
                else
 
662
                {
 
663
                        gtk_tree_path_free (path);
 
664
                        jump_to_file ();
 
665
                        return;
 
666
                }
 
667
        }
 
668
 
 
669
        // invert the colors
 
670
        gchar *color;
 
671
        gtk_tree_model_get (model, iter,
 
672
                        COL_OUTPUT_COLOR, &color,
 
673
                        -1);
 
674
        gtk_list_store_set (GTK_LIST_STORE (model), iter,
 
675
                        COL_OUTPUT_COLOR, "white",
 
676
                        COL_OUTPUT_BG_COLOR, color,
 
677
                        -1);
 
678
        g_free (color);
 
679
 
 
680
        g_object_set_data_full (G_OBJECT (model), "row-selected", path,
 
681
                        (GDestroyNotify) path_free);
 
682
        jump_to_file ();
 
683
        set_previous_next_actions_sensitivity ();
 
684
}
 
685
 
 
686
static void
 
687
jump_to_file (void)
 
688
{
 
689
        GtkTreeModel *model = gtk_tree_view_get_model (output_view);
 
690
        GtkTreePath *path_selected = g_object_get_data (G_OBJECT (model),
 
691
                        "row-selected");
 
692
        if (path_selected == NULL)
 
693
                return;
 
694
 
 
695
        GtkTreeIter iter;
 
696
        gtk_tree_model_get_iter (model, &iter, path_selected);
 
697
 
 
698
        gchar *filename = NULL;
 
699
        gchar *line_number = NULL;
 
700
        gint message_type;
 
701
 
 
702
        gtk_tree_model_get (model, &iter,
 
703
                        COL_OUTPUT_FILENAME, &filename,
 
704
                        COL_OUTPUT_LINE_NUMBER, &line_number,
 
705
                        COL_OUTPUT_MESSAGE_TYPE, &message_type,
 
706
                        -1);
 
707
 
 
708
        if (message_type != MESSAGE_TYPE_OTHER && filename != NULL
 
709
                        && strlen (filename) > 0)
 
710
        {
 
711
                // open the file (if the file is already opened, go to it)
 
712
                open_new_document_without_uri (filename);
 
713
 
 
714
                // we flush the queue here because without that, there is a bug when the
 
715
                // file is not already opened, scroll_to_cursor() doesn't work as
 
716
                // expected.
 
717
                flush_queue ();
 
718
 
 
719
                // go to line
 
720
                if (line_number != NULL && strlen (line_number) != 0
 
721
                                && latexila.active_doc != NULL)
 
722
                {
 
723
                        gint num = strtol (line_number, NULL, 10);
 
724
                        GtkTextIter iter_file;
 
725
                        GtkTextBuffer *buffer = GTK_TEXT_BUFFER (
 
726
                                        latexila.active_doc->source_buffer);
 
727
                        gtk_text_buffer_get_iter_at_line (buffer, &iter_file, --num);
 
728
                        gtk_text_buffer_place_cursor (buffer, &iter_file);
 
729
                        scroll_to_cursor ();
 
730
                }
 
731
 
 
732
                gtk_widget_grab_focus (latexila.active_doc->source_view);
 
733
        }
 
734
 
 
735
        g_free (filename);
 
736
        g_free (line_number);
 
737
}
 
738
 
 
739
static void
 
740
index_messages_by_type (GtkTreeModel *model)
 
741
{
 
742
        messages_index_t *index = g_object_get_data (G_OBJECT (model), "index");
 
743
        if (index == NULL)
 
744
                return;
 
745
 
 
746
        if (index->nb_errors == 0 && index->nb_warnings == 0
 
747
                        && index->nb_badboxes == 0)
 
748
                return;
 
749
 
 
750
        // the row number
 
751
        gint i = 0;
 
752
 
 
753
        // positions in the tables
 
754
        gint i_error = 0;
 
755
        gint i_warning = 0;
 
756
        gint i_badbox = 0;
 
757
 
 
758
        GtkTreeIter iter;
 
759
        gboolean valid = gtk_tree_model_get_iter_first (model, &iter);
 
760
 
 
761
        while (valid)
 
762
        {
 
763
                gint message_type;
 
764
                gtk_tree_model_get (model, &iter,
 
765
                                COL_OUTPUT_MESSAGE_TYPE, &message_type,
 
766
                                -1);
 
767
 
 
768
                switch (message_type)
 
769
                {
 
770
                        case MESSAGE_TYPE_ERROR:
 
771
                                index->errors[i_error] = i;
 
772
                                i_error++;
 
773
                                break;
 
774
 
 
775
                        case MESSAGE_TYPE_WARNING:
 
776
                                index->warnings[i_warning] = i;
 
777
                                i_warning++;
 
778
                                break;
 
779
 
 
780
                        case MESSAGE_TYPE_BADBOX:
 
781
                                index->badboxes[i_badbox] = i;
 
782
                                i_badbox++;
 
783
                                break;
 
784
 
 
785
                        default:
 
786
                                break;
 
787
                }
 
788
 
 
789
                valid = gtk_tree_model_iter_next (model, &iter);
 
790
                i++;
 
791
        }
 
792
 
 
793
        set_previous_next_actions_sensitivity ();
 
794
}
 
795
 
 
796
static void
 
797
set_previous_next_actions_sensitivity (void)
 
798
{
 
799
        GtkTreeModel *model = gtk_tree_view_get_model (output_view);
 
800
        messages_index_t *index = g_object_get_data (G_OBJECT (model), "index");
 
801
 
 
802
        if (index == NULL)
 
803
        {
 
804
                gtk_action_set_sensitive (latexila.actions.go_previous_error, FALSE);
 
805
                gtk_action_set_sensitive (latexila.actions.go_previous_warning, FALSE);
 
806
                gtk_action_set_sensitive (latexila.actions.go_previous_badbox, FALSE);
 
807
                gtk_action_set_sensitive (latexila.actions.go_next_error, FALSE);
 
808
                gtk_action_set_sensitive (latexila.actions.go_next_warning, FALSE);
 
809
                gtk_action_set_sensitive (latexila.actions.go_next_badbox, FALSE);
 
810
                return;
 
811
        }
 
812
 
 
813
        GtkTreePath *path = g_object_get_data (G_OBJECT (model), "row-selected");
 
814
 
 
815
        if (path == NULL)
 
816
        {
 
817
                gtk_action_set_sensitive (latexila.actions.go_previous_error, FALSE);
 
818
                gtk_action_set_sensitive (latexila.actions.go_previous_warning, FALSE);
 
819
                gtk_action_set_sensitive (latexila.actions.go_previous_badbox, FALSE);
 
820
 
 
821
                gtk_action_set_sensitive (latexila.actions.go_next_error, index->nb_errors > 0);
 
822
                gtk_action_set_sensitive (latexila.actions.go_next_warning, index->nb_warnings > 0);
 
823
                gtk_action_set_sensitive (latexila.actions.go_next_badbox, index->nb_badboxes > 0);
 
824
                return;
 
825
        }
 
826
 
 
827
        gint *indices = gtk_tree_path_get_indices (path);
 
828
        gint indice = indices[0];
 
829
 
 
830
        // errors
 
831
        if (index->nb_errors > 0)
 
832
        {
 
833
                gtk_action_set_sensitive (latexila.actions.go_previous_error,
 
834
                                index->errors[0] < indice);
 
835
                gtk_action_set_sensitive (latexila.actions.go_next_error,
 
836
                                index->errors[index->nb_errors - 1] > indice);
 
837
        }
 
838
        else
 
839
        {
 
840
                gtk_action_set_sensitive (latexila.actions.go_previous_error, FALSE);
 
841
                gtk_action_set_sensitive (latexila.actions.go_next_error, FALSE);
 
842
        }
 
843
 
 
844
        // warnings
 
845
        if (index->nb_warnings > 0)
 
846
        {
 
847
                gtk_action_set_sensitive (latexila.actions.go_previous_warning,
 
848
                                index->warnings[0] < indice);
 
849
                gtk_action_set_sensitive (latexila.actions.go_next_warning,
 
850
                                index->warnings[index->nb_warnings - 1] > indice);
 
851
        }
 
852
        else
 
853
        {
 
854
                gtk_action_set_sensitive (latexila.actions.go_previous_warning, FALSE);
 
855
                gtk_action_set_sensitive (latexila.actions.go_next_warning, FALSE);
 
856
        }
 
857
 
 
858
        // badboxes
 
859
        if (index->nb_badboxes > 0)
 
860
        {
 
861
                gtk_action_set_sensitive (latexila.actions.go_previous_badbox,
 
862
                                index->badboxes[0] < indice);
 
863
                gtk_action_set_sensitive (latexila.actions.go_next_badbox,
 
864
                                index->badboxes[index->nb_badboxes - 1] > indice);
 
865
        }
 
866
        else
 
867
        {
 
868
                gtk_action_set_sensitive (latexila.actions.go_previous_badbox, FALSE);
 
869
                gtk_action_set_sensitive (latexila.actions.go_next_badbox, FALSE);
 
870
        }
 
871
}
 
872
 
 
873
static void
 
874
path_free (gpointer data)
 
875
{
 
876
        gtk_tree_path_free ((GtkTreePath *) data);
 
877
}
 
878
 
 
879
static void
 
880
index_free (gpointer data)
 
881
{
 
882
        messages_index_t *index = (messages_index_t *) data;
 
883
        g_free (index->errors);
 
884
        g_free (index->warnings);
 
885
        g_free (index->badboxes);
 
886
        g_free (index);
 
887
}