fix .desktop file
[gconf-editor] / src / gedit-output-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gedit-output_window.c
4  * This file is part of gedit
5  *
6  * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
7  * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi 
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 2 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, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, 
22  * Boston, MA 02111-1307, USA. 
23  */
24  
25 /*
26  * Modified by the gedit Team, 1998-2002. See the AUTHORS file for a 
27  * list of people on the gedit Team.  
28  * See the ChangeLog files for a list of changes. 
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <gtk/gtk.h>
36 #include <gdk/gdkkeysyms.h>
37 #include <glib/gi18n.h>
38
39 #include "gedit-output-window.h"
40
41 struct _GeditOutputWindowPrivate
42 {
43         GtkWidget       *close_button;
44         GtkWidget       *copy_button;
45         GtkWidget       *clear_button;
46
47         GtkWidget       *close_menu_item;
48         GtkWidget       *copy_menu_item;
49         GtkWidget       *clear_menu_item;
50
51         GtkWidget       *treeview;
52         GtkTreeModel    *model;
53 };
54
55 enum {
56         CLOSE_REQUESTED,
57         SELECTION_CHANGED,
58         LAST_SIGNAL
59 };
60
61 static guint signals [LAST_SIGNAL];
62
63 enum
64 {
65         COLUMN_LINES,
66         NUM_COLUMNS
67 };
68
69
70 static void gedit_output_window_finalize        (GObject        *object);
71 static void gedit_output_window_destroy         (GtkObject      *object);
72
73 G_DEFINE_TYPE(GeditOutputWindow, gedit_output_window, GTK_TYPE_HBOX)
74
75 static void
76 gedit_output_window_class_init (GeditOutputWindowClass *klass)
77 {
78         GObjectClass *object_class = G_OBJECT_CLASS (klass);
79
80         object_class->finalize = gedit_output_window_finalize;
81         
82         GTK_OBJECT_CLASS (klass)->destroy = gedit_output_window_destroy;
83
84         signals[CLOSE_REQUESTED] = 
85                 g_signal_new ("close_requested",
86                               G_OBJECT_CLASS_TYPE (object_class),
87                               G_SIGNAL_RUN_LAST,
88                               G_STRUCT_OFFSET (GeditOutputWindowClass, close_requested),
89                               NULL, 
90                               NULL,
91                               g_cclosure_marshal_VOID__VOID,
92                               G_TYPE_NONE, 
93                               0);
94         signals[SELECTION_CHANGED] = 
95                 g_signal_new ("selection_changed",
96                               G_OBJECT_CLASS_TYPE (object_class),
97                               G_SIGNAL_RUN_LAST,
98                               G_STRUCT_OFFSET (GeditOutputWindowClass, selection_changed),
99                               NULL, 
100                               NULL,
101                               g_cclosure_marshal_VOID__STRING,
102                               G_TYPE_NONE, 
103                               1, G_TYPE_STRING);
104
105 }
106
107 static void              
108 gedit_output_window_copy_selection (GeditOutputWindow *ow)
109 {
110         gboolean ret;
111         GtkTreeIter iter;
112         GtkTreeSelection *selection;
113
114         GString *string = NULL;
115
116         g_return_if_fail (GEDIT_IS_OUTPUT_WINDOW (ow));
117
118         selection = gtk_tree_view_get_selection (
119                         GTK_TREE_VIEW (ow->priv->treeview));
120
121         ret = gtk_tree_model_get_iter_first (ow->priv->model, &iter);
122         
123         while (ret)
124         {
125                 if (gtk_tree_selection_iter_is_selected (selection, &iter))
126                 {
127                         gchar *line;
128                         
129                         gtk_tree_model_get (ow->priv->model, &iter, COLUMN_LINES, &line, -1);
130
131                         if (string == NULL)
132                                 string = g_string_new (line);
133                         else
134                                 string = g_string_append (string, line);
135                         
136                         string = g_string_append_c (string, '\n');
137                         
138                         g_free (line);
139                 }
140
141                 ret = gtk_tree_model_iter_next (ow->priv->model, &iter);
142         }
143
144         if (string != NULL)
145         {
146                 gchar *text;
147
148                 pango_parse_markup (string->str, string->len, 0, NULL, &text, NULL, NULL);
149                 
150                 gtk_clipboard_set_text (gtk_widget_get_clipboard (
151                                                 GTK_WIDGET (ow), GDK_SELECTION_CLIPBOARD),
152                                         text, 
153                                         -1);
154
155                 g_free (text);
156         }
157
158         g_string_free (string, TRUE);
159 }
160
161 static void
162 close_clicked_callback (GtkWidget *widget, gpointer user_data)
163 {
164         GeditOutputWindow *ow;
165         
166         ow = GEDIT_OUTPUT_WINDOW (user_data);
167
168         g_signal_emit (ow, signals [CLOSE_REQUESTED], 0);
169 }
170
171 static void
172 clear_clicked_callback (GtkWidget *widget, gpointer user_data)
173 {
174         GeditOutputWindow *ow;
175         
176         ow = GEDIT_OUTPUT_WINDOW (user_data);
177
178         gedit_output_window_clear (ow);
179 }
180
181 static void
182 copy_clicked_callback (GtkWidget *widget, gpointer user_data)
183 {
184         GeditOutputWindow *ow;
185         
186         ow = GEDIT_OUTPUT_WINDOW (user_data);
187
188         gedit_output_window_copy_selection (ow);
189 }
190
191 static gboolean 
192 gedit_output_window_key_press_event_cb (GtkTreeView *widget, GdkEventKey *event, 
193                 GeditOutputWindow *ow)
194 {
195         if (event->keyval == GDK_Delete)
196         {
197                 gedit_output_window_clear (ow);
198                 return TRUE;
199         }
200
201         if (event->keyval == 'c')
202         {
203                 gedit_output_window_copy_selection (ow);
204                 return TRUE;
205         }
206         
207         return FALSE;
208 }
209
210 static GtkWidget *
211 create_popup_menu (GeditOutputWindow  *output_window)
212 {
213         GtkWidget *menu;
214         GtkWidget *menu_item;
215         
216         menu = gtk_menu_new ();
217
218         /* Add the clear button */
219         output_window->priv->clear_menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
220         gtk_widget_show (output_window->priv->clear_menu_item);
221         g_signal_connect (G_OBJECT (output_window->priv->clear_menu_item), "activate",
222                           G_CALLBACK (clear_clicked_callback), output_window);
223         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), output_window->priv->clear_menu_item);
224
225         /* Add the copy button */
226         output_window->priv->copy_menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
227         gtk_widget_show (output_window->priv->copy_menu_item);
228         g_signal_connect (G_OBJECT (output_window->priv->copy_menu_item), "activate",
229                           G_CALLBACK (copy_clicked_callback), output_window);
230         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), output_window->priv->copy_menu_item);
231
232         /* Add the separator */
233         menu_item = gtk_separator_menu_item_new ();
234         gtk_widget_show (menu_item);
235         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
236
237         /* Add the close button */
238         output_window->priv->close_menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE, NULL);
239         gtk_widget_show (output_window->priv->close_menu_item);
240         g_signal_connect (G_OBJECT (output_window->priv->close_menu_item), "activate",
241                           G_CALLBACK (close_clicked_callback), output_window);
242         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), output_window->priv->close_menu_item);
243         
244         gtk_widget_set_sensitive (output_window->priv->copy_menu_item, FALSE);
245         gtk_widget_set_sensitive (output_window->priv->clear_menu_item, FALSE);
246
247         return menu;
248 }
249
250 static gint
251 my_popup_handler (GtkWidget *widget, GdkEvent *event)
252 {
253   GtkMenu *menu;
254   GdkEventButton *event_button;
255
256   g_return_val_if_fail (widget != NULL, FALSE);
257   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
258   g_return_val_if_fail (event != NULL, FALSE);
259
260   /* The "widget" is the menu that was supplied when 
261    * g_signal_connect_swapped() was called.
262    */
263   menu = GTK_MENU (widget);
264
265   if (event->type == GDK_BUTTON_PRESS)
266     {
267       event_button = (GdkEventButton *) event;
268       if (event_button->button == 3)
269         {
270           gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 
271                           event_button->button, event_button->time);
272           return TRUE;
273         }
274     }
275
276   return FALSE;
277 }
278
279 static void
280 gedit_output_window_treeview_selection_changed (GtkTreeSelection *selection, 
281                                                 GeditOutputWindow  *output_window)
282 {
283         gboolean selected;
284         GtkTreeModel *model;
285         GtkTreeIter iter;
286         gchar *line;
287         
288         g_return_if_fail (output_window != NULL);
289         g_return_if_fail (selection != NULL);
290
291         selected = (gtk_tree_selection_count_selected_rows (selection) > 0);
292         
293         gtk_widget_set_sensitive (output_window->priv->copy_menu_item, selected);
294         gtk_widget_set_sensitive (output_window->priv->copy_button, selected);
295
296         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
297                 gtk_tree_model_get (model, &iter, COLUMN_LINES, &line, -1);     
298                 g_signal_emit (output_window, signals [SELECTION_CHANGED], 0, line);
299         }
300 }
301
302 static void
303 gedit_output_window_format_button (GtkButton* button, gint w, gint h)
304 {
305         GtkRcStyle* rc = gtk_widget_get_modifier_style (GTK_WIDGET (button));
306         rc->xthickness = 0;
307         rc->ythickness = 0;
308         gtk_widget_modify_style (GTK_WIDGET (button), rc);
309         gtk_button_set_relief (button, GTK_RELIEF_NONE);
310         gtk_button_set_focus_on_click (button, FALSE);
311 }
312
313 static void 
314 gedit_output_window_init (GeditOutputWindow  *output_window)
315 {
316         GtkSettings             *settings;
317         gint                     w, h;
318         GtkWidget               *vbox1;
319         GtkWidget               *image;
320         GtkWidget               *hbox2;
321         GtkWidget               *vseparator;
322         GtkWidget               *vbox2;
323         GtkWidget               *scrolledwindow;
324         GtkTreeViewColumn       *column;
325         GtkCellRenderer         *cell;
326         GtkTreeSelection        *selection;
327         GtkWidget               *popup_menu;
328
329         GList                   *focusable_widgets = NULL;
330
331         output_window->priv = g_new0 (GeditOutputWindowPrivate, 1);
332
333         settings = gtk_widget_get_settings (GTK_WIDGET (output_window));
334
335         gtk_icon_size_lookup_for_settings (settings,
336                                            GTK_ICON_SIZE_MENU,
337                                            &w, &h);
338
339         vbox1 = gtk_vbox_new (FALSE, 2);
340         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
341
342         /* Create the close button */
343         output_window->priv->close_button = gtk_button_new ();
344         gtk_box_pack_start (GTK_BOX (vbox1), output_window->priv->close_button, FALSE, FALSE, 0);
345
346         gtk_widget_set_tooltip_text (output_window->priv->close_button,
347                                      _("Close the output window"));
348
349         gedit_output_window_format_button (GTK_BUTTON (output_window->priv->close_button), w, h);
350
351         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
352         gtk_container_add (GTK_CONTAINER (output_window->priv->close_button), image);
353         
354         g_signal_connect (output_window->priv->close_button, 
355                           "clicked",
356                           G_CALLBACK (close_clicked_callback),
357                           output_window);
358
359         /* Create the 3 vertical separators */
360         hbox2 = gtk_hbox_new (TRUE, 0);
361         gtk_box_pack_start (GTK_BOX (vbox1), hbox2, TRUE, TRUE, 0);
362         gtk_container_set_border_width (GTK_CONTAINER (hbox2), 4);
363
364         vseparator = gtk_vseparator_new ();
365         gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, FALSE, 0);
366
367         vseparator = gtk_vseparator_new ();
368         gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, FALSE, 0);
369
370         vseparator = gtk_vseparator_new ();
371         gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, TRUE, 0);
372
373         /* Create the vbox for the copy and clear buttons */
374         vbox2 = gtk_vbox_new (TRUE, 0);
375         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, TRUE, 0);
376
377         /* Create the copy button */
378         output_window->priv->copy_button = gtk_button_new ();
379         gtk_box_pack_start (GTK_BOX (vbox2), output_window->priv->copy_button, FALSE, FALSE, 0);
380
381         gtk_widget_set_tooltip_text (output_window->priv->copy_button, 
382                                      _("Copy selected lines"));
383
384         gedit_output_window_format_button (GTK_BUTTON (output_window->priv->copy_button), w, h);
385
386         image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
387         gtk_container_add (GTK_CONTAINER (output_window->priv->copy_button), image);
388
389         g_signal_connect (output_window->priv->copy_button, 
390                           "clicked",
391                           G_CALLBACK (copy_clicked_callback),
392                           output_window);
393
394         /* Create the clear button */
395         output_window->priv->clear_button = gtk_button_new ();
396         gtk_box_pack_start (GTK_BOX (vbox2), output_window->priv->clear_button, FALSE, FALSE, 0);
397
398         gtk_widget_set_tooltip_text (output_window->priv->clear_button, 
399                                      _("Clear the output window"));
400         
401         gedit_output_window_format_button (GTK_BUTTON (output_window->priv->clear_button), w, h);
402
403         image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
404         gtk_container_add (GTK_CONTAINER (output_window->priv->clear_button), image);
405
406         g_signal_connect (output_window->priv->clear_button, 
407                           "clicked",
408                           G_CALLBACK (clear_clicked_callback),
409                           output_window);
410
411         gtk_widget_set_sensitive (output_window->priv->copy_button, FALSE);
412         gtk_widget_set_sensitive (output_window->priv->clear_button, FALSE);
413
414         /* Create the scrolled window */
415         scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
416         
417         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), 
418                                         GTK_POLICY_AUTOMATIC, 
419                                         GTK_POLICY_AUTOMATIC);
420         
421         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), 
422                                              GTK_SHADOW_ETCHED_IN);
423
424         output_window->priv->treeview = gtk_tree_view_new ();
425         gtk_container_add (GTK_CONTAINER (scrolledwindow), output_window->priv->treeview);
426         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (output_window->priv->treeview), FALSE);
427
428
429         /* List */
430         output_window->priv->model = GTK_TREE_MODEL (
431                         gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING));
432
433         gtk_tree_view_set_model (GTK_TREE_VIEW (output_window->priv->treeview), 
434                                  output_window->priv->model);
435
436         /* Add the suggestions column */
437         cell = gtk_cell_renderer_text_new ();
438         column = gtk_tree_view_column_new_with_attributes (_("Output Lines"), cell, 
439                         "markup", COLUMN_LINES, NULL);
440
441         gtk_tree_view_append_column (GTK_TREE_VIEW (output_window->priv->treeview), column);
442
443         gtk_tree_view_set_search_column (GTK_TREE_VIEW (output_window->priv->treeview),
444                         COLUMN_LINES);
445
446         selection = gtk_tree_view_get_selection (
447                         GTK_TREE_VIEW (output_window->priv->treeview));
448
449         gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
450         
451         gtk_box_pack_end (GTK_BOX (output_window), scrolledwindow, TRUE, TRUE, 0);
452         gtk_box_pack_start (GTK_BOX (output_window), vbox1, FALSE, FALSE, 0);
453
454         gtk_widget_set_size_request (GTK_WIDGET (output_window), 3 * w, 5 * (h + 2));
455
456         g_signal_connect (G_OBJECT (output_window->priv->treeview), "key_press_event",
457                           G_CALLBACK (gedit_output_window_key_press_event_cb), output_window);
458
459         g_signal_connect (G_OBJECT (selection), "changed", 
460                           G_CALLBACK (gedit_output_window_treeview_selection_changed), 
461                           output_window);
462
463         focusable_widgets = g_list_append (focusable_widgets, output_window->priv->treeview);
464         focusable_widgets = g_list_append (focusable_widgets, output_window->priv->close_button);
465         focusable_widgets = g_list_append (focusable_widgets, output_window->priv->copy_button);
466         focusable_widgets = g_list_append (focusable_widgets, output_window->priv->clear_button);
467
468         gtk_container_set_focus_chain (GTK_CONTAINER (output_window), focusable_widgets);
469
470         g_list_free (focusable_widgets);
471
472         popup_menu = create_popup_menu (output_window);
473                 
474         gtk_menu_attach_to_widget(GTK_MENU(popup_menu), output_window->priv->treeview, NULL);
475         
476         g_signal_connect_swapped (output_window->priv->treeview, "button_press_event",  G_CALLBACK (my_popup_handler), popup_menu);
477         
478                           
479 }
480
481 static void 
482 gedit_output_window_finalize (GObject *object)
483 {
484         GeditOutputWindow *ow;
485
486         ow = GEDIT_OUTPUT_WINDOW (object);
487
488         if (ow->priv != NULL)
489         {
490                 g_free (ow->priv);
491         }
492
493         G_OBJECT_CLASS (gedit_output_window_parent_class)->finalize (object);
494 }
495
496 static void 
497 gedit_output_window_destroy (GtkObject *object)
498 {
499         GTK_OBJECT_CLASS (gedit_output_window_parent_class)->destroy (object);
500 }
501
502 GtkWidget *
503 gedit_output_window_new (void)
504 {
505         return gtk_widget_new (GEDIT_TYPE_OUTPUT_WINDOW, NULL);
506 }
507
508 void             
509 gedit_output_window_clear (GeditOutputWindow *ow)
510 {
511         g_return_if_fail (GEDIT_IS_OUTPUT_WINDOW (ow));
512
513         gtk_list_store_clear (GTK_LIST_STORE (ow->priv->model));
514
515         gtk_widget_set_sensitive (ow->priv->clear_button, FALSE);
516         gtk_widget_set_sensitive (ow->priv->clear_menu_item, FALSE);
517 }
518
519 void
520 gedit_output_window_append_line (GeditOutputWindow *ow, const gchar *line, gboolean scroll)
521 {
522         GtkListStore *store;
523         GtkTreeIter iter;
524         GtkTreePath *path;
525
526         g_return_if_fail (GEDIT_IS_OUTPUT_WINDOW (ow));
527         g_return_if_fail (line != NULL);
528         
529         store = GTK_LIST_STORE (ow->priv->model);
530         g_return_if_fail (store != NULL);
531
532         gtk_list_store_append (store, &iter);
533
534         gtk_list_store_set (store, &iter, COLUMN_LINES, line, -1);
535
536         gtk_widget_set_sensitive (ow->priv->clear_button, TRUE);
537         gtk_widget_set_sensitive (ow->priv->clear_menu_item, TRUE);
538
539         if (!scroll)
540                 return;
541
542         path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
543         g_return_if_fail (path != NULL);
544                 
545         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ow->priv->treeview),
546                                       path, 
547                                       NULL,
548                                       TRUE,
549                                       0.0,
550                                       0.0);
551
552         gtk_tree_path_free (path);
553 }
554
555 void
556 gedit_output_window_prepend_line (GeditOutputWindow *ow, const gchar *line, gboolean scroll)
557 {
558         GtkListStore *store;
559         GtkTreeIter iter;
560         GtkTreePath *path;
561
562         g_return_if_fail (GEDIT_IS_OUTPUT_WINDOW (ow));
563         g_return_if_fail (line != NULL);
564         
565         store = GTK_LIST_STORE (ow->priv->model);
566         g_return_if_fail (store != NULL);
567
568         gtk_list_store_prepend (store, &iter);
569
570         gtk_list_store_set (store, &iter, COLUMN_LINES, line, -1);
571
572         gtk_widget_set_sensitive (ow->priv->clear_button, TRUE);
573         gtk_widget_set_sensitive (ow->priv->clear_menu_item, TRUE);
574
575         if (!scroll)
576                 return;
577
578         path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
579         g_return_if_fail (path != NULL);
580                 
581         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ow->priv->treeview),
582                                       path, 
583                                       NULL,
584                                       TRUE,
585                                       0.0,
586                                       0.0);
587
588         gtk_tree_path_free (path);
589
590 }
591
592
593
594 void
595 gedit_output_window_set_select_multiple (GeditOutputWindow *ow, const GtkSelectionMode type)
596 {
597         GtkTreeSelection        *selection;
598
599         g_return_if_fail (GEDIT_IS_OUTPUT_WINDOW (ow));
600
601         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ow->priv->treeview));
602         gtk_tree_selection_set_mode (selection, type);
603 }