1 /*-
2 * Copyright (c) 2004-2008 os-cillation e.K.
3 *
4 * Written by Benedikt Meurer <benny@xfce.org>.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #ifdef HAVE_ERRNO_H
25 #include <errno.h>
26 #endif
27 #ifdef HAVE_LIMITS_H
28 #include <limits.h>
29 #endif
30 #include <stdlib.h>
31 #ifdef HAVE_MEMORY_H
32 #include <memory.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37
38 #include <libxfce4ui/libxfce4ui.h>
39
40 #ifdef GDK_WINDOWING_X11
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #endif
44
45 #include <terminal/terminal-app.h>
46 #include <terminal/terminal-config.h>
47 #include <terminal/terminal-preferences.h>
48 #include <terminal/terminal-private.h>
49 #include <terminal/terminal-window.h>
50 #include <terminal/terminal-window-dropdown.h>
51
52 #define ACCEL_MAP_PATH "xfce4/terminal/accels.scm"
53 #define TERMINAL_DESKTOP_FILE (DATADIR "/applications/xfce4-terminal.desktop")
54
55
56
57 static void terminal_app_finalize (GObject *object);
58 static void terminal_app_update_accels (TerminalApp *app);
59 static void terminal_app_update_tab_key_accels (gpointer data,
60 const gchar *accel_path,
61 guint accel_key,
62 GdkModifierType accel_mods,
63 gboolean changed);
64 static void terminal_app_update_windows_accels (gpointer user_data);
65 static gboolean terminal_app_accel_map_load (gpointer user_data);
66 static gboolean terminal_app_accel_map_save (gpointer user_data);
67 static gboolean terminal_app_unset_urgent_bell (TerminalWindow *window,
68 GdkEvent *event,
69 TerminalApp *app);
70 static void terminal_app_new_window (TerminalWindow *window,
71 const gchar *working_directory,
72 TerminalApp *app);
73 static void terminal_app_new_window_with_terminal (TerminalWindow *existing,
74 TerminalScreen *terminal,
75 gint x,
76 gint y,
77 TerminalApp *app);
78 static void terminal_app_window_destroyed (GtkWidget *window,
79 TerminalApp *app);
80 static void terminal_app_save_yourself (XfceSMClient *client,
81 TerminalApp *app);
82 static void terminal_app_open_window (TerminalApp *app,
83 TerminalWindowAttr *attr);
84
85
86
87 struct _TerminalAppClass
88 {
89 GObjectClass parent_class;
90 };
91
92 struct _TerminalApp
93 {
94 GObject parent_instance;
95 TerminalPreferences *preferences;
96 XfceSMClient *session_client;
97 gchar *initial_menu_bar_accel;
98 GSList *windows;
99
100 guint accel_map_load_id;
101 guint accel_map_save_id;
102 GtkAccelMap *accel_map;
103 GSList *tab_key_accels;
104 };
105
106
107
108 GQuark
terminal_error_quark(void)109 terminal_error_quark (void)
110 {
111 static GQuark quark = 0;
112 if (G_UNLIKELY (quark == 0))
113 quark = g_quark_from_static_string ("terminal-error-quark");
114 return quark;
115 }
116
117
118
G_DEFINE_TYPE(TerminalApp,terminal_app,G_TYPE_OBJECT)119 G_DEFINE_TYPE (TerminalApp, terminal_app, G_TYPE_OBJECT)
120
121
122
123 static void
124 terminal_app_class_init (TerminalAppClass *klass)
125 {
126 GObjectClass *gobject_class;
127
128 gobject_class = G_OBJECT_CLASS (klass);
129 gobject_class->finalize = terminal_app_finalize;
130 }
131
132
133
134 static void
terminal_app_init(TerminalApp * app)135 terminal_app_init (TerminalApp *app)
136 {
137 app->preferences = terminal_preferences_get ();
138 g_signal_connect_swapped (G_OBJECT (app->preferences), "notify::shortcuts-no-menukey",
139 G_CALLBACK (terminal_app_update_accels), app);
140 g_signal_connect_swapped (G_OBJECT (app->preferences), "notify::shortcuts-no-helpkey",
141 G_CALLBACK (terminal_app_update_accels), app);
142
143 /* remember the original menu bar accel */
144 g_object_get (G_OBJECT (gtk_settings_get_default ()),
145 "gtk-menu-bar-accel", &app->initial_menu_bar_accel,
146 NULL);
147
148 terminal_app_update_accels (app);
149
150 /* schedule accel map load and update windows when finished */
151 app->accel_map_load_id = gdk_threads_add_idle_full (G_PRIORITY_LOW, terminal_app_accel_map_load, app,
152 terminal_app_update_windows_accels);
153 }
154
155
156
157 static void
terminal_app_finalize(GObject * object)158 terminal_app_finalize (GObject *object)
159 {
160 TerminalApp *app = TERMINAL_APP (object);
161 GSList *lp;
162
163 /* stop accel map stuff */
164 if (G_UNLIKELY (app->accel_map_load_id != 0))
165 g_source_remove (app->accel_map_load_id);
166 if (app->accel_map != NULL)
167 g_object_unref (G_OBJECT (app->accel_map));
168 if (G_UNLIKELY (app->accel_map_save_id != 0))
169 {
170 g_source_remove (app->accel_map_save_id);
171 terminal_app_accel_map_save (app);
172 }
173
174 for (lp = app->windows; lp != NULL; lp = lp->next)
175 {
176 g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_window_destroyed), app);
177 g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_new_window), app);
178 g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_new_window_with_terminal), app);
179 g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_unset_urgent_bell), app);
180 gtk_widget_destroy (GTK_WIDGET (lp->data));
181 }
182 g_slist_free (app->windows);
183
184 g_signal_handlers_disconnect_by_func (G_OBJECT (app->preferences), G_CALLBACK (terminal_app_update_accels), app);
185 g_object_unref (G_OBJECT (app->preferences));
186
187 if (app->initial_menu_bar_accel != NULL)
188 g_free (app->initial_menu_bar_accel);
189
190 if (app->session_client != NULL)
191 g_object_unref (G_OBJECT (app->session_client));
192
193 for (lp = app->tab_key_accels; lp != NULL; lp = lp->next)
194 g_free (((TerminalAccel*) lp->data)->path);
195 g_slist_free_full (app->tab_key_accels, g_free);
196
197 (*G_OBJECT_CLASS (terminal_app_parent_class)->finalize) (object);
198 }
199
200
201
202 static void
terminal_app_update_accels(TerminalApp * app)203 terminal_app_update_accels (TerminalApp *app)
204 {
205 gboolean no_key;
206 GdkModifierType mod;
207 guint key;
208
209 g_object_get (G_OBJECT (app->preferences),
210 "shortcuts-no-menukey", &no_key,
211 NULL);
212 g_object_set (G_OBJECT (gtk_settings_get_default ()),
213 "gtk-menu-bar-accel",
214 no_key ? NULL : app->initial_menu_bar_accel,
215 NULL);
216 gtk_accelerator_parse (app->initial_menu_bar_accel, &key, &mod);
217 gtk_accel_map_change_entry ("<Actions>/terminal-window/toggle-menubar",
218 no_key ? 0 : key, no_key ? 0 : mod, TRUE);
219
220 g_object_get (G_OBJECT (app->preferences),
221 "shortcuts-no-helpkey", &no_key,
222 NULL);
223 gtk_accel_map_change_entry ("<Actions>/terminal-window/contents",
224 no_key ? 0 : GDK_KEY_F1, 0, TRUE);
225 }
226
227
228
229 static void
terminal_app_update_tab_key_accels(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)230 terminal_app_update_tab_key_accels (gpointer data,
231 const gchar *accel_path,
232 guint accel_key,
233 GdkModifierType accel_mods,
234 gboolean changed)
235 {
236 if (accel_key == GDK_KEY_Tab || accel_key == GDK_KEY_ISO_Left_Tab)
237 {
238 TerminalApp *app = TERMINAL_APP (data);
239 TerminalAccel *accel = g_new0 (TerminalAccel, 1);
240
241 accel->path = g_strdup (g_strrstr (accel_path, "/") + 1); // <Actions>/terminal-window/action
242 accel->mods = accel_mods;
243
244 app->tab_key_accels = g_slist_prepend (app->tab_key_accels, accel);
245 }
246 }
247
248
249
250 static void
terminal_app_update_windows_accels(gpointer user_data)251 terminal_app_update_windows_accels (gpointer user_data)
252 {
253 TerminalApp *app = TERMINAL_APP (user_data);
254 GSList *lp;
255
256 for (lp = app->windows; lp != NULL; lp = lp->next)
257 {
258 terminal_window_rebuild_tabs_menu (TERMINAL_WINDOW (lp->data));
259 terminal_window_update_tab_key_accels (TERMINAL_WINDOW (lp->data), app->tab_key_accels);
260 }
261
262 app->accel_map_load_id = 0;
263 }
264
265
266
267 static gboolean
terminal_app_accel_map_save(gpointer user_data)268 terminal_app_accel_map_save (gpointer user_data)
269 {
270 TerminalApp *app = TERMINAL_APP (user_data);
271 gchar *path;
272
273 app->accel_map_save_id = 0;
274
275 /* save the current accel map */
276 path = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH, TRUE);
277 if (G_LIKELY (path != NULL))
278 {
279 /* save the accel map */
280 gtk_accel_map_save (path);
281 g_free (path);
282 }
283
284 return FALSE;
285 }
286
287
288
289 static void
terminal_app_accel_map_changed(TerminalApp * app)290 terminal_app_accel_map_changed (TerminalApp *app)
291 {
292 /* stop pending save */
293 if (app->accel_map_save_id != 0)
294 {
295 g_source_remove (app->accel_map_save_id);
296 app->accel_map_save_id = 0;
297 }
298
299 /* schedule new save */
300 app->accel_map_save_id = gdk_threads_add_timeout_seconds (10, terminal_app_accel_map_save, app);
301 }
302
303
304
305 static gboolean
terminal_app_accel_map_load(gpointer user_data)306 terminal_app_accel_map_load (gpointer user_data)
307 {
308 TerminalApp *app = TERMINAL_APP (user_data);
309 gchar *path;
310 gchar name[50];
311 guint i;
312
313 path = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH);
314 if (G_LIKELY (path != NULL))
315 {
316 /* load the accel map */
317 gtk_accel_map_load (path);
318 g_free (path);
319 }
320
321 /* watch for changes */
322 app->accel_map = gtk_accel_map_get ();
323 g_signal_connect_swapped (G_OBJECT (app->accel_map), "changed",
324 G_CALLBACK (terminal_app_accel_map_changed), app);
325
326 /* check and create default Alt+N accelerators */
327 for (i = 1; i < 10; i++)
328 {
329 g_snprintf (name, sizeof (name), "<Actions>/terminal-window/goto-tab-%d", i);
330 if (!gtk_accel_map_lookup_entry (name, NULL))
331 gtk_accel_map_change_entry (name, GDK_KEY_0 + i, GDK_MOD1_MASK, FALSE);
332 }
333
334 /* identify accelerators containing the Tab key */
335 if (app->tab_key_accels != NULL)
336 {
337 GSList *lp;
338 for (lp = app->tab_key_accels; lp != NULL; lp = lp->next)
339 g_free (((TerminalAccel*) lp->data)->path);
340 g_slist_free_full (app->tab_key_accels, g_free);
341 app->tab_key_accels = NULL;
342 }
343 gtk_accel_map_foreach (app, terminal_app_update_tab_key_accels);
344
345 return FALSE;
346 }
347
348
349
350 static gboolean
terminal_app_unset_urgent_bell(TerminalWindow * window,GdkEvent * event,TerminalApp * app)351 terminal_app_unset_urgent_bell (TerminalWindow *window,
352 GdkEvent *event,
353 TerminalApp *app)
354 {
355 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (window));
356 gtk_window_set_urgency_hint (GTK_WINDOW (toplevel), FALSE);
357
358 return FALSE;
359 }
360
361
362
363 static void
terminal_app_take_window(TerminalApp * app,GtkWindow * window)364 terminal_app_take_window (TerminalApp *app,
365 GtkWindow *window)
366 {
367 GtkWindowGroup *group;
368
369 terminal_return_if_fail (GTK_IS_WINDOW (window));
370
371 group = gtk_window_group_new ();
372 gtk_window_group_add_window (group, window);
373 g_object_unref (group);
374
375 g_signal_connect (G_OBJECT (window), "destroy",
376 G_CALLBACK (terminal_app_window_destroyed), app);
377 g_signal_connect (G_OBJECT (window), "new-window",
378 G_CALLBACK (terminal_app_new_window), app);
379 g_signal_connect (G_OBJECT (window), "new-window-with-screen",
380 G_CALLBACK (terminal_app_new_window_with_terminal), app);
381 g_signal_connect (G_OBJECT (window), "focus-in-event",
382 G_CALLBACK (terminal_app_unset_urgent_bell), app);
383 g_signal_connect (G_OBJECT (window), "key-release-event",
384 G_CALLBACK (terminal_app_unset_urgent_bell), app);
385 app->windows = g_slist_prepend (app->windows, window);
386
387 terminal_window_update_tab_key_accels (TERMINAL_WINDOW (window), app->tab_key_accels);
388 }
389
390
391
392 static GtkWidget*
terminal_app_create_window(TerminalApp * app,const gchar * role,gboolean fullscreen,TerminalVisibility menubar,TerminalVisibility borders,TerminalVisibility toolbar)393 terminal_app_create_window (TerminalApp *app,
394 const gchar *role,
395 gboolean fullscreen,
396 TerminalVisibility menubar,
397 TerminalVisibility borders,
398 TerminalVisibility toolbar)
399 {
400 GtkWidget *window;
401 gchar *new_role = NULL;
402
403 if (role == NULL)
404 {
405 /* create a new window role */
406 new_role = g_strdup_printf ("%s-%u-%u", PACKAGE_NAME, (guint) time (NULL), g_random_int ());
407 role = new_role;
408 }
409
410 window = terminal_window_new (role, fullscreen, menubar, borders, toolbar);
411 g_free (new_role);
412
413 terminal_app_take_window (app, GTK_WINDOW (window));
414
415 return window;
416 }
417
418
419
420 static GtkWidget*
terminal_app_create_drop_down(TerminalApp * app,const gchar * role,const gchar * icon,gboolean fullscreen,TerminalVisibility menubar,TerminalVisibility toolbar)421 terminal_app_create_drop_down (TerminalApp *app,
422 const gchar *role,
423 const gchar *icon,
424 gboolean fullscreen,
425 TerminalVisibility menubar,
426 TerminalVisibility toolbar)
427 {
428 GtkWidget *window;
429
430 window = terminal_window_dropdown_new (role, icon, fullscreen, menubar, toolbar);
431
432 terminal_app_take_window (app, GTK_WINDOW (window));
433
434 return window;
435 }
436
437
438
439 static void
terminal_app_new_window(TerminalWindow * window,const gchar * working_directory,TerminalApp * app)440 terminal_app_new_window (TerminalWindow *window,
441 const gchar *working_directory,
442 TerminalApp *app)
443 {
444 TerminalWindowAttr *win_attr;
445 TerminalTabAttr *tab_attr;
446 TerminalScreen *terminal;
447 GdkScreen *screen;
448 gboolean inherit_geometry;
449 glong w, h;
450
451 screen = gtk_window_get_screen (GTK_WINDOW (window));
452
453 win_attr = terminal_window_attr_new ();
454 win_attr->display = g_strdup (gdk_display_get_name (gdk_screen_get_display (screen)));
455 tab_attr = win_attr->tabs->data;
456 tab_attr->directory = g_strdup (working_directory);
457 if (terminal_window_get_font (window))
458 win_attr->font = g_strdup (terminal_window_get_font (window));
459 win_attr->zoom = terminal_window_get_zoom_level (window);
460
461 /* check if we should try to inherit the parent geometry */
462 g_object_get (G_OBJECT (app->preferences), "misc-inherit-geometry", &inherit_geometry, NULL);
463 if (G_UNLIKELY (inherit_geometry))
464 {
465 /* determine the currently active terminal screen for the window */
466 terminal = terminal_window_get_active (window);
467 if (G_LIKELY (terminal != NULL))
468 {
469 /* let the new window inherit the geometry from its parent */
470 g_free (win_attr->geometry);
471 terminal_screen_get_size (terminal, &w, &h);
472 win_attr->geometry = g_strdup_printf ("%ldx%ld", w, h);
473 }
474 }
475
476 terminal_app_open_window (app, win_attr);
477
478 terminal_window_attr_free (win_attr);
479 }
480
481
482
483 static void
terminal_app_new_window_with_terminal(TerminalWindow * existing,TerminalScreen * terminal,gint x,gint y,TerminalApp * app)484 terminal_app_new_window_with_terminal (TerminalWindow *existing,
485 TerminalScreen *terminal,
486 gint x,
487 gint y,
488 TerminalApp *app)
489 {
490 GtkWidget *window;
491 GdkScreen *screen;
492 glong width, height;
493
494 terminal_return_if_fail (TERMINAL_IS_WINDOW (existing));
495 terminal_return_if_fail (TERMINAL_IS_SCREEN (terminal));
496 terminal_return_if_fail (TERMINAL_IS_APP (app));
497
498 window = terminal_app_create_window (app, NULL, FALSE,
499 TERMINAL_VISIBILITY_DEFAULT,
500 TERMINAL_VISIBILITY_DEFAULT,
501 TERMINAL_VISIBILITY_DEFAULT);
502
503 /* set new window position */
504 if (x > -1 && y > -1)
505 gtk_window_move (GTK_WINDOW (window), x, y);
506
507 /* place the new window on the same screen as
508 * the existing window.
509 */
510 screen = gtk_window_get_screen (GTK_WINDOW (existing));
511 if (G_LIKELY (screen != NULL))
512 gtk_window_set_screen (GTK_WINDOW (window), screen);
513
514 /* this is required to get the geometry right
515 * later in the terminal_window_add() call.
516 */
517 gtk_widget_hide (GTK_WIDGET (terminal));
518
519 terminal_window_add (TERMINAL_WINDOW (window), terminal);
520
521 if (G_UNLIKELY (terminal_window_is_drop_down (existing)))
522 {
523 /* resize new window to the default geometry */
524 #ifdef GDK_WINDOWING_X11
525 gchar *geo;
526 guint w, h;
527 g_object_get (G_OBJECT (app->preferences), "misc-default-geometry", &geo, NULL);
528 if (G_LIKELY (geo != NULL))
529 {
530 XParseGeometry (geo, NULL, NULL, &w, &h);
531 g_free (geo);
532 terminal_screen_force_resize_window (terminal, GTK_WINDOW (window), w, h);
533 }
534 #endif
535 }
536 else
537 {
538 /* resize new window to the original terminal geometry */
539 terminal_screen_get_size (terminal, &width, &height);
540 terminal_screen_force_resize_window (terminal, GTK_WINDOW (window), width, height);
541 }
542
543 gtk_widget_show (window);
544 }
545
546
547
548 static void
terminal_app_window_destroyed(GtkWidget * window,TerminalApp * app)549 terminal_app_window_destroyed (GtkWidget *window,
550 TerminalApp *app)
551 {
552 terminal_return_if_fail (g_slist_find (app->windows, window) != NULL);
553
554 app->windows = g_slist_remove (app->windows, window);
555
556 if (G_UNLIKELY (app->windows == NULL))
557 gtk_main_quit ();
558 }
559
560
561
562 static void
terminal_app_save_yourself(XfceSMClient * client,TerminalApp * app)563 terminal_app_save_yourself (XfceSMClient *client,
564 TerminalApp *app)
565 {
566 GSList *result = NULL;
567 GSList *lp;
568 const gchar * const *oargv;
569 gchar **argv;
570 gint argc;
571 gint n;
572
573 for (lp = app->windows, n = 0; lp != NULL; lp = lp->next)
574 {
575 /* don't session save dropdown windows */
576 if (TERMINAL_IS_WINDOW_DROPDOWN (lp->data))
577 continue;
578
579 if (n++ != 0)
580 result = g_slist_append (result, g_strdup ("--window"));
581 result = g_slist_concat (result, terminal_window_get_restart_command (lp->data));
582 }
583
584 /* no windows were saved - this can happen if there is only a dropdown window
585 that we don't want to save */
586 if (result == NULL)
587 return;
588
589 argc = g_slist_length (result) + 1;
590 argv = g_new (gchar*, argc + 1);
591 for (lp = result, n = 1; n < argc && lp != NULL; lp = lp->next, ++n)
592 argv[n] = lp->data;
593 argv[n] = NULL;
594
595 oargv = xfce_sm_client_get_restart_command (client);
596 if (oargv != NULL)
597 {
598 terminal_assert (oargv[0] != NULL);
599 argv[0] = g_strdup (oargv[0]);
600 }
601 else
602 {
603 argv[0] = g_strdup (PACKAGE_NAME);
604 }
605
606 xfce_sm_client_set_restart_command (client, argv);
607
608 g_slist_free (result);
609 g_strfreev (argv);
610 }
611
612
613
614 static GdkDisplay *
terminal_app_find_display(const gchar * display_name,gint * screen_num)615 terminal_app_find_display (const gchar *display_name,
616 gint *screen_num)
617 {
618 const gchar *other_name;
619 GdkDisplay *display = NULL;
620 GSList *displays;
621 GSList *dp;
622 gulong n;
623 gchar *period;
624 gchar *name;
625 gchar *end;
626 gint num = -1;
627
628 if (display_name != NULL)
629 {
630 name = g_strdup (display_name);
631
632 /* extract screen number from display name */
633 period = strrchr (name, '.');
634 if (period != NULL)
635 {
636 errno = 0;
637 *period++ = '\0';
638 end = period;
639 n = strtoul (period, &end, 0);
640 if (errno == 0 && period != end)
641 num = n;
642 }
643
644 displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
645 for (dp = displays; dp != NULL; dp = dp->next)
646 {
647 other_name = gdk_display_get_name (dp->data);
648 if (strncmp (other_name, name, strlen (name)) == 0)
649 {
650 display = dp->data;
651 break;
652 }
653 }
654 g_slist_free (displays);
655
656 g_free (name);
657
658 if (display == NULL)
659 display = gdk_display_open (display_name);
660 }
661
662 if (display == NULL)
663 display = gdk_display_get_default ();
664
665 if (screen_num != NULL)
666 *screen_num = num;
667
668 return display;
669 }
670
671
672
673 static GdkScreen *
terminal_app_find_screen(GdkDisplay * display,gint screen_num)674 terminal_app_find_screen (GdkDisplay *display,
675 gint screen_num)
676 {
677 GdkScreen *screen = NULL;
678
679 if (display != NULL)
680 {
681 if (screen == NULL)
682 screen = gdk_display_get_default_screen (display);
683
684 if (screen != NULL)
685 g_object_ref (G_OBJECT (screen));
686 }
687
688 if (screen == NULL)
689 {
690 screen = gdk_screen_get_default ();
691 g_object_ref (G_OBJECT (screen));
692 }
693
694 return screen;
695 }
696
697
698
699 static GdkScreen *
terminal_app_find_screen_by_name(const gchar * display_name)700 terminal_app_find_screen_by_name (const gchar *display_name)
701 {
702 GdkDisplay *display;
703 gint screen_num;
704
705 display = terminal_app_find_display (display_name, &screen_num);
706 return terminal_app_find_screen (display, screen_num);
707 }
708
709
710
711 static void
terminal_app_open_window(TerminalApp * app,TerminalWindowAttr * attr)712 terminal_app_open_window (TerminalApp *app,
713 TerminalWindowAttr *attr)
714 {
715 GtkWidget *window;
716 TerminalScreen *terminal;
717 GdkScreen *screen;
718 gchar *geometry;
719 GSList *lp;
720 gboolean reuse_window = FALSE;
721 GdkDisplay *attr_display;
722 gint attr_screen_num;
723 gint active_tab = -1, i;
724 #ifdef GDK_WINDOWING_X11
725 GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;
726 gint mask = NoValue, x, y, new_x, new_y;
727 guint width = 1, height = 1, new_width, new_height;
728 gint screen_width = 0, screen_height = 0;
729 gint window_width, window_height;
730 #endif
731
732 terminal_return_if_fail (TERMINAL_IS_APP (app));
733 terminal_return_if_fail (attr != NULL);
734
735 if (attr->drop_down)
736 {
737 /* look for an exising drop-down window */
738 attr_display = terminal_app_find_display (attr->display, &attr_screen_num);
739 for (lp = app->windows; lp != NULL; lp = lp->next)
740 {
741 if (TERMINAL_IS_WINDOW_DROPDOWN (lp->data))
742 {
743 /* check if the screen of the display matches (bug #9957) */
744 screen = gtk_window_get_screen (GTK_WINDOW (lp->data));
745 if (gdk_screen_get_display (screen) == attr_display)
746 break;
747 }
748 }
749
750 if (lp != NULL)
751 {
752 if (G_UNLIKELY (attr->reuse_last_window))
753 {
754 /* use the drop-down window to insert the tab */
755 window = lp->data;
756 reuse_window = TRUE;
757 }
758 else
759 {
760 /* toggle state of visible window */
761 terminal_window_dropdown_toggle (lp->data, attr->startup_id, FALSE);
762 return;
763 }
764 }
765 else
766 {
767 /* create new drop-down window */
768 window = terminal_app_create_drop_down (app,
769 attr->role,
770 attr->icon,
771 attr->fullscreen,
772 attr->menubar,
773 attr->toolbar);
774
775 /* drop-down window can be maximized */
776 if (attr->maximize)
777 gtk_window_maximize (GTK_WINDOW (window));
778
779 /* put it on the correct screen/display */
780 screen = terminal_app_find_screen (attr_display, attr_screen_num);
781 gtk_window_set_screen (GTK_WINDOW (window), screen);
782 }
783 }
784 else if (attr->reuse_last_window && app->windows != NULL)
785 {
786 /* open tabs in the existing window */
787 window = app->windows->data;
788 /* try to find active window (bug #13891) */
789 for (lp = app->windows; lp != NULL; lp = lp->next)
790 {
791 if (gtk_window_has_toplevel_focus (GTK_WINDOW (lp->data)))
792 {
793 window = lp->data;
794 break;
795 }
796 }
797 reuse_window = TRUE;
798 }
799 else
800 {
801 /* create new window */
802 window = terminal_app_create_window (app,
803 attr->role,
804 attr->fullscreen,
805 attr->menubar,
806 attr->borders,
807 attr->toolbar);
808
809 /* apply normal window properties */
810 if (attr->maximize)
811 gtk_window_maximize (GTK_WINDOW (window));
812 if (attr->minimize)
813 gtk_window_iconify (GTK_WINDOW (window));
814
815 if (attr->startup_id != NULL)
816 gtk_window_set_startup_id (GTK_WINDOW (window), attr->startup_id);
817
818 if (attr->icon != NULL)
819 {
820 if (g_path_is_absolute (attr->icon))
821 gtk_window_set_icon_from_file (GTK_WINDOW (window), attr->icon, NULL);
822 else
823 gtk_window_set_icon_name (GTK_WINDOW (window), attr->icon);
824 }
825
826 screen = terminal_app_find_screen_by_name (attr->display);
827 if (G_LIKELY (screen != NULL))
828 {
829 gtk_window_set_screen (GTK_WINDOW (window), screen);
830 g_object_unref (G_OBJECT (screen));
831 }
832 }
833
834 terminal_window_set_scrollbar_visibility (TERMINAL_WINDOW (window), attr->scrollbar);
835
836 /* font and zoom for new window */
837 if (!reuse_window)
838 {
839 if (attr->font)
840 terminal_window_set_font (TERMINAL_WINDOW (window), attr->font);
841 terminal_window_set_zoom_level (TERMINAL_WINDOW (window), attr->zoom);
842 }
843
844 if (!attr->drop_down)
845 {
846 /* try to apply the geometry to the window */
847 g_object_get (G_OBJECT (app->preferences), "misc-default-geometry", &geometry, NULL);
848 #ifdef GDK_WINDOWING_X11
849 /* defaults */
850 mask = XParseGeometry (geometry, &x, &y, &width, &height);
851
852 /* geometry provided via command line parameter */
853 if (G_UNLIKELY (attr->geometry != NULL))
854 {
855 g_free (geometry);
856 geometry = g_strdup (attr->geometry);
857 mask = XParseGeometry (geometry, &new_x, &new_y, &new_width, &new_height);
858
859 if (mask & WidthValue)
860 width = new_width;
861 if (mask & HeightValue)
862 height = new_height;
863 if (mask & XValue)
864 x = new_x;
865 if (mask & YValue)
866 y = new_y;
867 }
868 #endif
869 }
870
871 /* add the tabs */
872 for (lp = attr->tabs, i = 0; lp != NULL; lp = lp->next, ++i)
873 {
874 TerminalTabAttr *tab_attr = (TerminalTabAttr *) lp->data;
875 terminal = terminal_screen_new (tab_attr, width, height);
876 terminal_window_add (TERMINAL_WINDOW (window), terminal);
877 terminal_screen_launch_child (terminal);
878
879 /* whether the tab was set as active */
880 if (G_UNLIKELY (tab_attr->active))
881 active_tab = i;
882 }
883
884 /* set active tab */
885 if (active_tab > -1)
886 {
887 GtkNotebook *notebook = GTK_NOTEBOOK (terminal_window_get_notebook (TERMINAL_WINDOW (window)));
888 gtk_notebook_set_current_page (notebook, active_tab);
889 }
890
891 if (!attr->drop_down)
892 {
893 /* move the window to desired position */
894 #ifdef GDK_WINDOWING_X11
895 if ((mask & XValue) || (mask & YValue))
896 {
897 screen = gtk_window_get_screen (GTK_WINDOW (window));
898 gdk_window_get_geometry (gdk_screen_get_root_window (screen), NULL, NULL,
899 &screen_width, &screen_height);
900 gtk_window_get_size (GTK_WINDOW (window), &window_width, &window_height);
901 if (mask & XNegative)
902 {
903 x = screen_width - window_width + x;
904 gravity = GDK_GRAVITY_NORTH_EAST;
905 }
906 if (mask & YNegative)
907 {
908 y = screen_height - window_height + y;
909 gravity = (mask & XNegative) ? GDK_GRAVITY_SOUTH_EAST : GDK_GRAVITY_SOUTH_WEST;
910 }
911 gtk_window_set_gravity (GTK_WINDOW (window), gravity);
912 gtk_window_move (GTK_WINDOW (window), x, y);
913 }
914 else if (!(mask & WidthValue) && !(mask & XValue))
915 #else
916 if (!gtk_window_parse_geometry (GTK_WINDOW (window), geometry))
917 #endif
918 g_printerr (_("Invalid geometry string \"%s\"\n"), geometry);
919
920 /* cleanup */
921 g_free (geometry);
922 }
923
924 /* show the window */
925 if (attr->drop_down)
926 terminal_window_dropdown_toggle (TERMINAL_WINDOW_DROPDOWN (window), attr->startup_id, reuse_window);
927 else
928 {
929 /* save window geometry to prevent overriding */
930 terminal_window_set_grid_size (TERMINAL_WINDOW (window), width, height);
931 terminal_screen_force_resize_window (terminal_window_get_active (TERMINAL_WINDOW (window)),
932 GTK_WINDOW (window), width, height);
933
934 if (reuse_window)
935 gtk_window_present (GTK_WINDOW (window));
936 else
937 gtk_widget_show (window);
938 }
939 }
940
941
942
943 /**
944 * terminal_app_process:
945 * @app
946 * @argv
947 * @argc
948 * @error
949 *
950 * Return value:
951 **/
952 gboolean
terminal_app_process(TerminalApp * app,gchar ** argv,gint argc,GError ** error)953 terminal_app_process (TerminalApp *app,
954 gchar **argv,
955 gint argc,
956 GError **error)
957 {
958 GSList *attrs, *lp;
959 gchar *sm_client_id = NULL;
960 TerminalWindowAttr *attr;
961 GError *err = NULL;
962
963 attrs = terminal_window_attr_parse (argc, argv, app->windows != NULL, error);
964 if (G_UNLIKELY (attrs == NULL))
965 return FALSE;
966
967 /* Connect to session manager first before starting any other windows */
968 for (lp = attrs; lp != NULL; lp = lp->next)
969 {
970 attr = lp->data;
971
972 /* take first sm client id */
973 if (attr->sm_client_id != NULL)
974 {
975 sm_client_id = g_strdup (attr->sm_client_id);
976 break;
977 }
978 }
979
980 if (G_LIKELY (app->session_client == NULL))
981 {
982 app->session_client = xfce_sm_client_get_full (XFCE_SM_CLIENT_RESTART_NORMAL,
983 XFCE_SM_CLIENT_PRIORITY_DEFAULT,
984 sm_client_id,
985 xfce_get_homedir (),
986 NULL,
987 PACKAGE_NAME ".desktop");
988 if (xfce_sm_client_connect (app->session_client, &err))
989 {
990 xfce_sm_client_set_desktop_file (app->session_client, TERMINAL_DESKTOP_FILE);
991 g_signal_connect (G_OBJECT (app->session_client), "save-state",
992 G_CALLBACK (terminal_app_save_yourself), app);
993 g_signal_connect (G_OBJECT (app->session_client), "quit",
994 G_CALLBACK (gtk_main_quit), NULL);
995 }
996 else
997 {
998 g_printerr (_("Failed to connect to session manager: %s\n"), err->message);
999 g_error_free (err);
1000 }
1001 }
1002
1003 for (lp = attrs; lp != NULL; lp = lp->next)
1004 {
1005 attr = lp->data;
1006
1007 terminal_app_open_window (app, attr);
1008 terminal_window_attr_free (attr);
1009 }
1010
1011 g_slist_free (attrs);
1012 g_free (sm_client_id);
1013
1014 return TRUE;
1015 }
1016