mssh-window.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <gdk/gdkkeysyms.h>
  5. #include <gtk/gtk.h>
  6. #define GETTEXT_PACKAGE "mssh"
  7. #include <glib/gi18n-lib.h>
  8. #include "mssh-terminal.h"
  9. #include "mssh-pref.h"
  10. #include "mssh-gconf.h"
  11. #include "mssh-window.h"
  12. #include "config.h"
  13. #include <regex.h>
  14. static void mssh_window_sendhost(GtkWidget *widget, gpointer data);
  15. static void mssh_window_destroy(GtkWidget *widget, gpointer data);
  16. static void mssh_window_pref(GtkWidget *widget, gpointer data);
  17. static gboolean mssh_window_key_press(GtkWidget *widget,
  18. GdkEventKey *event, gpointer data);
  19. static gboolean mssh_window_entry_focused(GtkWidget *widget,
  20. GtkDirectionType dir, gpointer data);
  21. static gboolean mssh_window_session_close(gpointer data);
  22. static void mssh_window_session_focused(MSSHTerminal *terminal,
  23. gpointer data);
  24. static gboolean mssh_window_mouse_paste_cb(MSSHTerminal *terminal,
  25. gpointer data);
  26. static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
  27. gint new_text_length, gint *position, gpointer data);
  28. static void mssh_window_add_session(MSSHWindow *window, char *hostname);
  29. static void mssh_window_init(MSSHWindow* window);
  30. static void mssh_window_class_init(MSSHWindowClass *klass);
  31. static void mssh_window_add(GtkWidget *widget, gpointer data);
  32. gboolean mssh_window_dialog_emit_response(GtkWidget *widget, GObject *acceleratable,
  33. guint keyval, GdkModifierType modifier, gpointer data);
  34. static void mssh_window_maximize(GtkWidget *widget, gpointer data);
  35. static void mssh_window_restore_layout(GtkWidget *widget, gpointer data);
  36. void mssh_window_relayout_for_one(MSSHWindow *window, GtkWidget *t);
  37. G_DEFINE_TYPE(MSSHWindow, mssh_window, GTK_TYPE_WINDOW)
  38. struct WinTermPair
  39. {
  40. MSSHWindow *window;
  41. MSSHTerminal *terminal;
  42. };
  43. GtkWidget* mssh_window_new(void)
  44. {
  45. return g_object_new(MSSH_TYPE_WINDOW, NULL);
  46. }
  47. static void mssh_window_sendhost(GtkWidget *widget, gpointer data)
  48. {
  49. int i;
  50. MSSHWindow *window = MSSH_WINDOW(data);
  51. for(i = 0; i < window->terminals->len; i++)
  52. {
  53. mssh_terminal_send_host(g_array_index(window->terminals,
  54. MSSHTerminal*, i));
  55. }
  56. }
  57. static void mssh_window_sendcommand(GtkWidget *widget, gpointer data)
  58. {
  59. int i;
  60. char *command;
  61. MSSHWindow *window = MSSH_WINDOW(data);
  62. GtkMenuItem *item = (GtkMenuItem *)widget;
  63. command = g_datalist_get_data(MSSH_WINDOW(data)->commands, gtk_menu_item_get_label (item));
  64. for(i = 0; i < window->terminals->len; i++)
  65. {
  66. mssh_terminal_send_string(g_array_index(window->terminals,
  67. MSSHTerminal*, i), command);
  68. }
  69. }
  70. static void mssh_window_destroy(GtkWidget *widget, gpointer data)
  71. {
  72. gtk_main_quit();
  73. }
  74. static void mssh_window_pref(GtkWidget *widget, gpointer data)
  75. {
  76. MSSHWindow *window = MSSH_WINDOW(data);
  77. GtkWidget *pref = mssh_pref_new(window->settings);
  78. gtk_window_set_transient_for(GTK_WINDOW(pref), GTK_WINDOW(window));
  79. gtk_window_set_modal(GTK_WINDOW(pref), TRUE);
  80. gtk_window_set_position(GTK_WINDOW(pref),
  81. GTK_WIN_POS_CENTER_ON_PARENT);
  82. gtk_widget_show_all(pref);
  83. }
  84. static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
  85. gint new_text_length, gint *position, gpointer data)
  86. {
  87. int i;
  88. MSSHWindow *window = MSSH_WINDOW(data);
  89. for(i = 0; i < window->terminals->len; i++)
  90. {
  91. mssh_terminal_send_string(g_array_index(window->terminals,
  92. MSSHTerminal*, i), new_text);
  93. }
  94. g_signal_stop_emission_by_name(G_OBJECT(widget), "insert-text");
  95. }
  96. static gboolean mssh_window_key_press(GtkWidget *widget,
  97. GdkEventKey *event, gpointer data)
  98. {
  99. int i;
  100. MSSHWindow *window = MSSH_WINDOW(data);
  101. for(i = 0; i < window->terminals->len; i++)
  102. {
  103. mssh_terminal_send_data(g_array_index(window->terminals,
  104. MSSHTerminal*, i), event);
  105. }
  106. return TRUE;
  107. }
  108. static gboolean mssh_window_entry_focused(GtkWidget *widget,
  109. GtkDirectionType dir, gpointer data)
  110. {
  111. MSSHWindow *window = MSSH_WINDOW(data);
  112. gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" - All");
  113. window->last_focus = NULL;
  114. /* clear the coloring for the focused window */
  115. mssh_gconf_notify_fg_colour(window->settings, MSSH_GCONF_KEY_FG_COLOUR,
  116. window);
  117. mssh_gconf_notify_bg_colour(window->settings, MSSH_GCONF_KEY_BG_COLOUR,
  118. window);
  119. return FALSE;
  120. }
  121. gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
  122. guint keyval, GdkModifierType modifier, gpointer data)
  123. {
  124. MSSHWindow *window = MSSH_WINDOW(data);
  125. GtkWidget *focus;
  126. int i, idx = -1, len = window->terminals->len;
  127. int wcols = window->columns_override ? window->columns_override :
  128. window->columns;
  129. int cols = (len < wcols) ? len : wcols;
  130. focus = gtk_window_get_focus(GTK_WINDOW(window));
  131. for(i = 0; i < len; i++)
  132. {
  133. if(focus == GTK_WIDGET(g_array_index(window->terminals,
  134. MSSHTerminal*, i)))
  135. {
  136. idx = i;
  137. break;
  138. }
  139. }
  140. /* recolor the windows */
  141. if (window->recolor_focused) {
  142. mssh_gconf_notify_fg_colour(window->settings,
  143. MSSH_GCONF_KEY_FG_COLOUR, window);
  144. mssh_gconf_notify_bg_colour(window->settings,
  145. MSSH_GCONF_KEY_BG_COLOUR, window);
  146. }
  147. if(focus == window->global_entry && keyval == GDK_KEY_Down &&
  148. window->dir_focus)
  149. idx = 0;
  150. else if(idx == -1 && window->dir_focus)
  151. return TRUE;
  152. else
  153. {
  154. switch(keyval)
  155. {
  156. case GDK_KEY_Up:
  157. if(window->dir_focus)
  158. idx = idx - cols;
  159. break;
  160. case GDK_KEY_Down:
  161. if(window->dir_focus)
  162. {
  163. if((idx + cols >= len) && (idx < len -
  164. (len % cols) ? (len % cols) : cols))
  165. idx = len - 1;
  166. else
  167. idx = idx + cols;
  168. }
  169. break;
  170. case GDK_KEY_Left:
  171. if(idx % cols != 0 || !window->dir_focus)
  172. idx = idx - 1;
  173. break;
  174. case GDK_KEY_Right:
  175. if(idx % cols != cols - 1 || !window->dir_focus)
  176. idx = idx + 1;
  177. break;
  178. }
  179. }
  180. if(idx >= len && !window->dir_focus)
  181. focus = window->global_entry;
  182. if(idx < -1 && !window->dir_focus)
  183. idx = len - 1;
  184. if(idx < 0)
  185. focus = window->global_entry;
  186. else if(idx < len)
  187. {
  188. focus = GTK_WIDGET(g_array_index(window->terminals,
  189. MSSHTerminal*, idx));
  190. }
  191. gtk_window_set_focus(GTK_WINDOW(window), focus);
  192. return TRUE;
  193. }
  194. static gboolean mssh_window_session_close(gpointer data)
  195. {
  196. int i, idx = -1;
  197. struct WinTermPair *data_pair = (struct WinTermPair*)data;
  198. for(i = 0; i < data_pair->window->terminals->len; i++)
  199. {
  200. if(data_pair->terminal == g_array_index(
  201. data_pair->window->terminals, MSSHTerminal*, i))
  202. {
  203. idx = i;
  204. break;
  205. }
  206. }
  207. data_pair->window->last_closed = idx;
  208. if(idx == -1)
  209. {
  210. fprintf(stderr,
  211. _("mssh: Fatal Error: Can't find terminal to remove!\n"));
  212. }
  213. else
  214. {
  215. /* set the focus on the entry only if the terminal closed has it */
  216. if ( gtk_window_get_focus(GTK_WINDOW(data_pair->window)) == GTK_WIDGET(data_pair->terminal) ) {
  217. gtk_window_set_focus(GTK_WINDOW(data_pair->window), GTK_WIDGET(data_pair->window->global_entry));
  218. }
  219. gtk_widget_destroy(data_pair->terminal->menu_item);
  220. gtk_container_remove(GTK_CONTAINER(data_pair->window->grid),
  221. GTK_WIDGET(data_pair->terminal));
  222. g_array_remove_index(data_pair->window->terminals, idx);
  223. mssh_window_relayout(data_pair->window);
  224. }
  225. if(data_pair->window->terminals->len == 0 &&
  226. data_pair->window->exit_on_all_closed)
  227. {
  228. mssh_window_destroy(NULL, (void*)data_pair->window);
  229. }
  230. free(data_pair);
  231. return FALSE;
  232. }
  233. void mssh_window_session_closed(MSSHTerminal *terminal, gpointer data)
  234. {
  235. struct WinTermPair *data_pair = malloc(sizeof(struct WinTermPair));
  236. data_pair->terminal = terminal;
  237. data_pair->window = MSSH_WINDOW(data);
  238. if(data_pair->window->close_ended_sessions)
  239. {
  240. g_timeout_add_seconds(data_pair->window->timeout,
  241. mssh_window_session_close, data_pair);
  242. }
  243. }
  244. static gboolean mssh_window_mouse_paste_cb(MSSHTerminal *terminal,
  245. gpointer data)
  246. {
  247. gtk_widget_grab_focus(GTK_WIDGET(terminal));
  248. return FALSE;
  249. }
  250. static void mssh_window_session_focused(MSSHTerminal *terminal,
  251. gpointer data)
  252. {
  253. char *title;
  254. size_t len;
  255. MSSHWindow *window = MSSH_WINDOW(data);
  256. len = strlen(PACKAGE_NAME" - ") + strlen(terminal->hostname) + 1;
  257. title = malloc(len);
  258. snprintf(title, len, PACKAGE_NAME" - %s", terminal->hostname);
  259. gtk_window_set_title(GTK_WINDOW(window), title);
  260. free(title);
  261. /* recolor all windows */
  262. mssh_gconf_notify_fg_colour(window->settings, MSSH_GCONF_KEY_FG_COLOUR,
  263. window);
  264. mssh_gconf_notify_bg_colour(window->settings, MSSH_GCONF_KEY_BG_COLOUR,
  265. window);
  266. /* recolor the focused window - if needed */
  267. if (window->recolor_focused && window->is_maximized == 0) {
  268. mssh_gconf_notify_fg_colour_focus(window->settings,
  269. MSSH_GCONF_KEY_FG_COLOUR_FOCUS,
  270. window);
  271. mssh_gconf_notify_bg_colour_focus(window->settings,
  272. MSSH_GCONF_KEY_BG_COLOUR_FOCUS,
  273. window);
  274. }
  275. }
  276. void mssh_window_relayout(MSSHWindow *window)
  277. {
  278. GtkWidget *focus;
  279. int i, len = window->terminals->len;
  280. int wcols = window->columns_override ? window->columns_override :
  281. window->columns;
  282. int cols = (len < wcols) ? len : wcols;
  283. int width = 1;
  284. focus = gtk_window_get_focus(GTK_WINDOW(window));
  285. if(!focus)
  286. {
  287. if(window->last_closed < 0)
  288. window->last_closed = 0;
  289. if(len == 0)
  290. focus = window->global_entry;
  291. else if(window->last_closed < len)
  292. {
  293. focus = GTK_WIDGET(g_array_index(window->terminals,
  294. MSSHTerminal*, window->last_closed));
  295. }
  296. else
  297. {
  298. focus = GTK_WIDGET(g_array_index(window->terminals,
  299. MSSHTerminal*, 0));
  300. }
  301. }
  302. for(i = 0; i < len; i++)
  303. {
  304. MSSHTerminal *terminal = g_array_index(window->terminals,
  305. MSSHTerminal*, i);
  306. g_object_ref(terminal);
  307. if(gtk_widget_get_parent(GTK_WIDGET(terminal)) == GTK_WIDGET(window->grid))
  308. {
  309. gtk_container_remove(GTK_CONTAINER(window->grid),
  310. GTK_WIDGET(terminal));
  311. }
  312. /* Set margins to terminal widget */
  313. gtk_widget_set_margin_start(GTK_WIDGET(terminal), 1);
  314. gtk_widget_set_margin_end(GTK_WIDGET(terminal), 1);
  315. gtk_widget_set_margin_top(GTK_WIDGET(terminal), 1);
  316. gtk_widget_set_margin_bottom(GTK_WIDGET(terminal), 1);
  317. if (i == len - 1) {
  318. width = cols - (i % cols);
  319. }
  320. gtk_grid_attach(GTK_GRID(window->grid), /* grid */
  321. GTK_WIDGET(terminal), /* child */
  322. (i % cols), /* left */
  323. i / cols, /* top */
  324. width, /* width */
  325. 1); /* height */
  326. g_object_unref(terminal);
  327. if(!terminal->started)
  328. {
  329. mssh_terminal_start_session(terminal, window->env);
  330. terminal->started = 1;
  331. }
  332. }
  333. gtk_widget_show_all(GTK_WIDGET(window));
  334. mssh_gconf_notify_font(window->settings, MSSH_GCONF_KEY_FONT, window);
  335. mssh_gconf_notify_fg_colour(window->settings, MSSH_GCONF_KEY_FG_COLOUR,
  336. window);
  337. mssh_gconf_notify_bg_colour(window->settings, MSSH_GCONF_KEY_BG_COLOUR,
  338. window);
  339. gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(focus));
  340. }
  341. static void mssh_window_add_session(MSSHWindow *window, char *hostname)
  342. {
  343. MSSHTerminal *terminal = MSSH_TERMINAL(mssh_terminal_new());
  344. terminal->backscroll_buffer_size = window->backscroll_buffer_size;
  345. g_array_append_val(window->terminals, terminal);
  346. g_signal_connect(G_OBJECT(terminal), "session-closed",
  347. G_CALLBACK(mssh_window_session_closed), window);
  348. g_signal_connect(G_OBJECT(terminal), "session-focused",
  349. G_CALLBACK(mssh_window_session_focused), window);
  350. g_signal_connect(GTK_WIDGET(terminal), "button-release-event",
  351. G_CALLBACK(mssh_window_mouse_paste_cb), window);
  352. mssh_terminal_init_session(terminal, hostname);
  353. gtk_menu_shell_append(GTK_MENU_SHELL(window->server_menu),
  354. terminal->menu_item);
  355. }
  356. static void mssh_window_init(MSSHWindow* window)
  357. {
  358. GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  359. GtkWidget *entry = gtk_entry_new();
  360. GtkWidget *menu_bar = gtk_menu_bar_new();
  361. GtkWidget *file_menu = gtk_menu_new();
  362. GtkWidget *edit_menu = gtk_menu_new();
  363. GtkWidget *file_item = gtk_menu_item_new_with_label(_("File"));
  364. GtkWidget *edit_item = gtk_menu_item_new_with_label(_("Edit"));
  365. GtkWidget *server_item = gtk_menu_item_new_with_label(_("Servers"));
  366. GtkWidget *command_item = gtk_menu_item_new_with_label(_("Commands"));
  367. GtkWidget *file_quit = gtk_menu_item_new_with_mnemonic(_("_Quit"));
  368. GtkWidget *file_sendhost = gtk_menu_item_new_with_label(_("Send hostname"));
  369. GtkWidget *file_add = gtk_menu_item_new_with_label(_("Add session"));
  370. GtkWidget *edit_pref = gtk_menu_item_new_with_mnemonic(_("_Edit"));
  371. GtkAccelGroup *accel = gtk_accel_group_new();
  372. window->accel = NULL;
  373. window->server_menu = gtk_menu_new();
  374. window->command_menu = gtk_menu_new();
  375. window->global_entry = entry;
  376. window->last_closed = -1;
  377. window->terminals = g_array_new(FALSE, TRUE, sizeof(MSSHTerminal*));
  378. window->backscroll_buffer_size = 5000;
  379. window->is_maximized = 0;
  380. window->recolor_focused = FALSE;
  381. gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
  382. gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_menu);
  383. gtk_menu_item_set_submenu(GTK_MENU_ITEM(server_item),
  384. window->server_menu);
  385. gtk_menu_item_set_submenu(GTK_MENU_ITEM(command_item),
  386. window->command_menu);
  387. gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_add);
  388. gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_sendhost);
  389. gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_quit);
  390. gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_pref);
  391. g_signal_connect(G_OBJECT(file_sendhost), "activate",
  392. G_CALLBACK(mssh_window_sendhost), window);
  393. g_signal_connect(G_OBJECT(file_add), "activate",
  394. G_CALLBACK(mssh_window_add), window);
  395. g_signal_connect(G_OBJECT(file_quit), "activate",
  396. G_CALLBACK(mssh_window_destroy), window);
  397. g_signal_connect(G_OBJECT(edit_pref), "activate",
  398. G_CALLBACK(mssh_window_pref), window);
  399. gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file_item);
  400. gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), edit_item);
  401. gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), server_item);
  402. gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), command_item);
  403. g_signal_connect(G_OBJECT(entry), "key-press-event",
  404. G_CALLBACK(mssh_window_key_press), window);
  405. g_signal_connect(G_OBJECT(entry), "insert-text",
  406. G_CALLBACK(mssh_window_insert), window);
  407. g_signal_connect(G_OBJECT(entry), "focus-in-event",
  408. G_CALLBACK(mssh_window_entry_focused), window);
  409. gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, TRUE, 0);
  410. gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 2);
  411. window->grid = gtk_grid_new();
  412. gtk_grid_set_row_homogeneous(GTK_GRID(window->grid), TRUE);
  413. gtk_grid_set_column_homogeneous(GTK_GRID(window->grid), TRUE);
  414. gtk_box_pack_start(GTK_BOX(vbox), window->grid, TRUE, TRUE, 0);
  415. gtk_container_add(GTK_CONTAINER(window), vbox);
  416. gtk_widget_set_size_request(GTK_WIDGET(window), 0, 0);
  417. gtk_window_set_default_size(GTK_WINDOW(window), 1024, 768);
  418. gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME);
  419. window->settings = g_settings_new("es.hgarcia.mssh");
  420. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_FONT,
  421. G_CALLBACK(mssh_gconf_notify_font), window);
  422. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_FG_COLOUR,
  423. G_CALLBACK(mssh_gconf_notify_fg_colour), window);
  424. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_BG_COLOUR,
  425. G_CALLBACK(mssh_gconf_notify_bg_colour), window);
  426. g_signal_connect(window->settings,
  427. "changed::"MSSH_GCONF_KEY_FG_COLOUR_FOCUS,
  428. G_CALLBACK(mssh_gconf_notify_fg_colour_focus), window);
  429. g_signal_connect(window->settings,
  430. "changed::"MSSH_GCONF_KEY_BG_COLOUR_FOCUS,
  431. G_CALLBACK(mssh_gconf_notify_bg_colour_focus), window);
  432. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_COLUMNS,
  433. G_CALLBACK(mssh_gconf_notify_columns), window);
  434. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_TIMEOUT,
  435. G_CALLBACK(mssh_gconf_notify_timeout), window);
  436. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_CLOSE_ENDED,
  437. G_CALLBACK(mssh_gconf_notify_close_ended), window);
  438. g_signal_connect(window->settings,
  439. "changed::"MSSH_GCONF_KEY_RECOLOR_FOCUSED,
  440. G_CALLBACK(mssh_gconf_notify_recolor_focused), window);
  441. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_QUIT_ALL_ENDED,
  442. G_CALLBACK(mssh_gconf_notify_quit_all_ended), window);
  443. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_DIR_FOCUS,
  444. G_CALLBACK(mssh_gconf_notify_dir_focus), window);
  445. g_signal_connect(window->settings, "changed::"MSSH_GCONF_KEY_MODIFIER,
  446. G_CALLBACK(mssh_gconf_notify_modifier), window);
  447. g_signal_connect(window->settings,
  448. "changed::"MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE,
  449. G_CALLBACK(mssh_gconf_backscroll_buffer_size), window);
  450. mssh_gconf_notify_columns(window->settings, MSSH_GCONF_KEY_COLUMNS, window);
  451. mssh_gconf_notify_timeout(window->settings, MSSH_GCONF_KEY_TIMEOUT, window);
  452. mssh_gconf_notify_close_ended(window->settings, MSSH_GCONF_KEY_CLOSE_ENDED,
  453. window);
  454. mssh_gconf_notify_recolor_focused(window->settings,
  455. MSSH_GCONF_KEY_RECOLOR_FOCUSED, window);
  456. mssh_gconf_notify_quit_all_ended(window->settings,
  457. MSSH_GCONF_KEY_QUIT_ALL_ENDED, window);
  458. mssh_gconf_notify_dir_focus(window->settings, MSSH_GCONF_KEY_DIR_FOCUS,
  459. window);
  460. mssh_gconf_notify_modifier(window->settings, MSSH_GCONF_KEY_MODIFIER,
  461. window);
  462. mssh_gconf_backscroll_buffer_size(window->settings,
  463. MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE,
  464. window);
  465. gtk_accel_group_connect(accel, GDK_KEY_Up, window->modifier,
  466. GTK_ACCEL_VISIBLE, g_cclosure_new(
  467. G_CALLBACK(mssh_window_focus), window, NULL));
  468. gtk_accel_group_connect(accel, GDK_KEY_Down, window->modifier,
  469. GTK_ACCEL_VISIBLE, g_cclosure_new(
  470. G_CALLBACK(mssh_window_focus), window, NULL));
  471. gtk_accel_group_connect(accel, GDK_KEY_Left, window->modifier,
  472. GTK_ACCEL_VISIBLE, g_cclosure_new(
  473. G_CALLBACK(mssh_window_focus), window, NULL));
  474. gtk_accel_group_connect(accel, GDK_KEY_Right, window->modifier,
  475. GTK_ACCEL_VISIBLE, g_cclosure_new(
  476. G_CALLBACK(mssh_window_focus), window, NULL));
  477. /* bind Ctrl + Shift + x to toggling maximize terminal */
  478. gtk_accel_group_connect(accel, GDK_KEY_x, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
  479. GTK_ACCEL_VISIBLE, g_cclosure_new(
  480. G_CALLBACK(mssh_window_toggle_maximize), window, NULL));
  481. /* bind Ctrl + Shift + N to show the dialog for adding new sessions */
  482. gtk_accel_group_connect(accel, GDK_KEY_n, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
  483. GTK_ACCEL_VISIBLE, g_cclosure_new(
  484. G_CALLBACK(mssh_window_add), window, NULL));
  485. window->accel = accel;
  486. gtk_window_add_accel_group(GTK_WINDOW(window), accel);
  487. }
  488. void mssh_window_start_session(MSSHWindow* window, char **env,
  489. GArray *hosts, long cols)
  490. {
  491. int i, j, k;
  492. int nhosts = hosts->len;
  493. int rows = (nhosts / 2) + (nhosts % 2);
  494. window->env = env;
  495. window->columns_override = cols;
  496. for(i = 0; i < rows; i++)
  497. {
  498. for(j = 0; j < 2; j++)
  499. {
  500. k = j + i*2;
  501. if(k < nhosts)
  502. {
  503. mssh_window_add_session(window, g_array_index(hosts,
  504. char*, k));
  505. }
  506. }
  507. }
  508. mssh_window_relayout(window);
  509. }
  510. void mssh_window_add_command(GQuark key_id, gpointer data, gpointer user_data)
  511. {
  512. GtkWidget *menu_item;
  513. GtkWidget* window = (GtkWidget *)user_data;
  514. menu_item = gtk_menu_item_new_with_label(g_quark_to_string (key_id));
  515. gtk_menu_shell_append(GTK_MENU_SHELL(MSSH_WINDOW(window)->command_menu), menu_item);
  516. g_signal_connect(G_OBJECT(menu_item), "activate",
  517. G_CALLBACK(mssh_window_sendcommand), window);
  518. }
  519. static void mssh_window_class_init(MSSHWindowClass *klass)
  520. {
  521. }
  522. void mssh_window_relayout_for_one(MSSHWindow *window, GtkWidget *t)
  523. {
  524. int len = window->terminals->len;
  525. int wcols = window->columns_override ? window->columns_override :
  526. window->columns;
  527. int cols = (len < wcols) ? len : wcols;
  528. int rows = (len + 1) / cols;
  529. /* get the terminal widget */
  530. GtkWidget *terminal = GTK_WIDGET(t);
  531. g_object_ref(terminal);
  532. /* remove the widget from the container temporarily */
  533. gtk_container_remove(GTK_CONTAINER(window->grid), GTK_WIDGET(terminal));
  534. /* add it back again, now resized */
  535. gtk_grid_attach(GTK_GRID(window->grid), GTK_WIDGET(terminal), 0, 0, cols, rows);
  536. /* make the terminal focused */
  537. gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(terminal));
  538. /* remove the coloring */
  539. if (window->recolor_focused) {
  540. mssh_gconf_notify_fg_colour(window->settings, MSSH_GCONF_KEY_FG_COLOUR,
  541. window);
  542. mssh_gconf_notify_bg_colour(window->settings, MSSH_GCONF_KEY_BG_COLOUR,
  543. window);
  544. }
  545. g_object_unref(terminal);
  546. }
  547. gboolean mssh_window_toggle_maximize(GtkWidget *widget, GObject *acceleratable,
  548. guint keyval, GdkModifierType modifier, gpointer data)
  549. {
  550. MSSHWindow *window = MSSH_WINDOW(data);
  551. if (window->is_maximized) {
  552. /* toggle restore */
  553. mssh_window_restore_layout(widget, data);
  554. } else {
  555. /* toggle maximize */
  556. mssh_window_maximize(widget, data);
  557. }
  558. return TRUE;
  559. }
  560. static void mssh_window_maximize(GtkWidget *widget, gpointer data)
  561. {
  562. /* find the id of the currently focused window (if any) */
  563. MSSHWindow *window = MSSH_WINDOW(data);
  564. int i;
  565. int idx = -1;
  566. int len = window->terminals->len;
  567. /* get the currently focused window */
  568. GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(window));
  569. /* save the currently focused window so we can restore it later */
  570. window->last_focus = focus;
  571. /* find the focused window in the terminal list */
  572. for(i = 0; i < len; i++)
  573. {
  574. if(focus == GTK_WIDGET(g_array_index(window->terminals,
  575. MSSHTerminal*, i)))
  576. {
  577. idx = i;
  578. break;
  579. }
  580. }
  581. /* recolor the window with the normal color */
  582. if (window->recolor_focused) {
  583. mssh_gconf_notify_fg_colour(window->settings, MSSH_GCONF_KEY_FG_COLOUR,
  584. window);
  585. mssh_gconf_notify_bg_colour(window->settings, MSSH_GCONF_KEY_BG_COLOUR,
  586. window);
  587. }
  588. if (idx == -1) {
  589. /* there's no window focused, do nothing */
  590. } else {
  591. /* call relayout, it will reposition the widget to occupy the whole table */
  592. mssh_window_relayout_for_one(window, GTK_WIDGET(g_array_index(window->terminals,
  593. MSSHTerminal*, idx)));
  594. window->is_maximized = 1;
  595. }
  596. }
  597. static void mssh_window_restore_layout(GtkWidget *widget, gpointer data)
  598. {
  599. /* get the window */
  600. MSSHWindow *window = MSSH_WINDOW(data);
  601. /* just call relayout */
  602. mssh_window_relayout(window);
  603. window->is_maximized = 0;
  604. /* restore the focus */
  605. if (window->last_focus != NULL) {
  606. gtk_window_set_focus(GTK_WINDOW(window), window->last_focus);
  607. }
  608. /* recolor the focused window - if needed */
  609. if (window->recolor_focused && window->is_maximized == 0) {
  610. mssh_gconf_notify_fg_colour_focus(window->settings,
  611. MSSH_GCONF_KEY_FG_COLOUR_FOCUS,
  612. window);
  613. mssh_gconf_notify_bg_colour_focus(window->settings,
  614. MSSH_GCONF_KEY_BG_COLOUR_FOCUS,
  615. window);
  616. }
  617. }
  618. /* show a popup window for adding new sessions */
  619. static void mssh_window_add(GtkWidget *widget, gpointer data)
  620. {
  621. MSSHWindow *window = MSSH_WINDOW(data);
  622. GtkWidget *dialog, *label, *content_area, *button_add;
  623. GtkWidget *new_session_entry;
  624. gint result;
  625. /* create new dialog */
  626. dialog = gtk_dialog_new();
  627. /* get the content area that will be packed */
  628. content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  629. /* label for text */
  630. label = gtk_label_new (_("Add new session with hostname: "));
  631. /* Add the label and entry, and show everything we've added to the dialog */
  632. new_session_entry = gtk_entry_new();
  633. gtk_entry_set_max_length (GTK_ENTRY(new_session_entry), 255);
  634. /* pack the widgets */
  635. gtk_container_add (GTK_CONTAINER (content_area), label);
  636. gtk_container_add (GTK_CONTAINER (content_area), new_session_entry);
  637. /* add two buttons */
  638. button_add = gtk_dialog_add_button(GTK_DIALOG(dialog), _("Add"), GTK_RESPONSE_ACCEPT);
  639. gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL);
  640. /* make the add button the default */
  641. gtk_widget_grab_default(button_add);
  642. /* set dialog properties (modal, etc) */
  643. gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  644. gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
  645. gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
  646. /* set it's title */
  647. gtk_window_set_title(GTK_WINDOW(dialog), _("Add new session"));
  648. /* catch the activate signal (hitting enter) */
  649. g_signal_connect(G_OBJECT(new_session_entry), "activate",
  650. G_CALLBACK(mssh_window_dialog_emit_response), window);
  651. /* show the dialog and it's widgets */
  652. gtk_widget_show_all (dialog);
  653. /* wait for input */
  654. result = gtk_dialog_run (GTK_DIALOG (dialog));
  655. switch (result)
  656. {
  657. case GTK_RESPONSE_ACCEPT:
  658. mssh_window_add_session(window, (gchar*) gtk_entry_get_text(GTK_ENTRY(new_session_entry)));
  659. /* relayout */
  660. mssh_window_relayout(window);
  661. break;
  662. default:
  663. /* do nothing */
  664. break;
  665. }
  666. gtk_widget_destroy (dialog);
  667. }
  668. /* catch the 'activate' signal of the entry (return has been pushed) */
  669. /* emit the response for accept, simulating a mouse click on the add button */
  670. gboolean mssh_window_dialog_emit_response(GtkWidget *widget, GObject *acceleratable,
  671. guint keyval, GdkModifierType modifier, gpointer data)
  672. {
  673. /* get the dialog by getting the parent of the parent for the emitting (entry) widget */
  674. GtkWidget *vbox = gtk_widget_get_parent(widget);
  675. GtkWidget *dialog = gtk_widget_get_parent(vbox);
  676. /* emit the response signal simulating the clicking of 'ok' */
  677. gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
  678. return TRUE;
  679. }