1
/*******************************************************************************
3
* Description: VTE-based terminal emulator
5
* Copyright (C) 2006-2008 David Gómez <david@pleyades.net>
6
* Copyright (C) 2008 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2 as
10
* published by the Free Software Foundation
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
*****************************************************************************/
29
#include <sys/types.h>
36
#include <glib/gstdio.h>
38
#include <gdk/gdkkeysyms.h>
39
#include <pango/pango.h>
42
#define _(String) gettext(String)
43
#define N_(String) (String)
44
#define GETTEXT_PACKAGE "sakura"
46
#define SAY(format,...) do {\
47
if (strcmp("Debug", BUILDTYPE)==0) {\
48
fprintf(stderr, "[%d] ", getpid());\
49
fprintf(stderr, "[%s] ", __FUNCTION__);\
50
if (format) fprintf(stderr, format, ##__VA_ARGS__);\
56
#define PALETTE_SIZE 16
58
/* Color palettes. Color lists borrowed from gnome-terminal source (thanks! ;)) */
59
const GdkColor tango_palette[PALETTE_SIZE] =
61
{ 0, 0x2e2e, 0x3434, 0x3636 },
62
{ 0, 0xcccc, 0x0000, 0x0000 },
63
{ 0, 0x4e4e, 0x9a9a, 0x0606 },
64
{ 0, 0xc4c4, 0xa0a0, 0x0000 },
65
{ 0, 0x3434, 0x6565, 0xa4a4 },
66
{ 0, 0x7575, 0x5050, 0x7b7b },
67
{ 0, 0x0606, 0x9820, 0x9a9a },
68
{ 0, 0xd3d3, 0xd7d7, 0xcfcf },
69
{ 0, 0x5555, 0x5757, 0x5353 },
70
{ 0, 0xefef, 0x2929, 0x2929 },
71
{ 0, 0x8a8a, 0xe2e2, 0x3434 },
72
{ 0, 0xfcfc, 0xe9e9, 0x4f4f },
73
{ 0, 0x7272, 0x9f9f, 0xcfcf },
74
{ 0, 0xadad, 0x7f7f, 0xa8a8 },
75
{ 0, 0x3434, 0xe2e2, 0xe2e2 },
76
{ 0, 0xeeee, 0xeeee, 0xecec }
79
const GdkColor linux_palette[PALETTE_SIZE] =
81
{ 0, 0x0000, 0x0000, 0x0000 },
82
{ 0, 0xaaaa, 0x0000, 0x0000 },
83
{ 0, 0x0000, 0xaaaa, 0x0000 },
84
{ 0, 0xaaaa, 0x5555, 0x0000 },
85
{ 0, 0x0000, 0x0000, 0xaaaa },
86
{ 0, 0xaaaa, 0x0000, 0xaaaa },
87
{ 0, 0x0000, 0xaaaa, 0xaaaa },
88
{ 0, 0xaaaa, 0xaaaa, 0xaaaa },
89
{ 0, 0x5555, 0x5555, 0x5555 },
90
{ 0, 0xffff, 0x5555, 0x5555 },
91
{ 0, 0x5555, 0xffff, 0x5555 },
92
{ 0, 0xffff, 0xffff, 0x5555 },
93
{ 0, 0x5555, 0x5555, 0xffff },
94
{ 0, 0xffff, 0x5555, 0xffff },
95
{ 0, 0x5555, 0xffff, 0xffff },
96
{ 0, 0xffff, 0xffff, 0xffff }
99
const GdkColor xterm_palette[PALETTE_SIZE] =
101
{0, 0x0000, 0x0000, 0x0000 },
102
{0, 0xcdcb, 0x0000, 0x0000 },
103
{0, 0x0000, 0xcdcb, 0x0000 },
104
{0, 0xcdcb, 0xcdcb, 0x0000 },
105
{0, 0x1e1a, 0x908f, 0xffff },
106
{0, 0xcdcb, 0x0000, 0xcdcb },
107
{0, 0x0000, 0xcdcb, 0xcdcb },
108
{0, 0xe5e2, 0xe5e2, 0xe5e2 },
109
{0, 0x4ccc, 0x4ccc, 0x4ccc },
110
{0, 0xffff, 0x0000, 0x0000 },
111
{0, 0x0000, 0xffff, 0x0000 },
112
{0, 0xffff, 0xffff, 0x0000 },
113
{0, 0x4645, 0x8281, 0xb4ae },
114
{0, 0xffff, 0x0000, 0xffff },
115
{0, 0x0000, 0xffff, 0xffff },
116
{0, 0xffff, 0xffff, 0xffff }
119
const GdkColor rxvt_palette[PALETTE_SIZE] =
121
{ 0, 0x0000, 0x0000, 0x0000 },
122
{ 0, 0xcdcd, 0x0000, 0x0000 },
123
{ 0, 0x0000, 0xcdcd, 0x0000 },
124
{ 0, 0xcdcd, 0xcdcd, 0x0000 },
125
{ 0, 0x0000, 0x0000, 0xcdcd },
126
{ 0, 0xcdcd, 0x0000, 0xcdcd },
127
{ 0, 0x0000, 0xcdcd, 0xcdcd },
128
{ 0, 0xfafa, 0xebeb, 0xd7d7 },
129
{ 0, 0x4040, 0x4040, 0x4040 },
130
{ 0, 0xffff, 0x0000, 0x0000 },
131
{ 0, 0x0000, 0xffff, 0x0000 },
132
{ 0, 0xffff, 0xffff, 0x0000 },
133
{ 0, 0x0000, 0x0000, 0xffff },
134
{ 0, 0xffff, 0x0000, 0xffff },
135
{ 0, 0x0000, 0xffff, 0xffff },
136
{ 0, 0xffff, 0xffff, 0xffff }
140
GtkWidget *main_window;
144
PangoFontDescription *font;
155
bool fake_transparency;
157
char *opacity_level_percent;
161
bool show_closebutton;
164
bool blinking_cursor;
166
bool keep_fc; /* Global flag to indicate that we don't want changes in the files and columns values */
167
GtkWidget *item_clear_background; /* We include here only the items which need to be hided */
168
GtkWidget *item_copy_link;
169
GtkWidget *item_open_link;
170
GtkWidget *open_link_separator;
175
const GdkColor *palette;
176
gint add_tab_accelerator;
177
gint del_tab_accelerator;
178
gint switch_tab_accelerator;
179
gint copy_accelerator;
180
gint scrollbar_accelerator;
181
gint open_url_accelerator;
196
GtkWidget *vte; /* Reference to VTE terminal */
197
pid_t pid; /* pid of the forked proccess */
198
GtkWidget *scrollbar;
203
#define ICON_FILE "terminal-tango.svg"
204
#define SCROLL_LINES 4096
205
#define HTTP_REGEXP "(ftp|http)s?://[-a-zA-Z0-9.?$%&/=_~#.,:;+]*"
206
#define CONFIGFILE "sakura.conf"
207
#define DEFAULT_COLUMNS 80
208
#define DEFAULT_ROWS 24
209
#define DEFAULT_FONT "monospace 11"
210
#define DEFAULT_WORD_CHARS "-A-Za-z0-9,./?%&#_~"
211
#define DEFAULT_PALETTE "linux"
212
#define DEFAULT_ADD_TAB_ACCELERATOR (GDK_CONTROL_MASK|GDK_SHIFT_MASK)
213
#define DEFAULT_DEL_TAB_ACCELERATOR (GDK_CONTROL_MASK|GDK_SHIFT_MASK)
214
#define DEFAULT_SWITCH_TAB_ACCELERATOR (GDK_MOD1_MASK)
215
#define DEFAULT_COPY_ACCELERATOR (GDK_CONTROL_MASK|GDK_SHIFT_MASK)
216
#define DEFAULT_SCROLLBAR_ACCELERATOR (GDK_CONTROL_MASK|GDK_SHIFT_MASK)
217
#define DEFAULT_OPEN_URL_ACCELERATOR (GDK_CONTROL_MASK|GDK_SHIFT_MASK)
218
#define DEFAULT_ADD_TAB_KEY GDK_T
219
#define DEFAULT_DEL_TAB_KEY GDK_W
220
#define DEFAULT_PREV_TAB_KEY GDK_Left
221
#define DEFAULT_NEXT_TAB_KEY GDK_Right
222
#define DEFAULT_COPY_KEY GDK_C
223
#define DEFAULT_PASTE_KEY GDK_V
224
#define DEFAULT_SCROLLBAR_KEY GDK_S
225
#define DEFAULT_FULLSCREEN_KEY GDK_F11
226
const char cfg_group[] = "sakura";
228
static GQuark term_data_id = 0;
229
#define sakura_get_page_term( sakura, page_idx ) \
230
(struct terminal*)g_object_get_qdata( \
231
G_OBJECT( gtk_notebook_get_nth_page( (GtkNotebook*)sakura.notebook, page_idx ) ), term_data_id);
233
#define sakura_set_page_term( sakura, page_idx, term ) \
234
g_object_set_qdata_full( \
235
G_OBJECT( gtk_notebook_get_nth_page( (GtkNotebook*)sakura.notebook, page_idx) ), \
236
term_data_id, term, (GDestroyNotify)g_free);
239
static gboolean sakura_key_press (GtkWidget *, GdkEventKey *, gpointer);
240
static void sakura_increase_font (GtkWidget *, void *);
241
static void sakura_decrease_font (GtkWidget *, void *);
242
static void sakura_child_exited (GtkWidget *, void *);
243
static void sakura_eof (GtkWidget *, void *);
244
static void sakura_title_changed (GtkWidget *, void *);
245
static gboolean sakura_delete_window (GtkWidget *, void *);
246
static void sakura_destroy_window (GtkWidget *, void *);
247
static void sakura_font_dialog (GtkWidget *, void *);
248
static void sakura_set_name_dialog (GtkWidget *, void *);
249
static void sakura_color_dialog (GtkWidget *, void *);
250
static void sakura_opacity_dialog (GtkWidget *, void *);
251
static void sakura_set_title_dialog (GtkWidget *, void *);
252
static void sakura_select_background_dialog (GtkWidget *, void *);
253
static void sakura_new_tab (GtkWidget *, void *);
254
static void sakura_close_tab (GtkWidget *, void *);
255
static void sakura_full_screen (GtkWidget *, void *);
256
static void sakura_open_url (GtkWidget *, void *);
257
static void sakura_clear (GtkWidget *, void *);
258
static gboolean sakura_resized_window(GtkWidget *, GdkEventConfigure *, void *);
259
static void sakura_setname_entry_changed(GtkWidget *, void *);
260
static void sakura_copy(GtkWidget *, void *);
261
static void sakura_paste(GtkWidget *, void *);
262
static void sakura_show_first_tab (GtkWidget *widget, void *data);
263
static void sakura_show_close_button (GtkWidget *widget, void *data);
264
static void sakura_show_scrollbar(GtkWidget *, void *);
265
static void sakura_closebutton_clicked(GtkWidget *, void *);
268
static void sakura_error(const char *, ...);
271
static void sakura_init();
272
static void sakura_init_popup();
273
static void sakura_destroy();
274
static void sakura_add_tab();
275
static void sakura_del_tab();
276
static void sakura_set_font();
277
static void sakura_set_size(gint, gint);
278
static void sakura_kill_child();
279
static void sakura_set_bgimage();
280
static void sakura_key_file_set_key(GKeyFile *, const gchar *, const gchar *, guint);
281
static guint sakura_key_file_get_key(GKeyFile *, const gchar *, const gchar *);
283
static const char *option_font;
284
static const char *option_execute;
285
static gboolean option_version=FALSE;
286
static gint option_ntabs=1;
287
static gint option_login = FALSE;
288
static const char *option_title;
289
static int option_rows, option_columns;
290
static gboolean option_hold=FALSE;
292
static GOptionEntry entries[] = {
293
{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, N_("Print version number"), NULL },
294
{ "font", 'f', 0, G_OPTION_ARG_STRING, &option_font, N_("Select initial terminal font"), NULL },
295
{ "ntabs", 'n', 0, G_OPTION_ARG_INT, &option_ntabs, N_("Select initial number of tabs"), NULL },
296
{ "execute", 'e', 0, G_OPTION_ARG_STRING, &option_execute, N_("Execute command"), NULL },
297
{ "login", 'l', 0, G_OPTION_ARG_NONE, &option_login, N_("Login shell"), NULL },
298
{ "title", 't', 0, G_OPTION_ARG_STRING, &option_title, N_("Set window title"), NULL },
299
{ "columns", 'c', 0, G_OPTION_ARG_INT, &option_columns, N_("Set columns number"), NULL },
300
{ "rows", 'r', 0, G_OPTION_ARG_INT, &option_rows, N_("Set rows number"), NULL },
301
{ "hold", 'h', 0, G_OPTION_ARG_NONE, &option_hold, N_("Hold window after execute command"), NULL },
307
gboolean sakura_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
309
unsigned int topage=0;
310
gint npages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
311
gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
313
if (event->type!=GDK_KEY_PRESS) return FALSE;
315
/* add_tab_accelerator + T or del_tab_accelerator + W pressed */
316
if ( (event->state & sakura.add_tab_accelerator)==sakura.add_tab_accelerator &&
317
event->keyval==sakura.add_tab_key ) {
320
} else if ( (event->state & sakura.del_tab_accelerator)==sakura.del_tab_accelerator &&
321
event->keyval==sakura.del_tab_key ) {
323
/* Delete current tab */
324
sakura_del_tab(page);
325
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==0)
330
/* switch_tab_accelerator + number pressed / switch_tab_accelerator + Left-Right cursor */
331
if ( (event->state & sakura.switch_tab_accelerator) == sakura.switch_tab_accelerator ) {
332
if ((event->keyval>=GDK_1) && (event->keyval<=GDK_9)) {
333
switch(event->keyval) {
334
case GDK_1: topage=0; break;
335
case GDK_2: topage=1; break;
336
case GDK_3: topage=2; break;
337
case GDK_4: topage=3; break;
338
case GDK_5: topage=4; break;
339
case GDK_6: topage=5; break;
340
case GDK_7: topage=6; break;
341
case GDK_8: topage=7; break;
342
case GDK_9: topage=8; break;
344
if (topage <= npages)
345
gtk_notebook_set_current_page(GTK_NOTEBOOK(sakura.notebook), topage);
347
} else if (event->keyval==sakura.prev_tab_key) {
348
if (gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook))==0) {
349
gtk_notebook_set_current_page(GTK_NOTEBOOK(sakura.notebook), npages-1);
351
gtk_notebook_prev_page(GTK_NOTEBOOK(sakura.notebook));
354
} else if (event->keyval==sakura.next_tab_key) {
355
if (gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook))==(npages-1)) {
356
gtk_notebook_set_current_page(GTK_NOTEBOOK(sakura.notebook), 0);
358
gtk_notebook_next_page(GTK_NOTEBOOK(sakura.notebook));
364
/* copy_accelerator-[C/V] pressed */
365
if ( (event->state & sakura.copy_accelerator)==sakura.copy_accelerator ) {
366
if (event->keyval==sakura.copy_key) {
367
sakura_copy(NULL, NULL);
369
} else if (event->keyval==sakura.paste_key) {
370
sakura_paste(NULL, NULL);
375
/* scrollbar_accelerator-[S] pressed */
376
if ( (event->state & sakura.scrollbar_accelerator)==sakura.scrollbar_accelerator ) {
377
if (event->keyval==sakura.scrollbar_key) {
378
sakura_show_scrollbar(NULL, NULL);
379
sakura_set_size(sakura.columns, sakura.rows);
384
/* F11 (fullscreen) pressed */
385
if (event->keyval==sakura.fullscreen_key){
386
sakura_full_screen(NULL, NULL);
395
sakura_button_press(GtkWidget *widget, GdkEventButton *button_event, gpointer user_data)
397
struct terminal *term;
401
if (button_event->type != GDK_BUTTON_PRESS)
404
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
405
term = sakura_get_page_term(sakura, page);
407
/* Find out if cursor it's over a matched expression...*/
409
/* Get the column and row relative to pointer position */
410
column = ((glong) (button_event->x) / vte_terminal_get_char_width(
411
VTE_TERMINAL(term->vte)));
412
row = ((glong) (button_event->y) / vte_terminal_get_char_height(
413
VTE_TERMINAL(term->vte)));
414
sakura.current_match = vte_terminal_match_check(VTE_TERMINAL(term->vte), column, row, &tag);
416
/* Left button: open the URL if any */
417
if (button_event->button == 1 &&
418
((button_event->state & sakura.open_url_accelerator) == sakura.open_url_accelerator)
419
&& sakura.current_match) {
421
sakura_open_url(NULL, NULL);
426
/* Right button: show the popup menu */
427
if (button_event->button == 3) {
429
menu = GTK_MENU (widget);
431
if (sakura.current_match) {
432
/* Show the extra options in the menu */
433
gtk_widget_show(sakura.item_open_link);
434
gtk_widget_show(sakura.item_copy_link);
435
gtk_widget_show(sakura.open_link_separator);
437
/* Hide all the options */
438
gtk_widget_hide(sakura.item_open_link);
439
gtk_widget_hide(sakura.item_copy_link);
440
gtk_widget_hide(sakura.open_link_separator);
443
gtk_menu_popup (menu, NULL, NULL, NULL, NULL, button_event->button, button_event->time);
453
sakura_page_removed (GtkWidget *widget, void *data)
455
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==1) {
456
/* If the first tab is disabled, window size changes and we need
457
* to recalculate its size */
458
sakura_set_size(sakura.columns, sakura.rows);
464
sakura_increase_font (GtkWidget *widget, void *data)
468
size=pango_font_description_get_size(sakura.font);
469
pango_font_description_set_size(sakura.font, ((size/PANGO_SCALE)+1) * PANGO_SCALE);
472
sakura_set_size(sakura.columns, sakura.rows);
477
sakura_decrease_font (GtkWidget *widget, void *data)
481
size=pango_font_description_get_size(sakura.font);
482
pango_font_description_set_size(sakura.font, ((size/PANGO_SCALE)-1) * PANGO_SCALE);
485
sakura_set_size(sakura.columns, sakura.rows);
490
sakura_child_exited (GtkWidget *widget, void *data)
493
struct terminal *term;
495
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
496
term = sakura_get_page_term(sakura, page);
498
if (option_hold==TRUE) {
499
SAY("hold option has been activated");
503
SAY("waiting for terminal pid %d", term->pid);
505
waitpid(term->pid, &status, WNOHANG);
506
/* TODO: check wait return */
508
sakura_del_tab(page);
510
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==0)
516
sakura_eof (GtkWidget *widget, void *data)
519
struct terminal *term;
521
SAY("Got EOF signal");
523
/* Workaround for libvte strange behaviour. There is not child-exited signal for
524
the last terminal, so we need to kill it here. Check with libvte authors about
525
child-exited/eof signals */
526
if (gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook))==0) {
528
term = sakura_get_page_term(sakura, 0);
530
if (option_hold==TRUE) {
531
SAY("hold option has been activated");
535
SAY("waiting for terminal pid (in eof) %d", term->pid);
537
waitpid(term->pid, &status, WNOHANG);
538
/* TODO: check wait return */
542
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==0)
549
sakura_title_changed (GtkWidget *widget, void *data)
552
struct terminal *term;
554
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
555
term = sakura_get_page_term(sakura, page);
556
gtk_label_set_text(GTK_LABEL(term->label),vte_terminal_get_window_title(VTE_TERMINAL(term->vte)));
558
gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(sakura.notebook), term->hbox,
559
vte_terminal_get_window_title(VTE_TERMINAL(term->vte)));
560
gtk_window_set_title(GTK_WINDOW(sakura.main_window),
561
vte_terminal_get_window_title(VTE_TERMINAL(term->vte)));
566
sakura_delete_window (GtkWidget *widget, void *data)
570
gint npages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
573
dialog=gtk_message_dialog_new(GTK_WINDOW(sakura.main_window), GTK_DIALOG_MODAL,
574
GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
575
_("There are %d tabs opened.\n\nDo you really want to close Sakura?"), npages);
577
response=gtk_dialog_run(GTK_DIALOG(dialog));
578
gtk_widget_destroy(dialog);
580
if (response==GTK_RESPONSE_YES)
592
sakura_destroy_window (GtkWidget *widget, void *data)
599
sakura_font_dialog (GtkWidget *widget, void *data)
601
GtkWidget *font_dialog;
604
font_dialog=gtk_font_selection_dialog_new(_("Select font"));
605
gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog),
606
pango_font_description_to_string(sakura.font));
608
response=gtk_dialog_run(GTK_DIALOG(font_dialog));
610
if (response==GTK_RESPONSE_OK) {
611
pango_font_description_free(sakura.font);
612
sakura.font=pango_font_description_from_string(gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog)));
614
sakura_set_size(sakura.columns, sakura.rows);
615
g_key_file_set_value(sakura.cfg, cfg_group, "font", pango_font_description_to_string(sakura.font));
618
gtk_widget_destroy(font_dialog);
623
sakura_set_name_dialog (GtkWidget *widget, void *data)
625
GtkWidget *input_dialog;
626
GtkWidget *entry, *label;
627
GtkWidget *name_hbox; /* We need this for correct spacing */
630
struct terminal *term;
632
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
633
term = sakura_get_page_term(sakura, page);
635
input_dialog=gtk_dialog_new_with_buttons(_("Set name"), GTK_WINDOW(sakura.main_window), GTK_DIALOG_MODAL,
636
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
637
GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT, NULL);
639
gtk_dialog_set_default_response(GTK_DIALOG(input_dialog), GTK_RESPONSE_ACCEPT);
640
gtk_window_set_modal(GTK_WINDOW(input_dialog), TRUE);
643
gtk_widget_set_name (input_dialog, "set-name-dialog");
644
gtk_rc_parse_string ("widget \"set-name-dialog\" style \"hig-dialog\"\n");
646
name_hbox=gtk_hbox_new(FALSE, 0);
647
entry=gtk_entry_new();
648
label=gtk_label_new(_("New tab text"));
649
/* Set tab label as entry default text */
650
gtk_entry_set_text(GTK_ENTRY(entry), gtk_notebook_get_tab_label_text(GTK_NOTEBOOK(sakura.notebook), term->hbox));
651
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
652
gtk_box_pack_start(GTK_BOX(name_hbox), label, TRUE, TRUE, 12);
653
gtk_box_pack_start(GTK_BOX(name_hbox), entry, TRUE, TRUE, 12);
654
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(input_dialog)->vbox), name_hbox, FALSE, FALSE, 12);
655
/* Disable accept button until some text is entered */
656
g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sakura_setname_entry_changed), input_dialog);
657
gtk_dialog_set_response_sensitive(GTK_DIALOG(input_dialog), GTK_RESPONSE_ACCEPT, FALSE);
659
gtk_widget_show_all(name_hbox);
661
response=gtk_dialog_run(GTK_DIALOG(input_dialog));
662
if (response==GTK_RESPONSE_ACCEPT) {
663
gtk_label_set_text(GTK_LABEL(term->label), gtk_entry_get_text(GTK_ENTRY(entry)));
665
gtk_widget_destroy(input_dialog);
670
sakura_color_dialog (GtkWidget *widget, void *data)
672
GtkWidget *color_dialog;
673
GtkWidget *label1, *label2;
674
GtkWidget *buttonfore, *buttonback;
675
GtkWidget *hbox_fore, *hbox_back;
678
int i, n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
679
struct terminal *term;
681
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
682
term = sakura_get_page_term(sakura, page);
684
color_dialog=gtk_dialog_new_with_buttons(_("Select color"), GTK_WINDOW(sakura.main_window),
686
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
687
GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT, NULL);
689
gtk_dialog_set_default_response(GTK_DIALOG(color_dialog), GTK_RESPONSE_ACCEPT);
690
gtk_window_set_modal(GTK_WINDOW(color_dialog), TRUE);
692
gtk_widget_set_name (color_dialog, "set-color-dialog");
693
gtk_rc_parse_string ("widget \"set-color-dialog\" style \"hig-dialog\"\n");
695
hbox_fore=gtk_hbox_new(FALSE, 12);
696
hbox_back=gtk_hbox_new(FALSE, 12);
697
label1=gtk_label_new(_("Select foreground color:"));
698
label2=gtk_label_new(_("Select background color:"));
699
buttonfore=gtk_color_button_new_with_color(&sakura.forecolor);
700
buttonback=gtk_color_button_new_with_color(&sakura.backcolor);
702
gtk_box_pack_start(GTK_BOX(hbox_fore), label1, FALSE, FALSE, 12);
703
gtk_box_pack_end(GTK_BOX(hbox_fore), buttonfore, FALSE, FALSE, 12);
704
gtk_box_pack_start(GTK_BOX(hbox_back), label2, FALSE, FALSE, 12);
705
gtk_box_pack_end(GTK_BOX(hbox_back), buttonback, FALSE, FALSE, 12);
706
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(color_dialog)->vbox), hbox_fore, FALSE, FALSE, 6);
707
gtk_box_pack_end(GTK_BOX(GTK_DIALOG(color_dialog)->vbox), hbox_back, FALSE, FALSE, 6);
709
gtk_widget_show_all(GTK_DIALOG(color_dialog)->vbox);
711
response=gtk_dialog_run(GTK_DIALOG(color_dialog));
713
if (response==GTK_RESPONSE_ACCEPT) {
714
gtk_color_button_get_color(GTK_COLOR_BUTTON(buttonfore), &sakura.forecolor);
715
gtk_color_button_get_color(GTK_COLOR_BUTTON(buttonback), &sakura.backcolor);
717
for (i = (n_pages - 1); i >= 0; i--) {
718
term = sakura_get_page_term(sakura, i);
719
vte_terminal_set_colors(VTE_TERMINAL(term->vte), &sakura.forecolor, &sakura.backcolor,
720
sakura.palette, PALETTE_SIZE);
723
vte_terminal_set_colors(VTE_TERMINAL(term->vte), &sakura.forecolor, &sakura.backcolor,
724
sakura.palette, PALETTE_SIZE);
727
cfgtmp = g_strdup_printf("#%02x%02x%02x", sakura.forecolor.red >>8,
728
sakura.forecolor.green>>8, sakura.forecolor.blue>>8);
729
g_key_file_set_value(sakura.cfg, cfg_group, "forecolor", cfgtmp);
732
cfgtmp = g_strdup_printf("#%02x%02x%02x", sakura.backcolor.red >>8,
733
sakura.backcolor.green>>8, sakura.backcolor.blue>>8);
734
g_key_file_set_value(sakura.cfg, cfg_group, "backcolor", cfgtmp);
739
gtk_widget_destroy(color_dialog);
744
sakura_opacity_check (GtkWidget *widget, void *data)
748
state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
751
/* Enable spinbutton */
752
gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
754
/* Disable spinbutton */
755
gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
761
sakura_opacity_dialog (GtkWidget *widget, void *data)
763
GtkWidget *opacity_dialog, *spin_control, *spin_label, *check;
764
GtkObject *spinner_adj;
765
GtkWidget *dialog_hbox, *dialog_vbox, *dialog_spin_hbox;
768
struct terminal *term;
770
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
771
term = sakura_get_page_term(sakura, page);
773
opacity_dialog=gtk_dialog_new_with_buttons(_("Opacity"), GTK_WINDOW(sakura.main_window), GTK_DIALOG_MODAL,
774
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
775
GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT, NULL);
776
gtk_dialog_set_default_response(GTK_DIALOG(opacity_dialog), GTK_RESPONSE_ACCEPT);
777
gtk_window_set_modal(GTK_WINDOW(opacity_dialog), TRUE);
779
gtk_widget_set_name (opacity_dialog, "set-opacity-dialog");
780
gtk_rc_parse_string ("widget \"set-opacity-dialog\" style \"hig-dialog\"\n");
782
spinner_adj = gtk_adjustment_new (((1.0 - sakura.opacity_level) * 100), 0.0, 99.0, 1.0, 5.0, 5.0);
783
spin_control = gtk_spin_button_new(GTK_ADJUSTMENT(spinner_adj), 1.0, 0);
785
spin_label = gtk_label_new(_("Opacity level (%):"));
786
check = gtk_check_button_new_with_label(_("Disable opacity"));
787
dialog_hbox=gtk_hbox_new(FALSE, 0);
788
dialog_vbox=gtk_vbox_new(FALSE, 0);
789
dialog_spin_hbox=gtk_hbox_new(FALSE, 0);
791
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(opacity_dialog)->vbox), dialog_hbox, FALSE, FALSE, 6);
792
gtk_box_pack_start(GTK_BOX(dialog_hbox), dialog_vbox, FALSE, FALSE, 12);
793
gtk_box_pack_start(GTK_BOX(dialog_vbox), check, FALSE, FALSE, 6);
794
gtk_box_pack_start(GTK_BOX(dialog_spin_hbox), spin_label, FALSE, FALSE, 6);
795
gtk_box_pack_start(GTK_BOX(dialog_spin_hbox), spin_control, FALSE, FALSE, 6);
796
gtk_box_pack_start(GTK_BOX(dialog_vbox), dialog_spin_hbox, TRUE, TRUE, 6);
798
g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(sakura_opacity_check), spin_control);
800
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), !sakura.fake_transparency);
802
gtk_widget_show_all(dialog_hbox);
804
response=gtk_dialog_run(GTK_DIALOG(opacity_dialog));
805
if (response==GTK_RESPONSE_ACCEPT) {
807
int i, n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
809
value=g_strdup_printf("%d", gtk_spin_button_get_value_as_int((GtkSpinButton *) spin_control));
810
sakura.opacity_level = ( ( 100 - (atof(value)) ) / 100 );
811
sakura.opacity_level_percent = value;
812
sakura.fake_transparency=!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
814
if (sakura.fake_transparency) {
816
/* Set fake transparency for all tabs */
817
for (i = (n_pages - 1); i >= 0; i--) {
818
term = sakura_get_page_term(sakura, i);
819
vte_terminal_set_background_transparent(VTE_TERMINAL(term->vte), TRUE);
820
vte_terminal_set_background_saturation(VTE_TERMINAL(term->vte), sakura.opacity_level);
823
sakura.fake_transparency = TRUE;
824
g_key_file_set_value(sakura.cfg, cfg_group, "fake_transparency", "Yes");
827
/* Unset fake transparency for all tabs */
828
for (i = (n_pages - 1); i >= 0; i--) {
829
term = sakura_get_page_term(sakura, i);
830
vte_terminal_set_background_transparent(VTE_TERMINAL(term->vte), FALSE);
833
sakura.fake_transparency = FALSE;
834
g_key_file_set_value(sakura.cfg, cfg_group, "fake_transparency", "No");
837
g_key_file_set_value(sakura.cfg, cfg_group, "opacity_level", sakura.opacity_level_percent);
840
gtk_widget_destroy(opacity_dialog);
845
sakura_set_title_dialog (GtkWidget *widget, void *data)
847
GtkWidget *title_dialog;
848
GtkWidget *entry, *label;
849
GtkWidget *title_hbox;
852
struct terminal *term;
854
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
855
term = sakura_get_page_term(sakura, page);
857
title_dialog=gtk_dialog_new_with_buttons(_("Set window title"), GTK_WINDOW(sakura.main_window), GTK_DIALOG_MODAL,
858
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
859
GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT, NULL);
861
gtk_dialog_set_default_response(GTK_DIALOG(title_dialog), GTK_RESPONSE_ACCEPT);
862
gtk_window_set_modal(GTK_WINDOW(title_dialog), TRUE);
864
gtk_widget_set_name (title_dialog, "set-title-dialog");
865
gtk_rc_parse_string ("widget \"set-title-dialog\" style \"hig-dialog\"\n");
867
entry=gtk_entry_new();
868
label=gtk_label_new(_("New window title"));
869
title_hbox=gtk_hbox_new(FALSE, 0);
870
/* Set window label as entry default text */
871
gtk_entry_set_text(GTK_ENTRY(entry), gtk_window_get_title(GTK_WINDOW(sakura.main_window)));
872
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
873
gtk_box_pack_start(GTK_BOX(title_hbox), label, TRUE, TRUE, 12);
874
gtk_box_pack_start(GTK_BOX(title_hbox), entry, TRUE, TRUE, 12);
875
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(title_dialog)->vbox), title_hbox, FALSE, FALSE, 12);
876
/* Disable accept button until some text is entered */
877
g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sakura_setname_entry_changed), title_dialog);
878
gtk_dialog_set_response_sensitive(GTK_DIALOG(title_dialog), GTK_RESPONSE_ACCEPT, FALSE);
880
gtk_widget_show_all(title_hbox);
882
response=gtk_dialog_run(GTK_DIALOG(title_dialog));
883
if (response==GTK_RESPONSE_ACCEPT) {
884
gtk_window_set_title(GTK_WINDOW(sakura.main_window), gtk_entry_get_text(GTK_ENTRY(entry)));
886
gtk_widget_destroy(title_dialog);
892
sakura_select_background_dialog (GtkWidget *widget, void *data)
898
dialog = gtk_file_chooser_dialog_new (_("Select a background file"), GTK_WINDOW(sakura.main_window),
899
GTK_FILE_CHOOSER_ACTION_OPEN,
900
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
901
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
905
response=gtk_dialog_run(GTK_DIALOG(dialog));
906
if (response == GTK_RESPONSE_ACCEPT) {
907
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
908
sakura.background=g_strdup(filename);
909
sakura_set_bgimage(sakura.background);
910
gtk_widget_show(sakura.item_clear_background);
914
gtk_widget_destroy(dialog);
919
sakura_copy_url (GtkWidget *widget, void *data)
923
clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
924
gtk_clipboard_set_text(clip, sakura.current_match, -1 );
929
sakura_open_url (GtkWidget *widget, void *data)
935
browser=(gchar *)g_getenv("BROWSER");
938
cmd=g_strdup_printf("%s %s", browser, sakura.current_match);
940
if ( (browser = g_find_program_in_path("xdg-open")) ) {
941
cmd=g_strdup_printf("%s %s", browser, sakura.current_match);
944
cmd=g_strdup_printf("firefox %s", sakura.current_match);
947
if (!g_spawn_command_line_async(cmd, &error)) {
948
sakura_error("Couldn't exec \"%s\": %s", cmd, error->message);
956
sakura_clear (GtkWidget *widget, void *data)
959
struct terminal *term;
961
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
962
term = sakura_get_page_term(sakura, page);
964
gtk_widget_hide(sakura.item_clear_background);
966
vte_terminal_set_background_image(VTE_TERMINAL(term->vte), NULL);
968
// FIXME: is this really needed? IMHO, this should be done just before
969
// dumping the config to the config file.
970
g_key_file_set_value(sakura.cfg, cfg_group, "background", "none");
972
g_free(sakura.background);
973
sakura.background=NULL;
978
sakura_show_first_tab (GtkWidget *widget, void *data)
981
struct terminal *term;
983
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
984
term = sakura_get_page_term(sakura, page);
986
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
987
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), TRUE);
988
g_key_file_set_value(sakura.cfg, cfg_group, "show_always_first_tab", "Yes");
990
/* Only hide tabs if the notebook has one page */
991
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook)) == 1) {
992
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), FALSE);
994
g_key_file_set_value(sakura.cfg, cfg_group, "show_always_first_tab", "No");
1000
sakura_show_close_button (GtkWidget *widget, void *data)
1003
struct terminal *term;
1005
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1006
term = sakura_get_page_term(sakura, page);
1008
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
1009
g_key_file_set_boolean(sakura.cfg, cfg_group, "closebutton", TRUE);
1011
g_key_file_set_boolean(sakura.cfg, cfg_group, "closebutton", FALSE);
1017
sakura_show_scrollbar (GtkWidget *widget, void *data)
1020
struct terminal *term;
1026
n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
1027
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1028
term = sakura_get_page_term(sakura, page);
1030
if (!g_key_file_get_boolean(sakura.cfg, cfg_group, "scrollbar", NULL)) {
1031
sakura.show_scrollbar=true;
1032
g_key_file_set_boolean(sakura.cfg, cfg_group, "scrollbar", TRUE);
1034
sakura.show_scrollbar=false;
1035
g_key_file_set_boolean(sakura.cfg, cfg_group, "scrollbar", FALSE);
1038
/* Toggle/Untoggle the scrollbar for all tabs */
1039
for (i = (n_pages - 1); i >= 0; i--) {
1040
term = sakura_get_page_term(sakura, i);
1041
if (!sakura.show_scrollbar)
1042
gtk_widget_hide(term->scrollbar);
1044
gtk_widget_show(term->scrollbar);
1050
sakura_audible_bell (GtkWidget *widget, void *data)
1053
struct terminal *term;
1055
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1056
term = sakura_get_page_term(sakura, page);
1058
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
1059
vte_terminal_set_audible_bell (VTE_TERMINAL(term->vte), TRUE);
1060
g_key_file_set_value(sakura.cfg, cfg_group, "audible_bell", "Yes");
1062
vte_terminal_set_audible_bell (VTE_TERMINAL(term->vte), FALSE);
1063
g_key_file_set_value(sakura.cfg, cfg_group, "audible_bell", "No");
1069
sakura_visible_bell (GtkWidget *widget, void *data)
1072
struct terminal *term;
1074
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1075
term = sakura_get_page_term(sakura, page);
1077
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
1078
vte_terminal_set_visible_bell (VTE_TERMINAL(term->vte), TRUE);
1079
g_key_file_set_value(sakura.cfg, cfg_group, "visible_bell", "Yes");
1081
vte_terminal_set_visible_bell (VTE_TERMINAL(term->vte), FALSE);
1082
g_key_file_set_value(sakura.cfg, cfg_group, "visible_bell", "No");
1088
sakura_blinking_cursor (GtkWidget *widget, void *data)
1091
struct terminal *term;
1093
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1094
term = sakura_get_page_term(sakura, page);
1096
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
1097
vte_terminal_set_cursor_blink_mode (VTE_TERMINAL(term->vte), VTE_CURSOR_BLINK_ON);
1098
g_key_file_set_value(sakura.cfg, cfg_group, "blinking_cursor", "Yes");
1100
vte_terminal_set_cursor_blink_mode (VTE_TERMINAL(term->vte), VTE_CURSOR_BLINK_OFF);
1101
g_key_file_set_value(sakura.cfg, cfg_group, "blinking_cursor", "No");
1107
sakura_set_palette(GtkWidget *widget, void *data)
1109
struct terminal *term;
1112
char *palette=(char *)data;
1114
n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
1116
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
1117
if (strcmp(palette, "linux")==0) {
1118
sakura.palette=linux_palette;
1119
} else if (strcmp(palette, "tango")==0) {
1120
sakura.palette=tango_palette;
1121
} else if (strcmp(palette, "xterm")==0) {
1122
sakura.palette=xterm_palette;
1124
sakura.palette=rxvt_palette;
1127
for (i = (n_pages - 1); i >= 0; i--) {
1128
term = sakura_get_page_term(sakura, i);
1129
vte_terminal_set_colors(VTE_TERMINAL(term->vte), &sakura.forecolor, &sakura.backcolor,
1130
sakura.palette, PALETTE_SIZE);
1133
g_key_file_set_value(sakura.cfg, cfg_group, "palette", palette);
1138
/* Every the window changes its size by an user action (resize, fullscreen), calculate
1139
* the new values for the number of columns and rows */
1141
sakura_calculate_row_col (gint width, gint height)
1143
struct terminal *term;
1144
gint x_padding, y_padding;
1145
gint n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
1147
if (n_pages==-1) return;
1149
SAY("Calculating row_col");
1150
term = sakura_get_page_term(sakura, 0);
1152
/* This is to prevent a race with ConfigureEvents when the window is being destroyed */
1153
if (!VTE_IS_TERMINAL(term->vte)) return;
1155
vte_terminal_get_padding( VTE_TERMINAL(term->vte), &x_padding, &y_padding );
1156
sakura.char_width = vte_terminal_get_char_width(VTE_TERMINAL(term->vte));
1157
sakura.char_height = vte_terminal_get_char_height(VTE_TERMINAL(term->vte));
1158
/* Ignore resize events in sakura window is in fullscreen */
1159
if (!sakura.keep_fc) {
1160
/* We cannot trust in vte allocation values, they're unreliable */
1161
/* FIXME: Round values properly */
1162
sakura.columns = (width/sakura.char_width);
1163
sakura.rows = (height/sakura.char_height);
1164
sakura.keep_fc=false;
1165
SAY("new columns %ld and rows %ld", sakura.columns, sakura.rows);
1167
sakura.width = sakura.main_window->allocation.width + x_padding;
1168
sakura.height = sakura.main_window->allocation.height + y_padding;
1173
/* Retrieve the cwd of the specified term page.
1174
* Original function was from terminal-screen.c of gnome-terminal, copyright (C) 2001 Havoc Pennington
1175
* Adapted by Hong Jen Yee, non-linux shit removed by David Gómez */
1177
sakura_get_term_cwd(struct terminal* term)
1181
if (term->pid >= 0) {
1183
char buf[PATH_MAX+1];
1186
file = g_strdup_printf ("/proc/%d/cwd", term->pid);
1187
len = readlink (file, buf, sizeof (buf) - 1);
1189
if (len > 0 && buf[0] == '/') {
1191
cwd = g_strdup(buf);
1202
sakura_resized_window (GtkWidget *widget, GdkEventConfigure *event, void *data)
1204
if (event->width!=sakura.width || event->height!=sakura.height) {
1205
SAY("sakura w & h %d %d event w & h %d %d",
1206
sakura.width, sakura.height, event->width, event->height);
1207
/* Window has been resized by the user. Recalculate sizes */
1208
sakura_calculate_row_col (event->width, event->height);
1216
sakura_setname_entry_changed (GtkWidget *widget, void *data)
1218
GtkDialog *title_dialog=(GtkDialog *)data;
1220
if (strcmp(gtk_entry_get_text(GTK_ENTRY(widget)), "")==0) {
1221
gtk_dialog_set_response_sensitive(GTK_DIALOG(title_dialog), GTK_RESPONSE_ACCEPT, FALSE);
1223
gtk_dialog_set_response_sensitive(GTK_DIALOG(title_dialog), GTK_RESPONSE_ACCEPT, TRUE);
1228
/* Parameters are never used */
1230
sakura_copy (GtkWidget *widget, void *data)
1233
struct terminal *term;
1235
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1236
term = sakura_get_page_term(sakura, page);
1238
vte_terminal_copy_clipboard(VTE_TERMINAL(term->vte));
1242
/* Parameters are never used */
1244
sakura_paste (GtkWidget *widget, void *data)
1247
struct terminal *term;
1249
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1250
term = sakura_get_page_term(sakura, page);
1252
vte_terminal_paste_clipboard(VTE_TERMINAL(term->vte));
1257
sakura_new_tab (GtkWidget *widget, void *data)
1264
sakura_close_tab (GtkWidget *widget, void *data)
1266
gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1268
sakura_del_tab(page);
1270
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==0)
1276
sakura_full_screen (GtkWidget *widget, void *data)
1278
if (sakura.full_screen!=TRUE) {
1279
sakura.full_screen=TRUE;
1280
gtk_window_fullscreen(GTK_WINDOW(sakura.main_window));
1282
gtk_window_unfullscreen(GTK_WINDOW(sakura.main_window));
1283
sakura.full_screen=FALSE;
1288
/* Callback for the tabs close buttons */
1290
sakura_closebutton_clicked(GtkWidget *widget, void *data)
1293
GtkWidget *hbox=(GtkWidget *)data;
1295
page = gtk_notebook_page_num(GTK_NOTEBOOK(sakura.notebook), hbox);
1296
sakura_del_tab(page);
1298
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook))==0)
1303
/******* Functions ********/
1308
GError *gerror=NULL;
1309
char* configdir = NULL;
1311
term_data_id = g_quark_from_static_string("sakura_term");
1313
/* Config file initialization*/
1314
sakura.cfg = g_key_file_new();
1316
configdir = g_build_filename( g_get_user_config_dir(), "sakura", NULL );
1317
if( ! g_file_test( g_get_user_config_dir(), G_FILE_TEST_EXISTS) )
1318
g_mkdir( g_get_user_config_dir(), 0755 );
1319
if( ! g_file_test( configdir, G_FILE_TEST_EXISTS) )
1320
g_mkdir( configdir, 0755 );
1321
/* Use more standard-conforming path for config files, if available. */
1322
sakura.configfile=g_build_filename(configdir, CONFIGFILE, NULL);
1325
if (!g_key_file_load_from_file(sakura.cfg, sakura.configfile, 0, &gerror)) {
1326
char *file_contents;
1327
char *new_file_contents;
1329
/* Workaround for cfgpool to g_key_file update. We update the config
1330
* file here if needed. This support should be removed in future
1331
* versions (3.0?) as everyone is supposed to be using a recent (no cfgpool)
1332
* sakura release in the future */
1333
rename(sakura.configfile, "/tmp/sakura.cfg.old");
1334
g_file_get_contents("/tmp/sakura.cfg.old", &file_contents, NULL, NULL);
1335
new_file_contents=g_strconcat("[sakura]\n", file_contents, NULL);
1336
g_file_set_contents(sakura.configfile, new_file_contents, strlen(new_file_contents), NULL);
1337
g_free(file_contents); g_free(new_file_contents);
1338
unlink("/tmp/sakura.cfg.old");
1339
g_key_file_load_from_file(sakura.cfg, sakura.configfile, 0, &gerror);
1343
/* Add default values if needed */
1344
gchar *cfgtmp = NULL;
1346
/* We can safely ignore errors from g_key_file_get_value(), since if the
1347
* call to g_key_file_has_key() was successful, the key IS there. From the
1348
* glib docs I don't know if we can ignore errors from g_key_file_has_key,
1349
* too. I think we can: the only possible error is that the config file
1350
* doesn't exist, but we have just read it!
1353
if (!g_key_file_has_key(sakura.cfg, cfg_group, "forecolor", NULL)) {
1354
g_key_file_set_value(sakura.cfg, cfg_group, "forecolor", "#c0c0c0");
1356
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "forecolor", NULL);
1357
gdk_color_parse(cfgtmp, &sakura.forecolor);
1361
if (!g_key_file_has_key(sakura.cfg, cfg_group, "backcolor", NULL)) {
1362
g_key_file_set_value(sakura.cfg, cfg_group, "backcolor", "#000000");
1364
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "backcolor", NULL);
1365
gdk_color_parse(cfgtmp, &sakura.backcolor);
1369
if (!g_key_file_has_key(sakura.cfg, cfg_group, "opacity_level", NULL)) {
1370
g_key_file_set_value(sakura.cfg, cfg_group, "opacity_level", "80");
1372
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "opacity_level", NULL);
1373
sakura.opacity_level_percent=cfgtmp;
1374
sakura.opacity_level=( ( 100 - (atof(cfgtmp)) ) / 100 );
1378
if (!g_key_file_has_key(sakura.cfg, cfg_group, "fake_transparency", NULL)) {
1379
g_key_file_set_value(sakura.cfg, cfg_group, "fake_transparency", "No");
1381
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "fake_transparency", NULL);
1382
if (strcmp(cfgtmp, "Yes")==0) {
1383
sakura.fake_transparency=1;
1385
sakura.fake_transparency=0;
1390
if (!g_key_file_has_key(sakura.cfg, cfg_group, "background", NULL)) {
1391
g_key_file_set_value(sakura.cfg, cfg_group, "background", "none");
1393
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "background", NULL);
1394
if (strcmp(cfgtmp, "none")==0) {
1395
sakura.background=NULL;
1397
sakura.background=g_strdup(cfgtmp);
1402
if (!g_key_file_has_key(sakura.cfg, cfg_group, "font", NULL)) {
1403
g_key_file_set_value(sakura.cfg, cfg_group, "font", DEFAULT_FONT);
1406
if (!g_key_file_has_key(sakura.cfg, cfg_group, "show_always_first_tab", NULL)) {
1407
g_key_file_set_value(sakura.cfg, cfg_group, "show_always_first_tab", "No");
1410
if (!g_key_file_has_key(sakura.cfg, cfg_group, "scrollbar", NULL)) {
1411
g_key_file_set_boolean(sakura.cfg, cfg_group, "scrollbar", FALSE);
1413
sakura.show_scrollbar = g_key_file_get_boolean(sakura.cfg, cfg_group, "scrollbar", NULL);
1415
if (!g_key_file_has_key(sakura.cfg, cfg_group, "closebutton", NULL)) {
1416
g_key_file_set_boolean(sakura.cfg, cfg_group, "closebutton", FALSE);
1418
sakura.show_closebutton = g_key_file_get_boolean(sakura.cfg, cfg_group, "closebutton", NULL);
1420
if (!g_key_file_has_key(sakura.cfg, cfg_group, "audible_bell", NULL)) {
1421
g_key_file_set_value(sakura.cfg, cfg_group, "audible_bell", "Yes");
1423
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "audible_bell", NULL);
1424
sakura.audible_bell= (strcmp(cfgtmp, "Yes")==0) ? 1 : 0;
1427
if (!g_key_file_has_key(sakura.cfg, cfg_group, "visible_bell", NULL)) {
1428
g_key_file_set_value(sakura.cfg, cfg_group, "visible_bell", "No");
1430
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "visible_bell", NULL);
1431
sakura.visible_bell= (strcmp(cfgtmp, "Yes")==0) ? 1 : 0;
1434
if (!g_key_file_has_key(sakura.cfg, cfg_group, "blinking_cursor", NULL)) {
1435
g_key_file_set_value(sakura.cfg, cfg_group, "blinking_cursor", "No");
1437
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "blinking_cursor", NULL);
1438
sakura.blinking_cursor= (strcmp(cfgtmp, "Yes")==0) ? 1 : 0;
1441
if (!g_key_file_has_key(sakura.cfg, cfg_group, "word_chars", NULL)) {
1442
g_key_file_set_value(sakura.cfg, cfg_group, "word_chars", DEFAULT_WORD_CHARS);
1444
sakura.word_chars = g_key_file_get_value(sakura.cfg, cfg_group, "word_chars", NULL);
1446
if (!g_key_file_has_key(sakura.cfg, cfg_group, "palette", NULL)) {
1447
g_key_file_set_value(sakura.cfg, cfg_group, "palette", DEFAULT_PALETTE);
1449
cfgtmp = g_key_file_get_string(sakura.cfg, cfg_group, "palette", NULL);
1450
if (strcmp(cfgtmp, "linux")==0) {
1451
sakura.palette=linux_palette;
1452
} else if (strcmp(cfgtmp, "tango")==0) {
1453
sakura.palette=tango_palette;
1454
} else if (strcmp(cfgtmp, "xterm")==0) {
1455
sakura.palette=xterm_palette;
1457
sakura.palette=rxvt_palette;
1461
if (!g_key_file_has_key(sakura.cfg, cfg_group, "add_tab_accelerator", NULL)) {
1462
g_key_file_set_integer(sakura.cfg, cfg_group, "add_tab_accelerator", DEFAULT_ADD_TAB_ACCELERATOR);
1464
sakura.add_tab_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "add_tab_accelerator", NULL);
1466
if (!g_key_file_has_key(sakura.cfg, cfg_group, "del_tab_accelerator", NULL)) {
1467
g_key_file_set_integer(sakura.cfg, cfg_group, "del_tab_accelerator", DEFAULT_DEL_TAB_ACCELERATOR);
1469
sakura.del_tab_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "del_tab_accelerator", NULL);
1471
if (!g_key_file_has_key(sakura.cfg, cfg_group, "switch_tab_accelerator", NULL)) {
1472
g_key_file_set_integer(sakura.cfg, cfg_group, "switch_tab_accelerator", DEFAULT_SWITCH_TAB_ACCELERATOR);
1474
sakura.switch_tab_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "switch_tab_accelerator", NULL);
1476
if (!g_key_file_has_key(sakura.cfg, cfg_group, "copy_accelerator", NULL)) {
1477
g_key_file_set_integer(sakura.cfg, cfg_group, "copy_accelerator", DEFAULT_COPY_ACCELERATOR);
1479
sakura.copy_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "copy_accelerator", NULL);
1481
if (!g_key_file_has_key(sakura.cfg, cfg_group, "scrollbar_accelerator", NULL)) {
1482
g_key_file_set_integer(sakura.cfg, cfg_group, "scrollbar_accelerator", DEFAULT_SCROLLBAR_ACCELERATOR);
1484
sakura.scrollbar_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "scrollbar_accelerator", NULL);
1486
if (!g_key_file_has_key(sakura.cfg, cfg_group, "open_url_accelerator", NULL)) {
1487
g_key_file_set_integer(sakura.cfg, cfg_group, "open_url_accelerator", DEFAULT_OPEN_URL_ACCELERATOR);
1489
sakura.open_url_accelerator = g_key_file_get_integer(sakura.cfg, cfg_group, "open_url_accelerator", NULL);
1491
if (!g_key_file_has_key(sakura.cfg, cfg_group, "add_tab_key", NULL)) {
1492
sakura_key_file_set_key(sakura.cfg, cfg_group, "add_tab_key", DEFAULT_ADD_TAB_KEY);
1494
sakura.add_tab_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "add_tab_key");
1496
if (!g_key_file_has_key(sakura.cfg, cfg_group, "del_tab_key", NULL)) {
1497
sakura_key_file_set_key(sakura.cfg, cfg_group, "del_tab_key", DEFAULT_DEL_TAB_KEY);
1499
sakura.del_tab_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "del_tab_key");
1501
if (!g_key_file_has_key(sakura.cfg, cfg_group, "prev_tab_key", NULL)) {
1502
sakura_key_file_set_key(sakura.cfg, cfg_group, "prev_tab_key", DEFAULT_PREV_TAB_KEY);
1504
sakura.prev_tab_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "prev_tab_key");
1506
if (!g_key_file_has_key(sakura.cfg, cfg_group, "next_tab_key", NULL)) {
1507
sakura_key_file_set_key(sakura.cfg, cfg_group, "next_tab_key", DEFAULT_NEXT_TAB_KEY);
1509
sakura.next_tab_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "next_tab_key");
1511
if (!g_key_file_has_key(sakura.cfg, cfg_group, "copy_key", NULL)) {
1512
sakura_key_file_set_key(sakura.cfg, cfg_group, "copy_key", DEFAULT_COPY_KEY);
1514
sakura.copy_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "copy_key");
1516
if (!g_key_file_has_key(sakura.cfg, cfg_group, "paste_key", NULL)) {
1517
sakura_key_file_set_key(sakura.cfg, cfg_group, "paste_key", DEFAULT_PASTE_KEY);
1519
sakura.paste_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "paste_key");
1521
if (!g_key_file_has_key(sakura.cfg, cfg_group, "scrollbar_key", NULL)) {
1522
sakura_key_file_set_key(sakura.cfg, cfg_group, "scrollbar_key", DEFAULT_SCROLLBAR_KEY);
1524
sakura.scrollbar_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "scrollbar_key");
1526
if (!g_key_file_has_key(sakura.cfg, cfg_group, "fullscreen_key", NULL)) {
1527
sakura_key_file_set_key(sakura.cfg, cfg_group, "fullscreen_key", DEFAULT_FULLSCREEN_KEY);
1529
sakura.fullscreen_key = sakura_key_file_get_key(sakura.cfg, cfg_group, "fullscreen_key");
1531
/* Set dialog style */
1532
gtk_rc_parse_string ("style \"hig-dialog\" {\n"
1533
"GtkDialog::action-area-border = 12\n"
1534
"GtkDialog::button-spacing = 12\n"
1537
sakura.main_window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
1538
gtk_window_set_title(GTK_WINDOW(sakura.main_window), "sakura");
1539
gtk_window_set_icon_from_file(GTK_WINDOW(sakura.main_window), DATADIR "/pixmaps/" ICON_FILE, &gerror);
1540
/* Default terminal size*/
1541
sakura.columns = DEFAULT_COLUMNS;
1542
sakura.rows = DEFAULT_ROWS;
1544
sakura.notebook=gtk_notebook_new();
1546
/* Set argv for forked childs */
1548
sakura.argv[0]=g_strdup_printf("-%s", g_getenv("SHELL"));
1550
sakura.argv[0]=g_strdup(g_getenv("SHELL"));
1552
sakura.argv[1]=NULL;
1555
gtk_window_set_title(GTK_WINDOW(sakura.main_window), option_title);
1558
if (option_columns) {
1559
sakura.columns = option_columns;
1563
sakura.rows = option_rows;
1567
sakura.font=pango_font_description_from_string(option_font);
1569
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "font", NULL);
1570
sakura.font = pango_font_description_from_string(cfgtmp);
1574
sakura.menu=gtk_menu_new();
1575
sakura.label_count=1;
1576
sakura.full_screen=FALSE;
1577
sakura.keep_fc=false;
1580
sakura.http_regexp=g_regex_new(HTTP_REGEXP, G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY, &gerror);
1582
gtk_container_add(GTK_CONTAINER(sakura.main_window), sakura.notebook);
1585
gtk_notebook_set_scrollable(GTK_NOTEBOOK(sakura.notebook), TRUE);
1587
sakura_init_popup();
1589
g_signal_connect(G_OBJECT(sakura.main_window), "delete_event", G_CALLBACK(sakura_delete_window), NULL);
1590
g_signal_connect(G_OBJECT(sakura.main_window), "destroy", G_CALLBACK(sakura_destroy_window), NULL);
1591
g_signal_connect(G_OBJECT(sakura.main_window), "key-press-event", G_CALLBACK(sakura_key_press), NULL);
1592
g_signal_connect(G_OBJECT(sakura.main_window), "configure-event", G_CALLBACK(sakura_resized_window), NULL);
1599
GtkWidget *item_new_tab, *item_set_name, *item_close_tab, *item_copy,
1600
*item_paste, *item_select_font, *item_select_colors,
1601
*item_select_background, *item_set_title, *item_full_screen,
1602
*item_toggle_scrollbar, *item_options, *item_input_methods,
1603
*item_opacity_menu, *item_show_first_tab, *item_audible_bell, *item_visible_bell,
1604
*item_blinking_cursor,
1605
*item_palette, *item_palette_tango, *item_palette_linux, *item_palette_xterm, *item_palette_rxvt,
1606
*item_show_close_button;
1607
GtkAction *action_open_link, *action_copy_link, *action_new_tab, *action_set_name, *action_close_tab,
1608
*action_copy, *action_paste, *action_select_font, *action_select_colors,
1609
*action_select_background, *action_clear_background, *action_opacity, *action_set_title,
1610
*action_full_screen;
1611
GtkWidget *options_menu, *palette_menu;
1613
/* Define actions */
1614
action_open_link=gtk_action_new("open_link", _("Open link..."), NULL, NULL);
1615
action_copy_link=gtk_action_new("copy_link", _("Copy link..."), NULL, NULL);
1616
action_new_tab=gtk_action_new("new_tab", _("New tab"), NULL, GTK_STOCK_NEW);
1617
action_set_name=gtk_action_new("set_name", _("Set name..."), NULL, NULL);
1618
action_close_tab=gtk_action_new("close_tab", _("Close tab"), NULL, GTK_STOCK_CLOSE);
1619
action_full_screen=gtk_action_new("full_screen", _("Full screen"), NULL, GTK_STOCK_FULLSCREEN);
1620
action_copy=gtk_action_new("copy", _("Copy"), NULL, GTK_STOCK_COPY);
1621
action_paste=gtk_action_new("paste", _("Paste"), NULL, GTK_STOCK_PASTE);
1622
action_select_font=gtk_action_new("select_font", _("Select font..."), NULL, GTK_STOCK_SELECT_FONT);
1623
action_select_colors=gtk_action_new("select_colors", _("Select colors..."), NULL, GTK_STOCK_SELECT_COLOR);
1624
action_select_background=gtk_action_new("select_background", _("Select background..."), NULL, NULL);
1625
action_clear_background=gtk_action_new("clear_background", _("Clear background"), NULL, NULL);
1626
action_opacity=gtk_action_new("set_opacity", _("Set opacity level..."), NULL, NULL);
1627
action_set_title=gtk_action_new("set_title", _("Set window title..."), NULL, NULL);
1629
/* Create menuitems */
1630
sakura.item_open_link=gtk_action_create_menu_item(action_open_link);
1631
sakura.item_copy_link=gtk_action_create_menu_item(action_copy_link);
1632
item_new_tab=gtk_action_create_menu_item(action_new_tab);
1633
item_set_name=gtk_action_create_menu_item(action_set_name);
1634
item_close_tab=gtk_action_create_menu_item(action_close_tab);
1635
item_full_screen=gtk_action_create_menu_item(action_full_screen);
1636
item_copy=gtk_action_create_menu_item(action_copy);
1637
item_paste=gtk_action_create_menu_item(action_paste);
1638
item_select_font=gtk_action_create_menu_item(action_select_font);
1639
item_select_colors=gtk_action_create_menu_item(action_select_colors);
1640
item_select_background=gtk_action_create_menu_item(action_select_background);
1641
sakura.item_clear_background=gtk_action_create_menu_item(action_clear_background);
1642
item_opacity_menu=gtk_action_create_menu_item(action_opacity);
1643
item_set_title=gtk_action_create_menu_item(action_set_title);
1645
item_show_first_tab=gtk_check_menu_item_new_with_label(_("Show always first tab"));
1646
item_show_close_button=gtk_check_menu_item_new_with_label(_("Show tab close button"));
1647
item_toggle_scrollbar=gtk_check_menu_item_new_with_label(_("Toggle scrollbar"));
1648
item_audible_bell=gtk_check_menu_item_new_with_label(_("Set audible bell"));
1649
item_visible_bell=gtk_check_menu_item_new_with_label(_("Set visible bell"));
1650
item_blinking_cursor=gtk_check_menu_item_new_with_label(_("Set blinking cursor"));
1651
item_input_methods=gtk_menu_item_new_with_label(_("Input methods"));
1652
item_palette_tango=gtk_radio_menu_item_new_with_label(NULL, "Tango");
1653
item_palette_linux=gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(item_palette_tango), "Linux");
1654
item_palette_xterm=gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(item_palette_tango), "xterm");
1655
item_palette_rxvt=gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM(item_palette_tango), "rxvt");
1656
item_options=gtk_menu_item_new_with_label(_("Options"));
1657
item_palette=gtk_menu_item_new_with_label(_("Set palette"));
1659
/* Show defaults in menu items */
1660
gchar *cfgtmp = NULL;
1662
cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "show_always_first_tab", NULL);
1663
if (strcmp(cfgtmp, "Yes")==0) {
1664
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_show_first_tab), TRUE);
1666
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_show_first_tab), FALSE);
1670
if (sakura.show_closebutton) {
1671
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_show_close_button), TRUE);
1673
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_show_close_button), FALSE);
1676
if (sakura.show_scrollbar) {
1677
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_toggle_scrollbar), TRUE);
1679
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_toggle_scrollbar), FALSE);
1682
if (sakura.audible_bell) {
1683
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_audible_bell), TRUE);
1686
if (sakura.visible_bell) {
1687
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_visible_bell), TRUE);
1690
if (sakura.blinking_cursor) {
1691
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_blinking_cursor), TRUE);
1694
cfgtmp = g_key_file_get_string(sakura.cfg, cfg_group, "palette", NULL);
1695
if (strcmp(cfgtmp, "linux")==0) {
1696
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_palette_linux), TRUE);
1697
} else if (strcmp(cfgtmp, "tango")==0) {
1698
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_palette_tango), TRUE);
1699
} else if (strcmp(cfgtmp, "xterm")==0) {
1700
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_palette_xterm), TRUE);
1702
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item_palette_rxvt), TRUE);
1706
sakura.open_link_separator=gtk_separator_menu_item_new();
1708
/* Add items to popup menu */
1709
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), sakura.item_open_link);
1710
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), sakura.item_copy_link);
1711
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), sakura.open_link_separator);
1712
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_new_tab);
1713
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_set_name);
1714
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_close_tab);
1715
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), gtk_separator_menu_item_new());
1716
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_full_screen);
1717
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), gtk_separator_menu_item_new());
1718
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_copy);
1719
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_paste);
1720
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), gtk_separator_menu_item_new());
1721
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_select_colors);
1722
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_select_font);
1723
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_select_background);
1724
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), sakura.item_clear_background);
1725
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), gtk_separator_menu_item_new());
1726
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_options);
1727
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), gtk_separator_menu_item_new());
1728
gtk_menu_shell_append(GTK_MENU_SHELL(sakura.menu), item_input_methods);
1730
sakura.im_menu=gtk_menu_new();
1731
options_menu=gtk_menu_new();
1732
palette_menu=gtk_menu_new();
1734
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_show_first_tab);
1735
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_show_close_button);
1736
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_toggle_scrollbar);
1737
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_audible_bell);
1738
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_visible_bell);
1739
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_blinking_cursor);
1740
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), gtk_separator_menu_item_new());
1741
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_opacity_menu);
1742
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_set_title);
1743
gtk_menu_shell_append(GTK_MENU_SHELL(options_menu), item_palette);
1744
gtk_menu_shell_append(GTK_MENU_SHELL(palette_menu), item_palette_tango);
1745
gtk_menu_shell_append(GTK_MENU_SHELL(palette_menu), item_palette_linux);
1746
gtk_menu_shell_append(GTK_MENU_SHELL(palette_menu), item_palette_xterm);
1747
gtk_menu_shell_append(GTK_MENU_SHELL(palette_menu), item_palette_rxvt);
1749
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_input_methods), sakura.im_menu);
1750
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_options), options_menu);
1751
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_palette), palette_menu);
1753
/* ... and finally assign callbacks to menuitems */
1754
g_signal_connect(G_OBJECT(action_new_tab), "activate", G_CALLBACK(sakura_new_tab), NULL);
1755
g_signal_connect(G_OBJECT(action_set_name), "activate", G_CALLBACK(sakura_set_name_dialog), NULL);
1756
g_signal_connect(G_OBJECT(action_close_tab), "activate", G_CALLBACK(sakura_close_tab), NULL);
1757
g_signal_connect(G_OBJECT(action_select_font), "activate", G_CALLBACK(sakura_font_dialog), NULL);
1758
g_signal_connect(G_OBJECT(action_select_background), "activate",
1759
G_CALLBACK(sakura_select_background_dialog), NULL);
1760
g_signal_connect(G_OBJECT(action_copy), "activate", G_CALLBACK(sakura_copy), NULL);
1761
g_signal_connect(G_OBJECT(action_paste), "activate", G_CALLBACK(sakura_paste), NULL);
1762
g_signal_connect(G_OBJECT(action_select_colors), "activate", G_CALLBACK(sakura_color_dialog), NULL);
1764
g_signal_connect(G_OBJECT(item_show_first_tab), "activate", G_CALLBACK(sakura_show_first_tab), NULL);
1765
g_signal_connect(G_OBJECT(item_show_close_button), "activate", G_CALLBACK(sakura_show_close_button), NULL);
1766
g_signal_connect(G_OBJECT(item_toggle_scrollbar), "activate", G_CALLBACK(sakura_show_scrollbar), NULL);
1767
g_signal_connect(G_OBJECT(item_audible_bell), "activate", G_CALLBACK(sakura_audible_bell), NULL);
1768
g_signal_connect(G_OBJECT(item_visible_bell), "activate", G_CALLBACK(sakura_visible_bell), NULL);
1769
g_signal_connect(G_OBJECT(item_blinking_cursor), "activate", G_CALLBACK(sakura_blinking_cursor), NULL);
1770
g_signal_connect(G_OBJECT(action_opacity), "activate", G_CALLBACK(sakura_opacity_dialog), NULL);
1771
g_signal_connect(G_OBJECT(action_set_title), "activate", G_CALLBACK(sakura_set_title_dialog), NULL);
1772
g_signal_connect(G_OBJECT(item_palette_tango), "activate", G_CALLBACK(sakura_set_palette), "tango");
1773
g_signal_connect(G_OBJECT(item_palette_linux), "activate", G_CALLBACK(sakura_set_palette), "linux");
1774
g_signal_connect(G_OBJECT(item_palette_xterm), "activate", G_CALLBACK(sakura_set_palette), "xterm");
1775
g_signal_connect(G_OBJECT(item_palette_rxvt), "activate", G_CALLBACK(sakura_set_palette), "rxvt");
1777
g_signal_connect(G_OBJECT(action_open_link), "activate", G_CALLBACK(sakura_open_url), NULL);
1778
g_signal_connect(G_OBJECT(action_copy_link), "activate", G_CALLBACK(sakura_copy_url), NULL);
1779
g_signal_connect(G_OBJECT(action_clear_background), "activate", G_CALLBACK(sakura_clear), NULL);
1780
g_signal_connect(G_OBJECT(action_full_screen), "activate", G_CALLBACK(sakura_full_screen), NULL);
1783
gtk_widget_show_all(sakura.menu);
1785
/* We don't want to see this if there's no background image */
1786
if (!sakura.background) {
1787
gtk_widget_hide(sakura.item_clear_background);
1795
SAY("Destroying sakura");
1797
/* Delete all existing tabs */
1798
while (gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook)) >= 1) {
1802
pango_font_description_free(sakura.font);
1804
if (sakura.background)
1805
free(sakura.background);
1807
GError *gerror = NULL;
1809
gchar *data = g_key_file_to_data(sakura.cfg, &len, &gerror);
1811
fprintf(stderr, "%s\n", gerror->message);
1816
GIOChannel *cfgfile = g_io_channel_new_file(sakura.configfile, "w", &gerror);
1818
fprintf(stderr, "%s\n", gerror->message);
1822
/* FIXME: if the number of chars written is not "len", something happened.
1823
* Check for errors appropriately...*/
1824
GIOStatus status = g_io_channel_write_chars(cfgfile, data, len, NULL, &gerror);
1825
if (status != G_IO_STATUS_NORMAL) {
1826
// FIXME: we should deal with temporary failures (G_IO_STATUS_AGAIN)
1827
fprintf(stderr, "%s\n", gerror->message);
1831
g_io_channel_close(cfgfile);
1833
g_key_file_free(sakura.cfg);
1835
free(sakura.configfile);
1843
sakura_set_size(gint columns, gint rows)
1845
struct terminal *term;
1846
GtkRequisition main_request;
1847
GtkRequisition term_request;
1850
gint char_width, char_height;
1852
term = sakura_get_page_term(sakura, 0);
1854
/* New values used to resize the window */
1855
//sakura.columns = columns;
1856
//sakura.rows = rows;
1858
vte_terminal_get_padding(VTE_TERMINAL(term->vte), (int *)&pad_x, (int *)&pad_y);
1859
char_width = vte_terminal_get_char_width(VTE_TERMINAL(term->vte));
1860
char_height = vte_terminal_get_char_height(VTE_TERMINAL(term->vte));
1862
hints.min_width = char_width + pad_x;
1863
hints.min_height = char_height + pad_y;
1864
hints.base_width = pad_x;
1865
hints.base_height = pad_y;
1866
hints.width_inc = char_width;
1867
hints.height_inc = char_height;
1868
gtk_window_set_geometry_hints (GTK_WINDOW (sakura.main_window),
1869
GTK_WIDGET (term->vte),
1871
GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
1873
gtk_widget_size_request (sakura.main_window, &main_request);
1874
gtk_widget_size_request (term->vte, &term_request);
1875
sakura.width = main_request.width - term_request.width;
1876
sakura.height = main_request.height - term_request.height;
1877
sakura.width += pad_x + char_width * sakura.columns;
1878
sakura.height += pad_y + char_height * sakura.rows;
1879
if (GTK_WIDGET_MAPPED (sakura.main_window)) {
1880
gtk_window_resize (GTK_WINDOW (sakura.main_window), sakura.width, sakura.height);
1881
SAY("Resizing to %ld columns %ld rows", sakura.columns, sakura.rows);
1883
gtk_window_set_default_size (GTK_WINDOW (sakura.main_window), sakura.width, sakura.height);
1892
struct terminal *term;
1895
n_pages=gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook));
1897
/* Set the font for all tabs */
1898
for (i = (n_pages - 1); i >= 0; i--) {
1899
term = sakura_get_page_term(sakura, i);
1900
vte_terminal_set_font(VTE_TERMINAL(term->vte), sakura.font);
1908
struct terminal *term;
1909
GtkWidget *tab_hbox;
1910
GtkWidget *close_btn;
1917
term = g_new0( struct terminal, 1 );
1918
term->hbox=gtk_hbox_new(FALSE, 0);
1919
term->vte=vte_terminal_new();
1921
/* Create label (and optional close button) for tabs */
1922
label_text=g_strdup_printf(_("Terminal %d"), sakura.label_count++);
1923
term->label=gtk_label_new(label_text);
1925
tab_hbox=gtk_hbox_new(FALSE,2);
1926
gtk_box_pack_start(GTK_BOX(tab_hbox), term->label, FALSE, FALSE, 0);
1927
if (sakura.show_closebutton) {
1928
close_btn=gtk_button_new();
1929
gtk_button_set_relief(GTK_BUTTON(close_btn), GTK_RELIEF_NONE);
1930
GtkWidget *image=gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
1931
gtk_container_add (GTK_CONTAINER (close_btn), image);
1932
/* FIXME: Use GtkWidget set-style signal to properly reflect the changes */
1933
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (close_btn), GTK_ICON_SIZE_MENU, &w, &h);
1934
gtk_widget_set_size_request(close_btn, w+2, h+2);
1935
gtk_box_pack_start(GTK_BOX(tab_hbox), close_btn, FALSE, FALSE, 0);
1937
gtk_widget_show_all(tab_hbox);
1940
vte_terminal_set_scrollback_lines(VTE_TERMINAL(term->vte), SCROLL_LINES);
1941
vte_terminal_match_add_gregex(VTE_TERMINAL(term->vte), sakura.http_regexp, 0);
1942
vte_terminal_set_mouse_autohide(VTE_TERMINAL(term->vte), TRUE);
1944
term->scrollbar=gtk_vscrollbar_new(vte_terminal_get_adjustment(VTE_TERMINAL(term->vte)));
1946
gtk_box_pack_start(GTK_BOX(term->hbox), term->vte, TRUE, TRUE, 0);
1947
gtk_box_pack_start(GTK_BOX(term->hbox), term->scrollbar, FALSE, FALSE, 0);
1949
/* Select the directory to use for the new tab */
1950
index = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
1952
struct terminal *prev_term;
1953
prev_term = sakura_get_page_term( sakura, index );
1954
cwd = sakura_get_term_cwd( prev_term );
1957
cwd = g_get_current_dir();
1959
/* Keep values when adding tabs */
1960
sakura.keep_fc=true;
1962
if ((index=gtk_notebook_append_page(GTK_NOTEBOOK(sakura.notebook), term->hbox, tab_hbox))==-1) {
1963
sakura_error("Cannot create a new tab");
1967
#if GTK_CHECK_VERSION( 2, 10, 0 )
1968
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(sakura.notebook), term->hbox, TRUE);
1969
// TODO: Set group id to support detached tabs
1971
gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(sakura.notebook), term->hbox, TRUE);
1975
sakura_set_page_term(sakura, index, term );
1978
g_signal_connect(G_OBJECT(term->vte), "increase-font-size", G_CALLBACK(sakura_increase_font), NULL);
1979
g_signal_connect(G_OBJECT(term->vte), "decrease-font-size", G_CALLBACK(sakura_decrease_font), NULL);
1980
g_signal_connect(G_OBJECT(term->vte), "child-exited", G_CALLBACK(sakura_child_exited), NULL);
1981
g_signal_connect(G_OBJECT(term->vte), "eof", G_CALLBACK(sakura_eof), NULL);
1982
g_signal_connect(G_OBJECT(term->vte), "window-title-changed", G_CALLBACK(sakura_title_changed), NULL);
1983
g_signal_connect_swapped(G_OBJECT(term->vte), "button-press-event", G_CALLBACK(sakura_button_press), sakura.menu);
1985
/* Notebook signals */
1986
g_signal_connect(G_OBJECT(sakura.notebook), "page-removed", G_CALLBACK(sakura_page_removed), NULL);
1987
if (sakura.show_closebutton) {
1988
g_signal_connect(G_OBJECT(close_btn), "clicked", G_CALLBACK(sakura_closebutton_clicked), term->hbox);
1992
if ( gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook)) == 1) {
1993
gchar *cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "show_always_first_tab", NULL);
1994
if (strcmp(cfgtmp, "Yes")==0) {
1995
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), TRUE);
1997
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), FALSE);
2001
gtk_notebook_set_show_border(GTK_NOTEBOOK(sakura.notebook), FALSE);
2003
gtk_widget_show_all(sakura.main_window);
2004
/* We need only to call set_size for the first tab */
2005
sakura_set_size(sakura.columns, sakura.rows);
2007
if (option_execute) {
2008
int command_argc; char **command_argv;
2012
if (!g_shell_parse_argv(option_execute, &command_argc, &command_argv, &gerror)) {
2013
sakura_error("Cannot parse command line arguments");
2017
/* Check if the command is valid */
2018
path=g_find_program_in_path(command_argv[0]);
2022
option_execute=NULL;
2024
term->pid=vte_terminal_fork_command(VTE_TERMINAL(term->vte), command_argv[0],
2025
command_argv, NULL, cwd, FALSE, FALSE, FALSE);
2026
g_strfreev(command_argv);
2027
option_execute=NULL;
2029
if (option_hold==TRUE) {
2030
sakura_error("Hold option given without any command");
2033
/* sakura.argv[0] cannot be used as a parameter, it's different for login shells */
2034
term->pid=vte_terminal_fork_command(VTE_TERMINAL(term->vte), g_getenv("SHELL"),
2035
sakura.argv, NULL, cwd, FALSE, FALSE, FALSE);
2037
/* Not the first tab */
2039
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), TRUE);
2041
gtk_widget_show_all(term->hbox);
2042
sakura_set_size(sakura.columns, sakura.rows);
2043
/* Call set_current page after showing the widget: gtk ignores this
2044
* function in the window is not visible *sigh*. Gtk documentation
2045
* says this is for "historical" reasons. Me arse */
2046
gtk_notebook_set_current_page(GTK_NOTEBOOK(sakura.notebook), index);
2047
term->pid=vte_terminal_fork_command(VTE_TERMINAL(term->vte), g_getenv("SHELL"),
2048
sakura.argv, NULL, cwd, FALSE, FALSE, FALSE);
2053
/* Recalculate sizes if the scrollbar is disabled */
2054
if (!sakura.show_scrollbar) {
2055
gtk_widget_hide(term->scrollbar);
2056
sakura_set_size(sakura.columns, sakura.rows);
2059
/* Configuration per-terminal */
2060
vte_terminal_set_backspace_binding(VTE_TERMINAL(term->vte), VTE_ERASE_ASCII_DELETE);
2061
vte_terminal_set_colors(VTE_TERMINAL(term->vte), &sakura.forecolor, &sakura.backcolor,
2062
sakura.palette, PALETTE_SIZE);
2064
if (sakura.fake_transparency) {
2065
vte_terminal_set_background_saturation(VTE_TERMINAL (term->vte), sakura.opacity_level);
2066
vte_terminal_set_background_transparent(VTE_TERMINAL (term->vte),TRUE);
2069
if (sakura.background) {
2070
sakura_set_bgimage(sakura.background);
2073
if (sakura.word_chars) {
2074
vte_terminal_set_word_chars( VTE_TERMINAL (term->vte), sakura.word_chars );
2077
/* Get rid of these nasty bells */
2078
vte_terminal_set_audible_bell (VTE_TERMINAL(term->vte), sakura.audible_bell ? TRUE : FALSE);
2079
vte_terminal_set_visible_bell (VTE_TERMINAL(term->vte), sakura.visible_bell ? TRUE : FALSE);
2081
/* Disable stupid blinking cursor */
2082
vte_terminal_set_cursor_blink_mode (VTE_TERMINAL(term->vte), sakura.blinking_cursor ? VTE_CURSOR_BLINK_ON : VTE_CURSOR_BLINK_OFF);
2085
/* Grrrr. Why the fucking label widget in the notebook STEAL the fucking focus? */
2086
gtk_widget_grab_focus(term->vte);
2088
/* FIXME: Possible race here. Find some way to force to process all configure
2089
* events before setting keep_fc again to false */
2090
sakura.keep_fc=false;
2094
/* Delete the notebook tab passed as a parameter */
2096
sakura_del_tab(gint page)
2098
struct terminal *term;
2099
term = sakura_get_page_term(sakura, page);
2101
/* Do the first tab checks BEFORE deleting the tab, to ensure correct
2102
* sizes are calculated when the tab is deleted */
2103
if ( gtk_notebook_get_n_pages(GTK_NOTEBOOK(sakura.notebook)) == 2) {
2104
char *cfgtmp = g_key_file_get_value(sakura.cfg, cfg_group, "show_always_first_tab", NULL);
2105
if (strcmp(cfgtmp, "Yes")==0) {
2106
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), TRUE);
2108
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(sakura.notebook), FALSE);
2111
sakura.keep_fc=true;
2114
gtk_widget_hide(term->hbox);
2115
gtk_notebook_remove_page(GTK_NOTEBOOK(sakura.notebook), page);
2122
/* TODO: Kill the forked child nicely */
2127
sakura_set_bgimage(char *infile)
2129
GError *gerror=NULL;
2130
GdkPixbuf *pixbuf=NULL;
2132
struct terminal *term;
2134
if (!infile) SAY("File parameter is NULL");
2136
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(sakura.notebook));
2137
term = sakura_get_page_term(sakura, page);
2139
/* Check file existence and type */
2140
if (g_file_test(infile, G_FILE_TEST_IS_REGULAR)) {
2142
pixbuf = gdk_pixbuf_new_from_file (infile, &gerror);
2144
sakura_error("Not using image file, %s\n", gerror->message);
2146
vte_terminal_set_background_image(VTE_TERMINAL(term->vte), pixbuf);
2147
vte_terminal_set_background_saturation(VTE_TERMINAL(term->vte), TRUE);
2148
vte_terminal_set_background_transparent(VTE_TERMINAL(term->vte),FALSE);
2150
g_key_file_set_value(sakura.cfg, cfg_group, "background", infile);
2157
sakura_key_file_set_key(GKeyFile *cfg,const gchar *cfg_group, const gchar *keyname,guint value)
2161
if((cfg==NULL)||(cfg_group==NULL)||(keyname==NULL)) {
2164
valname=gdk_keyval_name(value);
2165
g_key_file_set_string(cfg, cfg_group,keyname, valname);
2170
sakura_key_file_get_key(GKeyFile *cfg,const gchar *cfg_group,const gchar *keyname)
2173
guint retval=GDK_VoidSymbol;
2175
value=g_key_file_get_string(cfg, cfg_group, keyname, NULL);
2177
retval=gdk_keyval_from_name(value);
2181
/* For backwards compatibility with integer values */
2182
/* If gdk_keyval_from_name fail, it seems to be integer value*/
2183
if ((retval==GDK_VoidSymbol)||(retval==0)) {
2184
retval=g_key_file_get_integer(cfg, cfg_group, keyname, NULL);
2192
sakura_error(const char *format, ...)
2197
va_start(args, format);
2199
dialog = gtk_message_dialog_new(GTK_WINDOW(sakura.main_window), GTK_DIALOG_DESTROY_WITH_PARENT,
2200
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, format, args);
2201
gtk_dialog_run (GTK_DIALOG (dialog));
2204
gtk_widget_destroy (dialog);
2209
main(int argc, char **argv)
2211
struct terminal *term;
2214
GOptionContext *context;
2218
setlocale(LC_ALL, "");
2219
localedir=g_strdup_printf("%s/locale", DATADIR);
2220
textdomain(GETTEXT_PACKAGE);
2221
bindtextdomain(GETTEXT_PACKAGE, localedir);
2222
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2224
/* Options parsing */
2225
context = g_option_context_new (_("- vte-based terminal emulator"));
2226
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
2227
g_option_group_set_translation_domain(gtk_get_option_group(TRUE), GETTEXT_PACKAGE);
2228
g_option_context_add_group (context, gtk_get_option_group(TRUE));
2229
g_option_context_parse (context, &argc, &argv, &error);
2231
if (option_version) {
2232
fprintf(stderr, _("sakura version is %s\n"), VERSION);
2236
if (option_ntabs <= 0) {
2240
g_option_context_free(context);
2242
gtk_init(&argc, &argv);
2248
for (i=0; i<option_ntabs; i++)
2251
/* Fill Input Methods menu */
2252
term = sakura_get_page_term(sakura, 0);
2253
vte_terminal_im_append_menuitems(VTE_TERMINAL(term->vte), GTK_MENU_SHELL(sakura.im_menu));