gui: context menu for the world tree
[ladish.git] / gui / world_tree.c
blob112c06ac6e1a32cd773d050e3e4816007d32bba8
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009 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.
27 #include "common.h"
28 #include "world_tree.h"
29 #include "glade.h"
30 #include "../catdup.h"
32 enum entry_type
34 entry_type_view,
35 entry_type_app
38 enum
40 COL_TYPE = 0,
41 COL_NAME,
42 COL_VIEW,
43 COL_ID,
44 COL_RUNNING,
45 COL_TERMINAL,
46 COL_LEVEL,
47 NUM_COLS
50 GtkWidget * g_world_tree_widget;
51 GtkTreeStore * g_treestore;
53 static
54 gboolean
55 on_select(
56 GtkTreeSelection * selection,
57 GtkTreeModel * model,
58 GtkTreePath * path,
59 gboolean path_currently_selected,
60 gpointer data)
62 GtkTreeIter iter;
63 graph_view_handle view;
64 gint type;
66 if (gtk_tree_model_get_iter(model, &iter, path))
68 gtk_tree_model_get(model, &iter, COL_TYPE, &type, COL_VIEW, &view, -1);
69 if (type == entry_type_view)
71 //log_info("%s is going to be %s.", get_view_name(view), path_currently_selected ? "unselected" : "selected");
72 if (!path_currently_selected)
74 activate_view(view);
79 return TRUE;
82 bool get_selected_app_id(uint64_t * id_ptr)
84 GtkTreeSelection * selection;
85 GtkTreeIter iter;
86 gint type;
87 uint64_t id;
89 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget));
90 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
92 return false;
95 gtk_tree_model_get(
96 GTK_TREE_MODEL(g_treestore),
97 &iter,
98 COL_TYPE, &type,
99 COL_ID, &id,
100 -1);
101 if (type != entry_type_app)
103 return false;
106 *id_ptr = id;
108 return true;
111 void on_popup_menu_action_app_start(GtkWidget * menuitem, gpointer userdata)
113 uint64_t id;
115 if (!get_selected_app_id(&id))
117 return;
120 log_info("start app %"PRIu64, id);
123 void on_popup_menu_action_app_stop(GtkWidget * menuitem, gpointer userdata)
125 uint64_t id;
127 if (!get_selected_app_id(&id))
129 return;
132 log_info("stop app %"PRIu64, id);
135 void on_popup_menu_action_app_remove(GtkWidget * menuitem, gpointer userdata)
137 uint64_t id;
139 if (!get_selected_app_id(&id))
141 return;
144 log_info("remove app %"PRIu64, id);
147 void popup_menu(GtkWidget * treeview, GdkEventButton * event)
149 GtkTreeSelection * selection;
150 GtkTreeIter iter;
151 GtkWidget * menu;
152 GtkWidget * menuitem;
153 gint type;
154 graph_view_handle view;
155 uint64_t id;
156 gboolean running;
157 gboolean terminal;
158 uint8_t level;
160 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget));
161 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
163 return;
166 gtk_tree_model_get(
167 GTK_TREE_MODEL(g_treestore),
168 &iter,
169 COL_TYPE, &type,
170 COL_VIEW, &view,
171 COL_ID, &id,
172 COL_RUNNING, &running,
173 COL_TERMINAL, &terminal,
174 COL_LEVEL, &level,
175 -1);
176 if (type != entry_type_app)
178 return;
181 menu = gtk_menu_new();
183 if (running)
185 menuitem = gtk_menu_item_new_with_label("Stop");
186 g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_app_stop, NULL);
187 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
189 else
191 menuitem = gtk_menu_item_new_with_label("Start");
192 g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_app_start, NULL);
193 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
196 menuitem = gtk_menu_item_new_with_label("Remove");
197 g_signal_connect(menuitem, "activate", (GCallback)on_popup_menu_action_app_remove, NULL);
198 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
200 gtk_widget_show_all(menu);
202 /* Note: event can be NULL here when called from view_onPopupMenu;
203 * gdk_event_get_time() accepts a NULL argument */
204 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, (event != NULL) ? event->button : 0, gdk_event_get_time((GdkEvent *)event));
207 gboolean on_button_pressed(GtkWidget * treeview, GdkEventButton * event, gpointer userdata)
209 /* single click with the right mouse button? */
210 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
212 /* select row if no row is selected or only one other row is selected */
214 GtkTreeSelection *selection;
216 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
218 /* Note: gtk_tree_selection_count_selected_rows() does not exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
219 if (gtk_tree_selection_count_selected_rows(selection) <= 1)
221 GtkTreePath *path;
223 /* Get tree path for row that was clicked */
224 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
226 gtk_tree_selection_unselect_all(selection);
227 gtk_tree_selection_select_path(selection, path);
228 gtk_tree_path_free(path);
233 popup_menu(treeview, event);
235 return TRUE; /* we handled this */
238 return FALSE; /* we did not handle this */
241 gboolean on_popup_menu(GtkWidget * treeview, gpointer userdata)
243 popup_menu(treeview, NULL);
244 return TRUE; /* we handled this */
247 void world_tree_init(void)
249 GtkTreeViewColumn * col;
250 GtkCellRenderer * renderer;
251 GtkTreeSelection * selection;
253 g_world_tree_widget = get_glade_widget("world_tree");
254 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(g_world_tree_widget), FALSE);
256 col = gtk_tree_view_column_new();
257 gtk_tree_view_column_set_title(col, "Name");
258 gtk_tree_view_append_column(GTK_TREE_VIEW(g_world_tree_widget), col);
259 renderer = gtk_cell_renderer_text_new();
260 gtk_tree_view_column_pack_start(col, renderer, TRUE);
261 gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME);
263 g_treestore = gtk_tree_store_new(
264 NUM_COLS,
265 G_TYPE_INT,
266 G_TYPE_STRING,
267 G_TYPE_POINTER,
268 G_TYPE_UINT64,
269 G_TYPE_BOOLEAN,
270 G_TYPE_BOOLEAN,
271 G_TYPE_CHAR);
272 gtk_tree_view_set_model(GTK_TREE_VIEW(g_world_tree_widget), GTK_TREE_MODEL(g_treestore));
274 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget));
275 gtk_tree_selection_set_select_function(selection, on_select, NULL, NULL);
277 g_signal_connect(g_world_tree_widget, "button-press-event", (GCallback)on_button_pressed, NULL);
278 g_signal_connect(g_world_tree_widget, "popup-menu", (GCallback)on_popup_menu, NULL);
281 void world_tree_add(graph_view_handle view, bool force_activate)
283 GtkTreeIter iter;
285 gtk_tree_store_append(g_treestore, &iter, NULL);
286 gtk_tree_store_set(g_treestore, &iter, COL_TYPE, entry_type_view, COL_VIEW, view, COL_NAME, get_view_name(view), -1);
288 /* select the first top level item */
289 if (force_activate || gtk_tree_model_iter_n_children(GTK_TREE_MODEL(g_treestore), NULL) == 1)
291 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget)), &iter);
295 static bool find_view(graph_view_handle view, GtkTreeIter * iter_ptr)
297 gint type;
298 graph_view_handle view2;
300 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(g_treestore), iter_ptr))
304 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), iter_ptr, COL_TYPE, &type, COL_VIEW, &view2, -1);
305 if (type == entry_type_view && view == view2)
307 return true;
310 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore), iter_ptr));
313 return false;
316 static bool find_app(graph_view_handle view, uint64_t id, GtkTreeIter * view_iter_ptr, GtkTreeIter * app_iter_ptr)
318 gint type;
319 uint64_t id2;
321 if (!find_view(view, view_iter_ptr))
323 return false;
326 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(g_treestore), app_iter_ptr, view_iter_ptr))
328 return false;
333 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), app_iter_ptr, COL_TYPE, &type, COL_ID, &id2, -1);
334 if (type == entry_type_app && id == id2)
336 return true;
339 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(g_treestore), app_iter_ptr));
341 return false;
344 void world_tree_remove(graph_view_handle view)
346 GtkTreeIter iter;
348 if (find_view(view, &iter))
350 gtk_tree_store_remove(g_treestore, &iter);
354 void world_tree_activate(graph_view_handle view)
356 GtkTreeIter iter;
358 if (find_view(view, &iter))
360 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(g_world_tree_widget)), &iter);
364 void world_tree_name_changed(graph_view_handle view)
366 GtkTreeIter iter;
368 if (find_view(view, &iter))
370 gtk_tree_store_set(g_treestore, &iter, COL_NAME, get_view_name(view), -1);
374 static char * get_app_name_string(const char * app_name, bool running, bool terminal, uint8_t level)
376 char * app_name_with_status;
378 app_name_with_status = catdup(running ? "" : "(inactive) ", app_name);
379 if (app_name_with_status == NULL)
381 log_error("catdup failed for app name");
382 return NULL;
385 return app_name_with_status;
388 void world_tree_add_app(graph_view_handle view, uint64_t id, const char * app_name, bool running, bool terminal, uint8_t level)
390 GtkTreeIter iter;
391 const char * view_name;
392 GtkTreeIter child;
393 GtkTreePath * path;
394 char * app_name_with_status;
396 if (!find_view(view, &iter))
398 ASSERT_NO_PASS;
399 return;
402 path = gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore), &iter);
404 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), &iter, COL_NAME, &view_name, -1);
406 log_info("adding app '%s' to '%s'", app_name, view_name);
408 app_name_with_status = get_app_name_string(app_name, running, terminal, level);
409 if (app_name_with_status == NULL)
411 goto free_path;
414 gtk_tree_store_append(g_treestore, &child, &iter);
415 gtk_tree_store_set(
416 g_treestore,
417 &child,
418 COL_TYPE, entry_type_app,
419 COL_NAME, app_name_with_status,
420 COL_ID, id,
421 COL_RUNNING, running,
422 COL_TERMINAL, terminal,
423 COL_LEVEL, level,
424 -1);
425 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget), path, false);
427 free(app_name_with_status);
428 free_path:
429 gtk_tree_path_free(path);
432 void world_tree_app_state_changed(graph_view_handle view, uint64_t id, const char * name, bool running, bool terminal, uint8_t level)
434 GtkTreeIter view_iter;
435 GtkTreeIter app_iter;
436 const char * view_name;
437 const char * app_name;
438 GtkTreePath * path;
439 char * app_name_with_status;
441 if (!find_app(view, id, &view_iter, &app_iter))
443 log_error("removed app not found");
444 return;
447 path = gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore), &view_iter);
449 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), &view_iter, COL_NAME, &view_name, -1);
450 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), &app_iter, COL_NAME, &app_name, -1);
452 log_info("changing app state '%s':'%s'", view_name, app_name);
454 app_name_with_status = get_app_name_string(app_name, running, terminal, level);
455 if (app_name_with_status == NULL)
457 goto free_path;
460 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget), path, false);
461 gtk_tree_store_set(
462 g_treestore,
463 &app_iter,
464 COL_NAME, app_name_with_status,
465 COL_RUNNING, running,
466 COL_TERMINAL, terminal,
467 COL_LEVEL, level,
468 -1);
470 free(app_name_with_status);
471 free_path:
472 gtk_tree_path_free(path);
475 void world_tree_remove_app(graph_view_handle view, uint64_t id)
477 GtkTreeIter view_iter;
478 GtkTreeIter app_iter;
479 const char * view_name;
480 const char * app_name;
481 GtkTreePath * path;
483 if (!find_app(view, id, &view_iter, &app_iter))
485 log_error("removed app not found");
486 return;
489 path = gtk_tree_model_get_path(GTK_TREE_MODEL(g_treestore), &view_iter);
491 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), &view_iter, COL_NAME, &view_name, -1);
492 gtk_tree_model_get(GTK_TREE_MODEL(g_treestore), &app_iter, COL_NAME, &app_name, -1);
494 log_info("removing app '%s' from '%s'", app_name, view_name);
496 gtk_tree_view_expand_row(GTK_TREE_VIEW(g_world_tree_widget), path, false);
497 gtk_tree_store_remove(g_treestore, &app_iter);
499 gtk_tree_path_free(path);