1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009, 2010 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 "../catdup.h"
51 GtkWidget
* g_world_tree_widget
;
52 GtkTreeStore
* g_treestore
;
54 bool get_app_view(GtkTreeIter
* app_iter_ptr
, graph_view_handle
* view_ptr
)
56 GtkTreeIter view_iter
;
59 if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(g_treestore
), &view_iter
, app_iter_ptr
))
66 GTK_TREE_MODEL(g_treestore
),
71 if (type
!= entry_type_view
)
83 GtkTreeSelection
* selection
,
86 gboolean path_currently_selected
,
90 graph_view_handle view
;
94 if (gtk_tree_model_get_iter(model
, &iter
, path
))
96 gtk_tree_model_get(model
, &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, COL_ID
, &id
, -1);
100 if (!get_app_view(&iter
, &view
))
105 case entry_type_view
:
106 //log_info("%s is going to be %s.", get_view_name(view), path_currently_selected ? "unselected" : "selected");
107 if (!path_currently_selected
)
118 bool get_selected_app_id(graph_view_handle
* view_ptr
, uint64_t * id_ptr
)
120 GtkTreeSelection
* selection
;
121 GtkTreeIter app_iter
;
124 graph_view_handle view
;
126 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
127 if (!gtk_tree_selection_get_selected(selection
, NULL
, &app_iter
))
133 GTK_TREE_MODEL(g_treestore
),
138 if (type
!= entry_type_app
)
143 if (!get_app_view(&app_iter
, &view
))
155 void on_popup_menu_action_app_start(GtkWidget
* menuitem
, gpointer userdata
)
158 ladish_app_supervisor_proxy_handle proxy
;
159 graph_view_handle view
;
161 if (!get_selected_app_id(&view
, &id
))
166 log_info("start app %"PRIu64
, id
);
168 proxy
= graph_view_get_app_supervisor(view
);
169 ladish_app_supervisor_proxy_start_app(proxy
, id
);
172 void on_popup_menu_action_app_stop(GtkWidget
* menuitem
, gpointer userdata
)
175 ladish_app_supervisor_proxy_handle proxy
;
176 graph_view_handle view
;
178 if (!get_selected_app_id(&view
, &id
))
183 log_info("stop app %"PRIu64
, id
);
185 proxy
= graph_view_get_app_supervisor(view
);
186 ladish_app_supervisor_proxy_stop_app(proxy
, id
);
189 void on_popup_menu_action_app_kill(GtkWidget
* menuitem
, gpointer userdata
)
192 ladish_app_supervisor_proxy_handle proxy
;
193 graph_view_handle view
;
195 if (!get_selected_app_id(&view
, &id
))
200 log_info("kill app %"PRIu64
, id
);
202 proxy
= graph_view_get_app_supervisor(view
);
203 ladish_app_supervisor_proxy_kill_app(proxy
, id
);
206 void on_popup_menu_action_app_remove(GtkWidget
* menuitem
, gpointer userdata
)
209 ladish_app_supervisor_proxy_handle proxy
;
210 graph_view_handle view
;
212 if (!get_selected_app_id(&view
, &id
))
217 log_info("remove app %"PRIu64
, id
);
219 proxy
= graph_view_get_app_supervisor(view
);
220 ladish_app_supervisor_proxy_remove_app(proxy
, id
);
223 void on_popup_menu_action_app_properties(GtkWidget
* menuitem
, gpointer userdata
)
226 ladish_app_supervisor_proxy_handle proxy
;
227 graph_view_handle view
;
234 GtkEntry
* command_entry
= GTK_ENTRY(get_gtk_builder_widget("app_command_entry"));
235 GtkEntry
* name_entry
= GTK_ENTRY(get_gtk_builder_widget("app_name_entry"));
236 GtkToggleButton
* terminal_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_terminal_check_button"));
237 GtkToggleButton
* level0_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level0"));
238 GtkToggleButton
* level1_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level1"));
239 GtkToggleButton
* level2_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level2"));
240 GtkToggleButton
* level3_button
= GTK_TOGGLE_BUTTON(get_gtk_builder_widget("app_level3"));
242 if (!get_selected_app_id(&view
, &id
))
247 log_info("app %"PRIu64
" properties", id
);
249 proxy
= graph_view_get_app_supervisor(view
);
251 if (!ladish_app_supervisor_get_app_properties(proxy
, id
, &name
, &command
, &running
, &terminal
, &level
))
253 error_message_box("Cannot get app properties");
257 log_info("'%s':'%s' %s level %"PRIu8
, name
, command
, terminal
? "terminal" : "shell", level
);
259 gtk_entry_set_text(name_entry
, name
);
260 gtk_entry_set_text(command_entry
, command
);
261 gtk_toggle_button_set_active(terminal_button
, terminal
);
263 gtk_widget_set_sensitive(GTK_WIDGET(command_entry
), !running
);
264 gtk_widget_set_sensitive(GTK_WIDGET(terminal_button
), !running
);
265 gtk_widget_set_sensitive(GTK_WIDGET(level0_button
), !running
);
266 gtk_widget_set_sensitive(GTK_WIDGET(level1_button
), !running
);
271 gtk_toggle_button_set_active(level0_button
, TRUE
);
274 gtk_toggle_button_set_active(level1_button
, TRUE
);
277 gtk_toggle_button_set_active(level2_button
, TRUE
);
280 gtk_toggle_button_set_active(level3_button
, TRUE
);
283 log_error("unknown level");
285 gtk_toggle_button_set_active(level0_button
, TRUE
);
291 gtk_window_set_focus(GTK_WINDOW(g_app_dialog
), running
? GTK_WIDGET(name_entry
) : GTK_WIDGET(command_entry
));
292 gtk_window_set_title(GTK_WINDOW(g_app_dialog
), "App properties");
294 gtk_widget_show(g_app_dialog
);
296 result
= gtk_dialog_run(GTK_DIALOG(g_app_dialog
));
299 if (gtk_toggle_button_get_active(level0_button
))
303 else if (gtk_toggle_button_get_active(level1_button
))
307 else if (gtk_toggle_button_get_active(level2_button
))
311 else if (gtk_toggle_button_get_active(level3_button
))
317 log_error("unknown level");
322 log_info("'%s':'%s' %s level %"PRIu8
, gtk_entry_get_text(name_entry
), gtk_entry_get_text(command_entry
), gtk_toggle_button_get_active(terminal_button
) ? "terminal" : "shell", level
);
323 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
))
325 error_message_box("Cannot set app properties.");
329 gtk_widget_hide(g_app_dialog
);
332 void popup_menu(GtkWidget
* treeview
, GdkEventButton
* event
)
334 GtkTreeSelection
* selection
;
337 GtkWidget
* menuitem
;
339 graph_view_handle view
;
345 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
346 if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
352 GTK_TREE_MODEL(g_treestore
),
357 COL_RUNNING
, &running
,
358 COL_TERMINAL
, &terminal
,
362 menu
= gtk_menu_new();
364 if (type
== entry_type_app
)
368 menuitem
= gtk_menu_item_new_with_label("Stop");
369 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_stop
, NULL
);
370 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
372 menuitem
= gtk_menu_item_new_with_label("Kill");
373 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_kill
, NULL
);
374 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
378 menuitem
= gtk_menu_item_new_with_label("Start");
379 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_start
, NULL
);
380 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
383 menuitem
= gtk_menu_item_new_with_label("Properties");
384 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_properties
, NULL
);
385 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
387 menuitem
= gtk_separator_menu_item_new(); /* separator */
388 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
390 menuitem
= gtk_menu_item_new_with_label("Remove");
391 g_signal_connect(menuitem
, "activate", (GCallback
)on_popup_menu_action_app_remove
, NULL
);
392 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
394 else if (type
== entry_type_view
)
396 fill_view_popup_menu(GTK_MENU(menu
), view
);
401 g_object_unref(menu
);
405 gtk_widget_show_all(menu
);
407 /* Note: event can be NULL here when called from view_onPopupMenu;
408 * gdk_event_get_time() accepts a NULL argument */
409 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, (event
!= NULL
) ? event
->button
: 0, gdk_event_get_time((GdkEvent
*)event
));
412 gboolean
on_button_pressed(GtkWidget
* treeview
, GdkEventButton
* event
, gpointer userdata
)
414 /* single click with the right mouse button? */
415 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
417 /* select row if no row is selected or only one other row is selected */
419 GtkTreeSelection
*selection
;
421 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
423 /* Note: gtk_tree_selection_count_selected_rows() does not exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
424 if (gtk_tree_selection_count_selected_rows(selection
) <= 1)
428 /* Get tree path for row that was clicked */
429 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), (gint
)event
->x
, (gint
)event
->y
, &path
, NULL
, NULL
, NULL
))
431 gtk_tree_selection_unselect_all(selection
);
432 gtk_tree_selection_select_path(selection
, path
);
433 gtk_tree_path_free(path
);
438 popup_menu(treeview
, event
);
440 return TRUE
; /* we handled this */
443 return FALSE
; /* we did not handle this */
446 gboolean
on_popup_menu(GtkWidget
* treeview
, gpointer userdata
)
448 popup_menu(treeview
, NULL
);
449 return TRUE
; /* we handled this */
452 static void on_row_activated(GtkTreeView
* treeview
, GtkTreePath
* path
, GtkTreeViewColumn
* col
, gpointer userdata
)
456 graph_view_handle view
;
459 ladish_app_supervisor_proxy_handle proxy
;
461 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(g_treestore
), &iter
, path
))
466 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_ID
, &id
, COL_RUNNING
, &running
, -1);
467 if (type
== entry_type_app
)
469 log_info("%s app %"PRIu64
, running
? "stop" : "start", id
);
471 if (!get_app_view(&iter
, &view
))
477 proxy
= graph_view_get_app_supervisor(view
);
478 (running
? ladish_app_supervisor_proxy_stop_app
: ladish_app_supervisor_proxy_start_app
)(proxy
, id
);
482 void world_tree_init(void)
484 GtkTreeViewColumn
* col
;
485 GtkCellRenderer
* renderer
;
486 GtkTreeSelection
* selection
;
488 g_world_tree_widget
= get_gtk_builder_widget("world_tree");
489 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(g_world_tree_widget
), FALSE
);
491 col
= gtk_tree_view_column_new();
492 gtk_tree_view_column_set_title(col
, "Name");
493 gtk_tree_view_append_column(GTK_TREE_VIEW(g_world_tree_widget
), col
);
494 renderer
= gtk_cell_renderer_text_new();
495 gtk_tree_view_column_pack_start(col
, renderer
, TRUE
);
496 gtk_tree_view_column_add_attribute(col
, renderer
, "text", COL_NAME
);
498 g_treestore
= gtk_tree_store_new(
507 gtk_tree_view_set_model(GTK_TREE_VIEW(g_world_tree_widget
), GTK_TREE_MODEL(g_treestore
));
509 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
));
510 gtk_tree_selection_set_select_function(selection
, on_select
, NULL
, NULL
);
512 g_signal_connect(g_world_tree_widget
, "button-press-event", (GCallback
)on_button_pressed
, NULL
);
513 g_signal_connect(g_world_tree_widget
, "popup-menu", (GCallback
)on_popup_menu
, NULL
);
514 g_signal_connect(g_world_tree_widget
, "row-activated", (GCallback
)on_row_activated
, NULL
);
517 void world_tree_add(graph_view_handle view
, bool force_activate
)
521 gtk_tree_store_append(g_treestore
, &iter
, NULL
);
522 gtk_tree_store_set(g_treestore
, &iter
, COL_TYPE
, entry_type_view
, COL_VIEW
, view
, COL_NAME
, get_view_name(view
), -1);
524 /* select the first top level item */
525 if (force_activate
|| gtk_tree_model_iter_n_children(GTK_TREE_MODEL(g_treestore
), NULL
) == 1)
527 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
)), &iter
);
531 graph_view_handle
world_tree_find_by_opath(const char * opath
)
534 graph_view_handle view
;
537 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), &iter
))
541 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, -1);
542 if (type
== entry_type_view
)
544 if (strcmp(get_view_opath(view
), opath
) == 0)
550 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), &iter
));
556 static bool find_view(graph_view_handle view
, GtkTreeIter
* iter_ptr
)
559 graph_view_handle view2
;
561 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), iter_ptr
))
565 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), iter_ptr
, COL_TYPE
, &type
, COL_VIEW
, &view2
, -1);
566 if (type
== entry_type_view
&& view
== view2
)
571 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), iter_ptr
));
577 static bool find_app(graph_view_handle view
, uint64_t id
, GtkTreeIter
* view_iter_ptr
, GtkTreeIter
* app_iter_ptr
)
582 if (!find_view(view
, view_iter_ptr
))
587 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
, view_iter_ptr
))
594 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
, COL_TYPE
, &type
, COL_ID
, &id2
, -1);
595 if (type
== entry_type_app
&& id
== id2
)
600 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), app_iter_ptr
));
605 void world_tree_remove(graph_view_handle view
)
609 if (find_view(view
, &iter
))
611 gtk_tree_store_remove(g_treestore
, &iter
);
615 void world_tree_activate(graph_view_handle view
)
619 if (find_view(view
, &iter
))
621 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget
)), &iter
);
625 void world_tree_name_changed(graph_view_handle view
)
629 if (find_view(view
, &iter
))
631 gtk_tree_store_set(g_treestore
, &iter
, COL_NAME
, get_view_name(view
), -1);
635 static char * get_app_name_string(const char * app_name
, bool running
, bool terminal
, uint8_t level
)
637 char * app_name_with_status
;
638 const char * level_string
;
643 level_string
= "[L0]";
646 level_string
= "[L1]";
649 level_string
= "[L?]";
652 app_name_with_status
= catdup3(level_string
, running
? " " : " (inactive) ", app_name
);
653 if (app_name_with_status
== NULL
)
655 log_error("catdup failed for app name");
659 return app_name_with_status
;
662 void world_tree_add_app(graph_view_handle view
, uint64_t id
, const char * app_name
, bool running
, bool terminal
, uint8_t level
)
665 const char * view_name
;
668 char * app_name_with_status
;
670 if (!find_view(view
, &iter
))
676 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &iter
);
678 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_NAME
, &view_name
, -1);
680 log_info("adding app '%s' to '%s'", app_name
, view_name
);
682 app_name_with_status
= get_app_name_string(app_name
, running
, terminal
, level
);
683 if (app_name_with_status
== NULL
)
688 gtk_tree_store_append(g_treestore
, &child
, &iter
);
692 COL_TYPE
, entry_type_app
,
693 COL_NAME
, app_name_with_status
,
695 COL_RUNNING
, running
,
696 COL_TERMINAL
, terminal
,
699 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
701 free(app_name_with_status
);
703 gtk_tree_path_free(path
);
706 void world_tree_app_state_changed(graph_view_handle view
, uint64_t id
, const char * app_name
, bool running
, bool terminal
, uint8_t level
)
708 GtkTreeIter view_iter
;
709 GtkTreeIter app_iter
;
710 const char * view_name
;
712 char * app_name_with_status
;
714 if (!find_app(view
, id
, &view_iter
, &app_iter
))
716 log_error("removed app not found");
720 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &view_iter
);
722 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &view_iter
, COL_NAME
, &view_name
, -1);
724 log_info("changing app state '%s':'%s'", view_name
, app_name
);
726 app_name_with_status
= get_app_name_string(app_name
, running
, terminal
, level
);
727 if (app_name_with_status
== NULL
)
732 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
736 COL_NAME
, app_name_with_status
,
737 COL_RUNNING
, running
,
738 COL_TERMINAL
, terminal
,
742 free(app_name_with_status
);
744 gtk_tree_path_free(path
);
747 void world_tree_remove_app(graph_view_handle view
, uint64_t id
)
749 GtkTreeIter view_iter
;
750 GtkTreeIter app_iter
;
751 const char * view_name
;
752 const char * app_name
;
755 if (!find_app(view
, id
, &view_iter
, &app_iter
))
757 log_error("removed app not found");
761 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore
), &view_iter
);
763 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &view_iter
, COL_NAME
, &view_name
, -1);
764 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &app_iter
, COL_NAME
, &app_name
, -1);
766 log_info("removing app '%s' from '%s'", app_name
, view_name
);
768 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget
), path
, false);
769 gtk_tree_store_remove(g_treestore
, &app_iter
);
771 gtk_tree_path_free(path
);
774 void world_tree_destroy_room_views(void)
777 graph_view_handle view
;
781 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore
), &iter
))
787 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore
), &iter
, COL_TYPE
, &type
, COL_VIEW
, &view
, -1);
788 if (type
== entry_type_view
&& is_room_view(view
))
790 //log_info("removing view for room %s", get_view_opath(view));
791 valid
= gtk_tree_store_remove(g_treestore
, &iter
);
797 /* no more entries */
804 if (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore
), &iter
))