/* PureAdmin * Copyright (C) 2003 Isak Savo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Callback-functions for the main window GUI. * * Copyright (C) 2003- Isak Savo */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "mainwin.h" #include "globals.h" #include "srv_comm.h" #include "gui_helper.h" #include "helper.h" #include "cfg.h" #include "logfile.h" /** Private functions **/ /* Called for all remaining elements in the hash table. It adds those entries to * a GList */ static gboolean rmd_users_func (gchar *key, gchar *val, GList **lst_p) { *lst_p = g_list_append (*lst_p, g_strdup(key)); return TRUE; } static GList *find_completed_activities (GList *old, GList *new) { GList *node; GList *rv = NULL; if (old == NULL) return NULL; if (new == NULL) return old; node = old; while (node) { guint id_oldlist = ((PActivity*) node->data)->id; GList *new2 = new; /* Search the entire new list for any occurrance of the current activity in the old list */ while (new2) { guint id_newlist = ((PActivity*) new2->data)->id; if (id_oldlist > id_newlist) { /* No need to search the list anymore, we know it doesn't exist in the new list since * both lists are sorted by ID*/ rv = g_list_append (rv, node->data); break; } if (id_oldlist == id_newlist) /* The same activity exists in both lists, nothing more to do this iteration */ break; new2 = g_list_next (new2); } node = g_list_next (node); } return rv; } struct usrlist_diff { GList *new_users; GList *rmd_users; }; static struct usrlist_diff *compare_user_lists (GList *L1, GList *L2) { GHashTable *h; GList *node; POnlineUser *u; struct usrlist_diff *rv = g_new0 (struct usrlist_diff, 1); h = g_hash_table_new_full ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, (GDestroyNotify) g_free, NULL); node = L1; while (node) { /* Add all elements from list 1 to a hash table with username@host as key and random junk as value * (we're only interested in O(1) lookup of users, not any values) */ gchar *tmp; u = (POnlineUser *) node->data; tmp = g_strconcat (u->username, "@", u->remote_addr, NULL); g_hash_table_insert (h, tmp, "random junk"); node = node->next; } node = L2; while (node) { gchar *tmp; u = (POnlineUser *) node->data; tmp = g_strconcat (u->username, "@", u->remote_addr, NULL); if (g_hash_table_lookup (h, tmp)) { /* User:Address pair existed earlier */ g_hash_table_remove (h, tmp); } else { /* Whooo, a new user! */ rv->new_users = g_list_append (rv->new_users, tmp); g_hash_table_remove (h, tmp); } node = node->next; } if (g_hash_table_size (h) > 0) { /* One or more users have disconnected */ g_hash_table_foreach_remove (h,(GHRFunc) rmd_users_func, (gpointer) &(rv->rmd_users)); } return rv; } void user_list_select_user (const gchar *user, const gchar *remote_addr) { GtkTreeModel *model = NULL; GtkTreeIter iter; gboolean more; model = gtk_tree_view_get_model (GTK_TREE_VIEW(MW("tree_users"))); g_return_if_fail (user != NULL && *user != '\0'); g_return_if_fail (model != NULL); more = gtk_tree_model_get_iter_first (model, &iter); while (more) { gchar *cur_user, *cur_addr; gtk_tree_model_get(model, &iter, COL_USR_USER, &cur_user, COL_USR_HOST, &cur_addr, -1); if (g_str_equal (cur_user, user) && g_str_equal (cur_addr, remote_addr)) { gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW(MW("tree_users"))), &iter); break; } more = gtk_tree_model_iter_next (model, &iter); } } void init_gui_mainwindow (void) { GtkWidget *pane; GdkPixbuf *pbuf; GList *list = NULL; gint i; #define NUM_WINDOW_ICONS 4 const gchar *icons[NUM_WINDOW_ICONS] = { "pureadmin-16x16.png", "pureadmin-24x24.png", "pureadmin-32x32.png", "pureadmin-48x48.png" }; if (cfg.save_window_geometry == FALSE) { pur_log_nfo ("Will not restore window geometry, option turned off"); return; } /* Set position */ gtk_window_move (GTK_WINDOW (MW("main_window")), cfg.win_pos[0], cfg.win_pos[1]); /* Set size */ gtk_window_resize (GTK_WINDOW (MW("main_window")), cfg.win_size[0], cfg.win_size[1]); /* Divider position */ pane = MW("hpaned1"); if (!pane) { pur_log_wrn ("Unable to get hpaned widget"); } else gtk_paned_set_position (GTK_PANED (pane), cfg.div_pos); for (i = 0; i < NUM_WINDOW_ICONS; i++) { GError *err; gchar *fname; err = NULL; fname = g_build_filename (pixmapdir, icons[i], NULL); pbuf = gdk_pixbuf_new_from_file (fname, &err); if (!pbuf) { pur_log_wrn ("Couldn't load icon file %s: %s", icons[i], err->message); g_error_free (err); } list = g_list_append (list,(gpointer) pbuf); g_free (fname); } if (list) { GList *node = list; gtk_window_set_default_icon_list (list); while (node) { g_object_unref (G_OBJECT(node->data)); node = g_list_next (node); } g_list_free (list); } else pur_log_wrn ("No icons existed in %s", pixmapdir); show_status_icon ("PureAdmin started successfully"); #undef NUM_WINDOW_ICONS } void init_gui_logwindow (void) { GtkWidget *tmp, *lbl; GtkTextBuffer *text; pur_log_dbg ("Initializing gui_logwindow"); tmp = MW("logview"); text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tmp)); logview_tags[LOG_INFO] = gtk_text_buffer_create_tag (text, "info", "foreground", "blue", NULL); logview_tags[LOG_WARNING] = gtk_text_buffer_create_tag (text, "warning", "foreground", "#2e9344", NULL); logview_tags[LOG_ERROR] = gtk_text_buffer_create_tag (text, "error", "foreground", "red", NULL); logview_tags[LOG_NOTICE] = gtk_text_buffer_create_tag (text, "notice", "foreground", "#a427a9", NULL); logview_tags[LOG_DEBUG] = gtk_text_buffer_create_tag (text, "debug", "foreground", "#899f22", NULL); lbl = MW("lbl_logfilename"); #ifdef HAVE_LIBFAM gtk_label_set_text (GTK_LABEL (lbl), cfg.logfile); #else /* Disable logview controls and print a message explaining why! */ log_display_error_text (0); #endif } void init_gui_activities (void) { GtkListStore *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeView *tree_activity; //GtkTreeSelection *selection; pur_log_dbg ("Initializing gui_activities"); /* Set-up treeview */ tree_activity = GTK_TREE_VIEW (MW("tree_activity")); g_return_if_fail (tree_activity != NULL); model = gtk_list_store_new (N_ACT_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT); gtk_tree_view_set_model (GTK_TREE_VIEW (tree_activity), GTK_TREE_MODEL (model)); g_object_unref (G_OBJECT (model)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_activity)), GTK_SELECTION_SINGLE); renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "pixbuf", COL_ACT_ICON, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(tree_activity), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", COL_ACT_TEXT, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(tree_activity), column); /* /\* Hook-up some signals *\/ */ /* selection = gtk_tree_view_get_selection (tree_activity); */ /* g_signal_connect ((gpointer) selection, "changed", */ /* G_CALLBACK (booh), */ /* NULL); */ } void init_gui_online_users (void) { gint i; gchar *titles[] = { " ", _("User"), _("Host"), _("Connections") }; GtkListStore *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeView *tree_users; /* Set-up treeview */ pur_log_dbg ("Initializing gui_online_users"); tree_users = GTK_TREE_VIEW (MW("tree_users")); model = gtk_list_store_new (N_USR_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); gtk_tree_view_set_model (GTK_TREE_VIEW (tree_users), GTK_TREE_MODEL (model)); g_object_unref (G_OBJECT (model)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_users)), GTK_SELECTION_SINGLE); renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes (titles[0], renderer, "pixbuf", COL_USR_ICON, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(tree_users), column); for (i = 1; i < N_USR_COLUMNS; i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes (titles[i], renderer, "text", i, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(tree_users), column); } /* Hook-up some signals */ /* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (main_treeview)); g_signal_connect ((gpointer) selection, "changed", G_CALLBACK (on_list_selection_changed), NULL);*/ } gboolean timeout_update_activity (gpointer data) { GList *act_list, *usr_list, *node; PActivity *activity; POnlineUser *user; struct usrlist_diff *diff = NULL; static GdkPixbuf *icon_up = NULL; static GdkPixbuf *icon_down = NULL; static GdkPixbuf *icon_user = NULL; static GtkTreeView *tree_activity = NULL, *tree_users = NULL; static GtkTreeModel *model = NULL, *model_users = NULL; static GtkTreeSelection *sel = NULL, *sel_users = NULL; static GList *prev_usr_list; static GList *prev_act_list; GtkTreeIter iter; GtkTreePath *act_path, *usr_path; PGuiActivityRow arow; PGuiUserRow urow; gchar *percent; gboolean restore_selection_act = FALSE, restore_selection_usr = FALSE; if (G_UNLIKELY (!has_initialized(FALSE))) return TRUE; if (G_UNLIKELY (prog_exiting)) { g_print ("Timeout aborting\n"); return FALSE; } if (G_UNLIKELY (!icon_up)) { icon_up = create_pixbuf ("up.png"); icon_down = create_pixbuf ("down.png"); icon_user = create_pixbuf ("user.png"); } if (G_UNLIKELY (!tree_activity)) { tree_activity = (GtkTreeView *) MW("tree_activity"); model = gtk_tree_view_get_model (tree_activity); sel = gtk_tree_view_get_selection (tree_activity); tree_users = (GtkTreeView *) MW("tree_users"); model_users = gtk_tree_view_get_model (tree_users); sel_users = gtk_tree_view_get_selection (tree_users); } restore_selection_act = gtk_tree_selection_get_selected (sel, &model, &iter); if (restore_selection_act) act_path = gtk_tree_model_get_path (model, &iter); else act_path = NULL; restore_selection_usr = gtk_tree_selection_get_selected (sel_users, &model_users, &iter); if (restore_selection_usr) usr_path = gtk_tree_model_get_path (model_users, &iter); else usr_path = NULL; gui_clear_activity_list (); gui_clear_users_list (); act_list = srv_get_activities (); node = g_list_first (act_list); while (node) { activity = (PActivity *) node->data; if (activity->state == FTPWHO_STATE_DOWNLOAD) arow.icon = icon_down; else if (activity->state == FTPWHO_STATE_UPLOAD) arow.icon = icon_up; else { /* Only append actual actions (DL/UL) */ node = g_list_next (node); continue; } arow.id = activity->id; if (activity->download_total_size > 0) percent = g_strdup_printf ("%.2f", 100 * (double) activity->download_current_size / (double) activity->download_total_size); else percent = g_strdup ("??"); arow.text = g_strdup_printf ("%s (%s):'%s' - (%s%%)", activity->username, activity->remote_addr, activity->filename, percent); gui_add_to_activities (arow); node = g_list_next (node); g_free (arow.text); g_free (percent); } if (restore_selection_act) { gtk_tree_selection_select_path (sel, act_path); g_signal_emit_by_name ((gpointer) tree_activity, "cursor_changed", tree_activity, NULL); } /* Update users list */ usr_list = srv_get_connected_users (g_list_first (act_list)); node = usr_list; urow.icon = icon_user; while (node) { user = (POnlineUser *) node->data; urow.user = string_from_vuser_locale (user->username); urow.host = g_strdup (user->remote_addr); urow.num_connections = user->num_connections; gui_add_to_online_users (urow); g_free (urow.user); g_free (urow.host); node = g_list_next (node); } diff = compare_user_lists (prev_usr_list, usr_list); if (diff->new_users || diff->rmd_users) { GString *msg = g_string_new (""); gint len = g_list_length (diff->new_users); if (len == 1) { gchar *tmp = pur_elipzise ((gchar *) diff->new_users->data, 20); g_string_append_printf (msg, "New connection from %s\n", tmp); } else if (len > 1) g_string_append_printf (msg, "%d new connections\n", len); len = g_list_length (diff->rmd_users); if (len == 1) { gchar *tmp = pur_elipzise ((gchar *) diff->rmd_users->data, 20); g_string_append_printf (msg, "%s has disconnected\n", tmp); } else if (len > 1) g_string_append_printf (msg, "%d users has disconnected\n", len); if (msg->str[msg->len-1] == '\n') g_string_truncate (msg, msg->len - 1); show_status_icon (msg->str); g_string_free (msg, TRUE); g_list_foreach (diff->new_users, (GFunc) g_free, NULL); g_list_foreach (diff->rmd_users, (GFunc) g_free, NULL); g_list_free (diff->new_users); g_list_free (diff->rmd_users); g_free (diff); } free_pouser_list (prev_usr_list); prev_usr_list = usr_list; if (cfg.always_notify_activities) { GList *done_activities = find_completed_activities(prev_act_list, act_list); node = done_activities; while (node) { PActivity *act = (PActivity *) node->data; gchar *body = g_strdup_printf (_("User '%s' has finished %s the file '%s'"), act->username, act->state == FTPWHO_STATE_DOWNLOAD ? _("downloading") : _("uploading"), act->filename); send_notification (_("FTP Activity Completed"), body); node = g_list_next (node); } } free_activity_list (prev_act_list); prev_act_list = act_list; if (restore_selection_usr) gtk_tree_selection_select_path (sel_users, usr_path); // gui_update_menu_sensivity (); /* Always return TRUE */ return TRUE; }