1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008,2009,2010,2011,2012 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the project_list class
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "world_tree.h"
29 #include "gtk_builder.h"
30 #include "../common/catdup.h"
53 GtkWidget
* g_world_tree_widget
;
54 GtkTreeStore
* g_treestore
;
56 bool get_app_view(GtkTreeIter
* app_iter_ptr
, graph_view_handle
* view_ptr
)
58 GtkTreeIter view_iter
;
61 if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(g_treestore
), &view_iter
, app_iter_ptr
))
68 GTK_TREE_MODEL(g_treestore
),
73 if (type
!= entry_type_view
)
85 GtkTreeSelection
* UNUSED(selection
),
88 gboolean path_currently_selected
,
89 gpointer
UNUSED(data
))
92 graph_view_handle view
;
96 if (gtk_tree_model_get_iter(model
, &iter
, path
))
98 gtk_tree_model_get(model
, &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, COL_ID
, &id
, -1);
102 if (!get_app_view(&iter
, &view
))
107 case entry_type_view
:
108 //log_info("%s is going to be %s.", get_view_name(view), path_currently_selected ? "unselected" : "selected");
109 if (!path_currently_selected
)
120 bool get_selected_app_id(graph_view_handle
* view_ptr
, uint64_t * id_ptr
)
122 GtkTreeSelection
* selection
;
123 GtkTreeIter app_iter
;
126 graph_view_handle view
;
128 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
129 if (!gtk_tree_selection_get_selected(selection
, NULL
, &app_iter
))
135 GTK_TREE_MODEL(g_treestore
),
140 if (type
!= entry_type_app
)
145 if (!get_app_view(&app_iter
, &view
))
157 void on_popup_menu_action_app_start(GtkWidget
* UNUSED(menuitem
), gpointer
UNUSED(userdata
))
160 ladish_app_supervisor_proxy_handle proxy
;
161 graph_view_handle view
;
163 if (!get_selected_app_id(&view
, &id
))
168 log_info("start app %"PRIu64
, id
);
170 proxy
= graph_view_get_app_supervisor(view
);
171 ladish_app_supervisor_proxy_start_app(proxy
, id
);
174 void on_popup_menu_action_app_stop(GtkWidget
* UNUSED(menuitem
), gpointer
UNUSED(userdata
))
177 ladish_app_supervisor_proxy_handle proxy
;
178 graph_view_handle view
;
180 if (!get_selected_app_id(&view
, &id
))
185 log_info("stop app %"PRIu64
, id
);
187 proxy
= graph_view_get_app_supervisor(view
);
188 ladish_app_supervisor_proxy_stop_app(proxy
, id
);
191 void on_popup_menu_action_app_kill(GtkWidget
* UNUSED(menuitem
), gpointer
UNUSED(userdata
))
194 ladish_app_supervisor_proxy_handle proxy
;
195 graph_view_handle view
;
197 if (!get_selected_app_id(&view
, &id
))
202 log_info("kill app %"PRIu64
, id
);
204 proxy
= graph_view_get_app_supervisor(view
);
205 ladish_app_supervisor_proxy_kill_app(proxy
, id
);
208 void on_popup_menu_action_app_remove(GtkWidget
* UNUSED(menuitem
), gpointer
UNUSED(userdata
))
211 ladish_app_supervisor_proxy_handle proxy
;
212 graph_view_handle view
;
214 if (!get_selected_app_id(&view
, &id
))
219 log_info("remove app %"PRIu64
, id
);
221 proxy
= graph_view_get_app_supervisor(view
);
222 ladish_app_supervisor_proxy_remove_app(proxy
, id
);
225 void on_popup_menu_action_app_properties(GtkWidget
* UNUSED(menuitem
), gpointer
UNUSED(userdata
))
228 ladish_app_supervisor_proxy_handle proxy
;
229 graph_view_handle view
;
236 GtkEntry
* command_entry
= GTK_ENTRY(get_gtk_builder_widget("app_command_entry"));
237 GtkEntry
* name_entry
= GTK_ENTRY(get_gtk_builder_widget("app_name_entry"));
238 GtkToggleButton
* terminal_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_terminal_check_button"));
239 GtkToggleButton
* level0_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level0"));
240 GtkToggleButton
* level1_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level1"));
241 GtkToggleButton
* level2lash_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level2lash"));
242 GtkToggleButton
* level2js_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level2js"));
244 if (!get_selected_app_id(&view
, &id
))
249 log_info("app %"PRIu64
" properties", id
);
251 proxy
= graph_view_get_app_supervisor(view
);
253 if (!ladish_app_supervisor_get_app_properties(proxy
, id
, &name
, &command
, &running
, &terminal
, &level
))
255 error_message_box(_("Cannot get app properties"));
259 log_info("'%s':'%s' %s level '%s'", name
, command
, terminal
? "terminal" : "shell", level
);
261 gtk_entry_set_text(name_entry
, name
);
262 gtk_entry_set_text(command_entry
, command
);
263 gtk_toggle_button_set_active(terminal_button
, terminal
);
265 gtk_widget_set_sensitive(GTK_WIDGET(command_entry
), !running
);
266 gtk_widget_set_sensitive(GTK_WIDGET(terminal_button
), !running
);
267 gtk_widget_set_sensitive(GTK_WIDGET(level0_button
), !running
);
268 gtk_widget_set_sensitive(GTK_WIDGET(level1_button
), !running
);
269 gtk_widget_set_sensitive(GTK_WIDGET(level2lash_button
), !running
);
270 gtk_widget_set_sensitive(GTK_WIDGET(level2js_button
), !running
&& is_room_view(view
));
272 /* comparing pointers here is ok, because ladish_map_app_level_constant() was called by ladish_app_supervisor_get_app_properties() */
273 if (level
== LADISH_APP_LEVEL_0
)
275 gtk_toggle_button_set_active(level0_button
, TRUE
);
277 else if (level
== LADISH_APP_LEVEL_1
)
279 gtk_toggle_button_set_active(level1_button
, TRUE
);
281 else if (level
== LADISH_APP_LEVEL_LASH
)
283 gtk_toggle_button_set_active(level2lash_button
, TRUE
);
285 else if (level
== LADISH_APP_LEVEL_JACKSESSION
)
287 gtk_toggle_button_set_active(level2js_button
, TRUE
);
291 log_error("unknown level");
293 gtk_toggle_button_set_active(level0_button
, TRUE
);
299 gtk_window_set_focus(GTK_WINDOW(g_app_dialog
), running
? GTK_WIDGET(name_entry
) : GTK_WIDGET(command_entry
));
300 gtk_window_set_title(GTK_WINDOW(g_app_dialog
), _("App properties"));
302 gtk_widget_show(g_app_dialog
);
304 result
= gtk_dialog_run(GTK_DIALOG(g_app_dialog
));
307 if (gtk_toggle_button_get_active(level0_button
))
309 level
= LADISH_APP_LEVEL_0
;
311 else if (gtk_toggle_button_get_active(level1_button
))
313 level
= LADISH_APP_LEVEL_1
;
315 else if (gtk_toggle_button_get_active(level2lash_button
))
317 level
= LADISH_APP_LEVEL_LASH
;
319 else if (gtk_toggle_button_get_active(level2js_button
))
321 level
= LADISH_APP_LEVEL_JACKSESSION
;
325 log_error("unknown level");
327 level
= LADISH_APP_LEVEL_0
;
330 log_info("'%s':'%s' %s level '%s'", gtk_entry_get_text(name_entry
), gtk_entry_get_text(command_entry
), gtk_toggle_button_get_active(terminal_button
) ? "terminal" : "shell", level
);
331 if (!ladish_app_supervisor_set_app_properties(proxy
, id
, gtk_entry_get_text(name_entry
), gtk_entry_get_text(command_entry
), gtk_toggle_button_get_active(terminal_button
), level
))
333 error_message_box(_("Cannot set app properties."));
337 gtk_widget_hide(g_app_dialog
);
340 void popup_menu(GtkWidget
* UNUSED(treeview
), GdkEventButton
* event
)
342 GtkTreeSelection
* selection
;
345 GtkWidget
* menuitem
;
347 graph_view_handle view
;
353 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
354 if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
360 GTK_TREE_MODEL(g_treestore
),
365 COL_RUNNING
, &running
,
366 COL_TERMINAL
, &terminal
,
370 menu
= gtk_menu_new();
372 if (type
== entry_type_app
)
376 menuitem
= gtk_menu_item_new_with_label(_("Stop"));
377 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_stop
, NULL
);
378 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
380 menuitem
= gtk_menu_item_new_with_label(_("Kill"));
381 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_kill
, NULL
);
382 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
386 menuitem
= gtk_menu_item_new_with_label(_("Start"));
387 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_start
, NULL
);
388 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
391 menuitem
= gtk_menu_item_new_with_label(_("Properties"));
392 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_properties
, NULL
);
393 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
395 menuitem
= gtk_separator_menu_item_new(); /* separator */
396 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
398 menuitem
= gtk_menu_item_new_with_label(_("Remove"));
399 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_remove
, NULL
);
400 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
402 else if (type
== entry_type_view
)
404 fill_view_popup_menu(GTK_MENU(menu
), view
);
409 g_object_unref(menu
);
413 gtk_widget_show_all(menu
);
415 /* Note: event can be NULL here when called from view_onPopupMenu;
416 * gdk_event_get_time() accepts a NULL argument */
417 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, (event
!= NULL
) ? event
->button
: 0, gdk_event_get_time((GdkEvent
*)event
));
420 gboolean
on_button_pressed(GtkWidget
* treeview
, GdkEventButton
* event
, gpointer
UNUSED(userdata
))
422 /* single click with the right mouse button? */
423 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
425 /* select row if no row is selected or only one other row is selected */
427 GtkTreeSelection
*selection
;
429 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
431 /* Note: gtk_tree_selection_count_selected_rows() does not exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
432 if (gtk_tree_selection_count_selected_rows(selection
) <= 1)
436 /* Get tree path for row that was clicked */
437 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), (gint
)event
->x
, (gint
)event
->y
, &path
, NULL
, NULL
, NULL
))
439 gtk_tree_selection_unselect_all(selection
);
440 gtk_tree_selection_select_path(selection
, path
);
441 gtk_tree_path_free(path
);
446 popup_menu(treeview
, event
);
448 return TRUE
; /* we handled this */
451 return FALSE
; /* we did not handle this */
454 gboolean
on_popup_menu(GtkWidget
* treeview
, gpointer
UNUSED(userdata
))
456 popup_menu(treeview
, NULL
);
457 return TRUE
; /* we handled this */
463 GtkTreeView
* UNUSED(treeview
),
465 GtkTreeViewColumn
* UNUSED(col
),
466 gpointer
UNUSED(userdata
))
470 graph_view_handle view
;
473 ladish_app_supervisor_proxy_handle proxy
;
475 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(g_treestore
), &iter
, path
))
480 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_ID
, &id
, COL_RUNNING
, &running
, -1);
481 if (type
== entry_type_app
)
483 log_info("%s app %"PRIu64
, running
? "stop" : "start", id
);
485 if (!get_app_view(&iter
, &view
))
491 proxy
= graph_view_get_app_supervisor(view
);
492 (running
? ladish_app_supervisor_proxy_stop_app
: ladish_app_supervisor_proxy_start_app
)(proxy
, id
);
496 void world_tree_init(void)
498 GtkTreeViewColumn
* col
;
499 GtkCellRenderer
* renderer
;
500 GtkTreeSelection
* selection
;
502 g_world_tree_widget
= get_gtk_builder_widget("world_tree");
503 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(g_world_tree_widget
), FALSE
);
505 col
= gtk_tree_view_column_new();
506 gtk_tree_view_column_set_title(col
, _("Name"));
507 gtk_tree_view_append_column(GTK_TREE_VIEW(g_world_tree_widget
), col
);
508 renderer
= gtk_cell_renderer_text_new();
509 gtk_tree_view_column_pack_start(col
, renderer
, TRUE
);
510 gtk_tree_view_column_add_attribute(col
, renderer
, "text", COL_NAME
);
512 g_treestore
= gtk_tree_store_new(
521 gtk_tree_view_set_model(GTK_TREE_VIEW(g_world_tree_widget
), GTK_TREE_MODEL(g_treestore
));
523 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
524 gtk_tree_selection_set_select_function(selection
, on_select
, NULL
, NULL
);
526 g_signal_connect(g_world_tree_widget
, "button-press-event", (GCallback
)on_button_pressed
, NULL
);
527 g_signal_connect(g_world_tree_widget
, "popup-menu", (GCallback
)on_popup_menu
, NULL
);
528 g_signal_connect(g_world_tree_widget
, "row-activated", (GCallback
)on_row_activated
, NULL
);
531 void world_tree_add(graph_view_handle view
, bool force_activate
)
535 gtk_tree_store_append(g_treestore
, &iter
, NULL
);
536 gtk_tree_store_set(g_treestore
, &iter
, COL_TYPE
, entry_type_view
, COL_VIEW
, view
, COL_NAME
, get_view_name(view
), -1);
538 /* select the first top level item */
539 if (force_activate
|| gtk_tree_model_iter_n_children(GTK_TREE_MODEL(g_treestore
), NULL
) == 1)
541 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
)), &iter
);
545 graph_view_handle
world_tree_find_by_opath(const char * opath
)
548 graph_view_handle view
;
551 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), &iter
))
555 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, -1);
556 if (type
== entry_type_view
)
558 if (strcmp(get_view_opath(view
), opath
) == 0)
564 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), &iter
));
570 static bool find_view(graph_view_handle view
, GtkTreeIter
* iter_ptr
)
573 graph_view_handle view2
;
575 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), iter_ptr
))
579 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), iter_ptr
, COL_TYPE
, &type
, COL_VIEW
, &view2
, -1);
580 if (type
== entry_type_view
&& view
== view2
)
585 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), iter_ptr
));
591 static bool find_app(graph_view_handle view
, uint64_t id
, GtkTreeIter
* view_iter_ptr
, GtkTreeIter
* app_iter_ptr
)
596 if (!find_view(view
, view_iter_ptr
))
601 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
, view_iter_ptr
))
608 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
, COL_TYPE
, &type
, COL_ID
, &id2
, -1);
609 if (type
== entry_type_app
&& id
== id2
)
614 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
));
619 void world_tree_remove(graph_view_handle view
)
623 if (find_view(view
, &iter
))
625 gtk_tree_store_remove(g_treestore
, &iter
);
629 void world_tree_activate(graph_view_handle view
)
633 if (find_view(view
, &iter
))
635 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
)), &iter
);
639 void world_tree_name_changed(graph_view_handle view
)
643 if (find_view(view
, &iter
))
645 gtk_tree_store_set(g_treestore
, &iter
, COL_NAME
, get_view_name(view
), -1);
649 static char * get_app_name_string(const char * app_name
, bool running
, bool UNUSED(terminal
), const char * level
)
651 char * app_name_with_status
;
652 const char * level_string
;
654 level
= ladish_map_app_level_constant(level
);
655 /* comparing pointers here is ok, because ladish_map_app_level_constant() was called */
656 if (level
== LADISH_APP_LEVEL_0
)
658 level_string
= "[L0]";
660 else if (level
== LADISH_APP_LEVEL_1
)
662 level_string
= "[L1]";
664 else if (level
== LADISH_APP_LEVEL_LASH
)
666 level_string
= "[LASH]";
668 else if (level
== LADISH_APP_LEVEL_JACKSESSION
)
670 level_string
= "[JS]";
674 level_string
= "[L?]";
677 app_name_with_status
= catdup3(level_string
, running
? " " : _(" (inactive) "), app_name
);
678 if (app_name_with_status
== NULL
)
680 log_error("catdup failed for app name");
684 return app_name_with_status
;
687 void world_tree_add_app(graph_view_handle view
, uint64_t id
, const char * app_name
, bool running
, bool terminal
, const char * level
)
690 const char * view_name
;
693 char * app_name_with_status
;
695 if (!find_view(view
, &iter
))
701 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &iter
);
703 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_NAME
, &view_name
, -1);
705 log_info("adding app '%s' to '%s'", app_name
, view_name
);
707 app_name_with_status
= get_app_name_string(app_name
, running
, terminal
, level
);
708 if (app_name_with_status
== NULL
)
713 gtk_tree_store_append(g_treestore
, &child
, &iter
);
717 COL_TYPE
, entry_type_app
,
718 COL_NAME
, app_name_with_status
,
720 COL_RUNNING
, running
,
721 COL_TERMINAL
, terminal
,
724 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
726 free(app_name_with_status
);
728 gtk_tree_path_free(path
);
731 void world_tree_app_state_changed(graph_view_handle view
, uint64_t id
, const char * app_name
, bool running
, bool terminal
, const char * level
)
733 GtkTreeIter view_iter
;
734 GtkTreeIter app_iter
;
735 const char * view_name
;
737 char * app_name_with_status
;
739 if (!find_app(view
, id
, &view_iter
, &app_iter
))
741 log_error("removed app not found");
745 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &view_iter
);
747 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &view_iter
, COL_NAME
, &view_name
, -1);
749 log_info("changing app state '%s':'%s'", view_name
, app_name
);
751 app_name_with_status
= get_app_name_string(app_name
, running
, terminal
, level
);
752 if (app_name_with_status
== NULL
)
757 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
761 COL_NAME
, app_name_with_status
,
762 COL_RUNNING
, running
,
763 COL_TERMINAL
, terminal
,
767 free(app_name_with_status
);
769 gtk_tree_path_free(path
);
772 void world_tree_remove_app(graph_view_handle view
, uint64_t id
)
774 GtkTreeIter view_iter
;
775 GtkTreeIter app_iter
;
776 const char * view_name
;
777 const char * app_name
;
780 if (!find_app(view
, id
, &view_iter
, &app_iter
))
782 log_error("removed app not found");
786 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &view_iter
);
788 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &view_iter
, COL_NAME
, &view_name
, -1);
789 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &app_iter
, COL_NAME
, &app_name
, -1);
791 log_info("removing app '%s' from '%s'", app_name
, view_name
);
793 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
794 gtk_tree_store_remove(g_treestore
, &app_iter
);
796 gtk_tree_path_free(path
);
799 void world_tree_destroy_room_views(void)
802 graph_view_handle view
;
806 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), &iter
))
812 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, -1);
813 if (type
== entry_type_view
&& is_room_view(view
))
815 //log_info("removing view for room %s", get_view_opath(view));
816 valid
= gtk_tree_store_remove(g_treestore
, &iter
);
822 /* no more entries */
829 if (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), &iter
))