mssh-window.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include <gconf/gconf-client.h>
  4. #include <gdk/gdkkeysyms.h>
  5. #include "mssh-terminal.h"
  6. #include "mssh-pref.h"
  7. #include "mssh-gconf.h"
  8. #include "mssh-window.h"
  9. #include "config.h"
  10. static void mssh_window_sendhost(GtkWidget *widget, gpointer data);
  11. static void mssh_window_destroy(GtkWidget *widget, gpointer data);
  12. static void mssh_window_pref(GtkWidget *widget, gpointer data);
  13. static gboolean mssh_window_key_press(GtkWidget *widget,
  14. GdkEventKey *event, gpointer data);
  15. static gboolean mssh_window_entry_focused(GtkWidget *widget,
  16. GtkDirectionType dir, gpointer data);
  17. static gboolean mssh_window_session_close(gpointer data);
  18. static void mssh_window_session_focused(MSSHTerminal *terminal,
  19. gpointer data);
  20. static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
  21. gint new_text_length, gint *position, gpointer data);
  22. static void mssh_window_add_session(MSSHWindow *window, char *hostname);
  23. static void mssh_window_init(MSSHWindow* window);
  24. static void mssh_window_class_init(MSSHWindowClass *klass);
  25. G_DEFINE_TYPE(MSSHWindow, mssh_window, GTK_TYPE_WINDOW)
  26. struct WinTermPair
  27. {
  28. MSSHWindow *window;
  29. MSSHTerminal *terminal;
  30. };
  31. GtkWidget* mssh_window_new(void)
  32. {
  33. return g_object_new(MSSH_TYPE_WINDOW, NULL);
  34. }
  35. static void mssh_window_sendhost(GtkWidget *widget, gpointer data)
  36. {
  37. int i;
  38. MSSHWindow *window = MSSH_WINDOW(data);
  39. for(i = 0; i < window->terminals->len; i++)
  40. {
  41. mssh_terminal_send_host(g_array_index(window->terminals,
  42. MSSHTerminal*, i));
  43. }
  44. }
  45. static void mssh_window_destroy(GtkWidget *widget, gpointer data)
  46. {
  47. gtk_main_quit();
  48. }
  49. static void mssh_window_pref(GtkWidget *widget, gpointer data)
  50. {
  51. MSSHWindow *window = MSSH_WINDOW(data);
  52. GtkWidget *pref = mssh_pref_new();
  53. gtk_window_set_transient_for(GTK_WINDOW(pref), GTK_WINDOW(window));
  54. gtk_window_set_modal(GTK_WINDOW(pref), TRUE);
  55. gtk_window_set_position(GTK_WINDOW(pref),
  56. GTK_WIN_POS_CENTER_ON_PARENT);
  57. gtk_widget_show_all(pref);
  58. }
  59. static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
  60. gint new_text_length, gint *position, gpointer data)
  61. {
  62. int i;
  63. MSSHWindow *window = MSSH_WINDOW(data);
  64. for(i = 0; i < window->terminals->len; i++)
  65. {
  66. mssh_terminal_send_string(g_array_index(window->terminals,
  67. MSSHTerminal*, i), new_text);
  68. }
  69. g_signal_stop_emission_by_name(G_OBJECT(widget), "insert-text");
  70. }
  71. static gboolean mssh_window_key_press(GtkWidget *widget,
  72. GdkEventKey *event, gpointer data)
  73. {
  74. int i;
  75. MSSHWindow *window = MSSH_WINDOW(data);
  76. for(i = 0; i < window->terminals->len; i++)
  77. {
  78. mssh_terminal_send_data(g_array_index(window->terminals,
  79. MSSHTerminal*, i), event);
  80. }
  81. return TRUE;
  82. }
  83. static gboolean mssh_window_entry_focused(GtkWidget *widget,
  84. GtkDirectionType dir, gpointer data)
  85. {
  86. MSSHWindow *window = MSSH_WINDOW(data);
  87. gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" - All");
  88. return FALSE;
  89. }
  90. gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
  91. guint keyval, GdkModifierType modifier, gpointer data)
  92. {
  93. MSSHWindow *window = MSSH_WINDOW(data);
  94. GtkWidget *focus;
  95. int i, idx = -1, len = window->terminals->len;
  96. int wcols = window->columns_override ? window->columns_override :
  97. window->columns;
  98. int cols = (len < wcols) ? len : wcols;
  99. focus = gtk_window_get_focus(GTK_WINDOW(window));
  100. for(i = 0; i < len; i++)
  101. {
  102. if(focus == GTK_WIDGET(g_array_index(window->terminals,
  103. MSSHTerminal*, i)))
  104. {
  105. idx = i;
  106. break;
  107. }
  108. }
  109. if(focus == window->global_entry && keyval == GDK_Down)
  110. idx = 0;
  111. else if(idx == -1)
  112. return FALSE;
  113. else
  114. {
  115. switch(keyval)
  116. {
  117. case GDK_Up:
  118. idx = idx - cols;
  119. break;
  120. case GDK_Down:
  121. if((idx + cols >= len) && (idx < len -
  122. (len % cols) ? (len % cols) : cols))
  123. idx = len - 1;
  124. else
  125. idx = idx + cols;
  126. break;
  127. case GDK_Left:
  128. if(idx % cols != 0)
  129. idx = idx - 1;
  130. break;
  131. case GDK_Right:
  132. if(idx % cols != cols - 1)
  133. idx = idx + 1;
  134. break;
  135. }
  136. }
  137. if(idx < 0)
  138. focus = window->global_entry;
  139. else if(idx < len)
  140. {
  141. focus = GTK_WIDGET(g_array_index(window->terminals,
  142. MSSHTerminal*, idx));
  143. }
  144. gtk_window_set_focus(GTK_WINDOW(window), focus);
  145. return TRUE;
  146. }
  147. static gboolean mssh_window_session_close(gpointer data)
  148. {
  149. int i, idx = -1;
  150. struct WinTermPair *data_pair = (struct WinTermPair*)data;
  151. for(i = 0; i < data_pair->window->terminals->len; i++)
  152. {
  153. if(data_pair->terminal == g_array_index(
  154. data_pair->window->terminals, MSSHTerminal*, i))
  155. {
  156. idx = i;
  157. break;
  158. }
  159. }
  160. if(idx == -1)
  161. {
  162. fprintf(stderr,
  163. "mssh: Fatal Error: Can't find terminal to remove!\n");
  164. }
  165. else
  166. {
  167. gtk_widget_destroy(data_pair->terminal->menu_item);
  168. gtk_container_remove(GTK_CONTAINER(data_pair->window->table),
  169. GTK_WIDGET(data_pair->terminal));
  170. g_array_remove_index(data_pair->window->terminals, idx);
  171. mssh_window_relayout(data_pair->window);
  172. }
  173. if(data_pair->window->terminals->len == 0 &&
  174. data_pair->window->exit_on_all_closed)
  175. {
  176. mssh_window_destroy(NULL, (void*)data_pair->window);
  177. }
  178. free(data_pair);
  179. return FALSE;
  180. }
  181. void mssh_window_session_closed(MSSHTerminal *terminal, gpointer data)
  182. {
  183. struct WinTermPair *data_pair = malloc(sizeof(struct WinTermPair));
  184. data_pair->terminal = terminal;
  185. data_pair->window = MSSH_WINDOW(data);
  186. if(data_pair->window->close_ended_sessions)
  187. {
  188. g_timeout_add_seconds(data_pair->window->timeout,
  189. mssh_window_session_close, data_pair);
  190. }
  191. }
  192. static void mssh_window_session_focused(MSSHTerminal *terminal,
  193. gpointer data)
  194. {
  195. char *title;
  196. size_t len;
  197. MSSHWindow *window = MSSH_WINDOW(data);
  198. len = strlen(PACKAGE_NAME" - ") + strlen(terminal->hostname) + 1;
  199. title = malloc(len);
  200. snprintf(title, len, PACKAGE_NAME" - %s", terminal->hostname);
  201. gtk_window_set_title(GTK_WINDOW(window), title);
  202. free(title);
  203. }
  204. void mssh_window_relayout(MSSHWindow *window)
  205. {
  206. GConfClient *client;
  207. GConfEntry *entry;
  208. GtkWidget *focus;
  209. int i, len = window->terminals->len;
  210. int wcols = window->columns_override ? window->columns_override :
  211. window->columns;
  212. int cols = (len < wcols) ? len : wcols;
  213. int rows = (len + 0.5) / cols;
  214. focus = gtk_window_get_focus(GTK_WINDOW(window));
  215. if(!focus)
  216. focus = window->global_entry;
  217. for(i = 0; i < len; i++)
  218. {
  219. MSSHTerminal *terminal = g_array_index(window->terminals,
  220. MSSHTerminal*, i);
  221. g_object_ref(terminal);
  222. if(GTK_WIDGET(terminal)->parent == GTK_WIDGET(window->table))
  223. {
  224. gtk_container_remove(GTK_CONTAINER(window->table),
  225. GTK_WIDGET(terminal));
  226. }
  227. gtk_table_attach(GTK_TABLE(window->table), GTK_WIDGET(terminal),
  228. (i % cols), (i == len - 1) ? cols : (i % cols) + 1, i / cols,
  229. (i / cols) + 1,
  230. GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);
  231. g_object_unref(terminal);
  232. if(!terminal->started)
  233. {
  234. mssh_terminal_start_session(terminal, window->env);
  235. terminal->started = 1;
  236. }
  237. }
  238. if(len > 0)
  239. {
  240. gtk_table_resize(GTK_TABLE(window->table), rows, cols);
  241. }
  242. client = gconf_client_get_default();
  243. gtk_widget_show_all(GTK_WIDGET(window));
  244. entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FONT, NULL,
  245. TRUE, NULL);
  246. mssh_gconf_notify_font(client, 0, entry, window);
  247. entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
  248. TRUE, NULL);
  249. mssh_gconf_notify_fg_colour(client, 0, entry, window);
  250. entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
  251. TRUE, NULL);
  252. mssh_gconf_notify_bg_colour(client, 0, entry, window);
  253. gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(focus));
  254. }
  255. static void mssh_window_add_session(MSSHWindow *window, char *hostname)
  256. {
  257. MSSHTerminal *terminal = MSSH_TERMINAL(mssh_terminal_new());
  258. g_array_append_val(window->terminals, terminal);
  259. g_signal_connect(G_OBJECT(terminal), "session-closed",
  260. G_CALLBACK(mssh_window_session_closed), window);
  261. g_signal_connect(G_OBJECT(terminal), "session-focused",
  262. G_CALLBACK(mssh_window_session_focused), window);
  263. mssh_terminal_init_session(terminal, hostname);
  264. gtk_menu_shell_append(GTK_MENU_SHELL(window->server_menu),
  265. terminal->menu_item);
  266. }
  267. static void mssh_window_init(MSSHWindow* window)
  268. {
  269. GConfClient *client;
  270. GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
  271. GtkWidget *entry = gtk_entry_new();
  272. GtkWidget *menu_bar = gtk_menu_bar_new();
  273. GtkWidget *file_menu = gtk_menu_new();
  274. GtkWidget *edit_menu = gtk_menu_new();
  275. GtkWidget *file_item = gtk_menu_item_new_with_label("File");
  276. GtkWidget *edit_item = gtk_menu_item_new_with_label("Edit");
  277. GtkWidget *server_item = gtk_menu_item_new_with_label("Servers");
  278. GtkWidget *file_quit = gtk_image_menu_item_new_from_stock(
  279. GTK_STOCK_QUIT, NULL);
  280. GtkWidget *file_sendhost = gtk_image_menu_item_new_with_label(
  281. "Send hostname");
  282. /* GtkWidget *file_add = gtk_image_menu_item_new_with_label(
  283. "Add session");*/
  284. GtkWidget *edit_pref = gtk_image_menu_item_new_from_stock(
  285. GTK_STOCK_PREFERENCES, NULL);
  286. GtkAccelGroup *accel = gtk_accel_group_new();
  287. window->server_menu = gtk_menu_new();
  288. window->global_entry = entry;
  289. window->terminals = g_array_new(FALSE, TRUE, sizeof(MSSHTerminal*));
  290. gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
  291. gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_menu);
  292. gtk_menu_item_set_submenu(GTK_MENU_ITEM(server_item),
  293. window->server_menu);
  294. /* gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_add);*/
  295. gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_sendhost);
  296. gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_quit);
  297. gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_pref);
  298. g_signal_connect(G_OBJECT(file_sendhost), "activate",
  299. G_CALLBACK(mssh_window_sendhost), window);
  300. g_signal_connect(G_OBJECT(file_quit), "activate",
  301. G_CALLBACK(mssh_window_destroy), window);
  302. g_signal_connect(G_OBJECT(edit_pref), "activate",
  303. G_CALLBACK(mssh_window_pref), window);
  304. gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), file_item);
  305. gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), edit_item);
  306. gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), server_item);
  307. g_signal_connect(G_OBJECT(entry), "key-press-event",
  308. G_CALLBACK(mssh_window_key_press), window);
  309. g_signal_connect(G_OBJECT(entry), "insert-text",
  310. G_CALLBACK(mssh_window_insert), window);
  311. g_signal_connect(G_OBJECT(entry), "focus-in-event",
  312. G_CALLBACK(mssh_window_entry_focused), window);
  313. gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, TRUE, 0);
  314. gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 2);
  315. window->table = gtk_table_new(1, 1, TRUE);
  316. gtk_box_pack_start(GTK_BOX(vbox), window->table, TRUE, TRUE, 0);
  317. gtk_container_add(GTK_CONTAINER(window), vbox);
  318. gtk_widget_set_size_request(GTK_WIDGET(window), 1024, 768);
  319. gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME);
  320. client = gconf_client_get_default();
  321. gconf_client_add_dir(client, MSSH_GCONF_PATH,
  322. GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
  323. gconf_client_notify_add(client, MSSH_GCONF_KEY_FONT,
  324. mssh_gconf_notify_font, window, NULL, NULL);
  325. gconf_client_notify_add(client, MSSH_GCONF_KEY_FG_COLOUR,
  326. mssh_gconf_notify_fg_colour, window, NULL, NULL);
  327. gconf_client_notify_add(client, MSSH_GCONF_KEY_BG_COLOUR,
  328. mssh_gconf_notify_bg_colour, window, NULL, NULL);
  329. gconf_client_notify_add(client, MSSH_GCONF_KEY_COLUMNS,
  330. mssh_gconf_notify_columns, window, NULL, NULL);
  331. gconf_client_notify_add(client, MSSH_GCONF_KEY_TIMEOUT,
  332. mssh_gconf_notify_timeout, window, NULL, NULL);
  333. gconf_client_notify_add(client, MSSH_GCONF_KEY_CLOSE_ENDED,
  334. mssh_gconf_notify_close_ended, window, NULL, NULL);
  335. gconf_client_notify_add(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED,
  336. mssh_gconf_notify_quit_all_ended, window, NULL, NULL);
  337. gconf_client_notify_add(client, MSSH_GCONF_KEY_MODIFIER,
  338. mssh_gconf_notify_modifier, window, NULL, NULL);
  339. gconf_client_notify(client, MSSH_GCONF_KEY_COLUMNS);
  340. gconf_client_notify(client, MSSH_GCONF_KEY_TIMEOUT);
  341. gconf_client_notify(client, MSSH_GCONF_KEY_CLOSE_ENDED);
  342. gconf_client_notify(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED);
  343. gconf_client_notify(client, MSSH_GCONF_KEY_MODIFIER);
  344. gtk_accel_group_connect(accel, GDK_Up, window->modifier,
  345. GTK_ACCEL_VISIBLE, g_cclosure_new(
  346. G_CALLBACK(mssh_window_focus), window, NULL));
  347. gtk_accel_group_connect(accel, GDK_Down, window->modifier,
  348. GTK_ACCEL_VISIBLE, g_cclosure_new(
  349. G_CALLBACK(mssh_window_focus), window, NULL));
  350. gtk_accel_group_connect(accel, GDK_Left, window->modifier,
  351. GTK_ACCEL_VISIBLE, g_cclosure_new(
  352. G_CALLBACK(mssh_window_focus), window, NULL));
  353. gtk_accel_group_connect(accel, GDK_Right, window->modifier,
  354. GTK_ACCEL_VISIBLE, g_cclosure_new(
  355. G_CALLBACK(mssh_window_focus), window, NULL));
  356. window->accel = accel;
  357. gtk_window_add_accel_group(GTK_WINDOW(window), accel);
  358. }
  359. void mssh_window_start_session(MSSHWindow* window, char **env,
  360. GArray *hosts, long cols)
  361. {
  362. int i, j, k;
  363. int nhosts = hosts->len;
  364. int rows = (nhosts / 2) + (nhosts % 2);
  365. window->env = env;
  366. window->columns_override = cols;
  367. for(i = 0; i < rows; i++)
  368. {
  369. for(j = 0; j < 2; j++)
  370. {
  371. k = j + i*2;
  372. if(k < nhosts)
  373. {
  374. mssh_window_add_session(window, g_array_index(hosts,
  375. char*, k));
  376. }
  377. }
  378. }
  379. mssh_window_relayout(window);
  380. }
  381. static void mssh_window_class_init(MSSHWindowClass *klass)
  382. {
  383. }