/* 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. */ /* * Misc helperfunctions related to the GUI * * Copyright (C) 2003 Isak Savo */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "eggstatusicon.h" #include "helper.h" #include "gui_helper.h" #include "srv_comm.h" #include "usr_manager.h" #include "cfg.h" #include "binreloc.h" #include "system_accounts.h" EggStatusIcon *status_icon = NULL; static gchar *sec_to_time (gulong sec) { gchar *retval; gint hr, min; hr = sec / 3600; sec -= hr * 3600; min = sec / 60; sec -= min * 60; retval = g_strdup_printf ("%02d:%02d:%02d", hr, min, (gint) sec); return retval; } static gchar *get_size_string (guint kbytes) { if (kbytes > 1024 * 1024) return g_strdup_printf ("%.2lf %s", (gdouble) kbytes / (1024.0 * 1024.0), _("GiB")); if (kbytes > 1024) return g_strdup_printf ("%.2lf %s", (gdouble) kbytes / 1024.0, _("MiB")); return g_strdup_printf ("%d %s", kbytes, _("KiB")); } static gchar *gen_speed_string (gulong speed) { if (speed > 1024 * 1024) return g_strdup_printf ("%.1lf %s", (gdouble) speed / (1024.0 * 1024.0), _("GiB/s")); if (speed > 1024) return g_strdup_printf ("%.1lf %s", (gdouble) speed / 1024.0, _("MiB/s")); if (speed > 0) return g_strdup_printf ("%lu %s", speed, _("KiB/s")); /* Zero bytes per second in transfer speed */ return g_strdup (_("0 b/s (stalled)")); } void gui_terminate (void) { GtkWidget *pane, *main_window; /* Save window size and position */ pane = MW("hpaned1"); main_window = MW("main_window"); gtk_window_get_position (GTK_WINDOW (main_window), &(cfg.win_pos[0]), &(cfg.win_pos[1])); gtk_window_get_size (GTK_WINDOW (main_window), &(cfg.win_size[0]), &(cfg.win_size[1])); cfg.div_pos = gtk_paned_get_position (GTK_PANED (pane)); } extern gboolean trayicon_activate (EggStatusIcon *icon, gpointer user_data); void show_status_icon (const gchar *tooltip) { gboolean is_active; if (!cfg.use_tray_icon) return; g_object_get (MW("main_window"), "is-active", &is_active, NULL); if (is_active) { pur_log_dbg ("Won't show notification icon: main window already focused"); return; } if (!status_icon) { gchar *trayicon_fname = g_build_filename (pixmapdir, "pureadmin-16x16.png", NULL); if (!g_file_test (trayicon_fname, G_FILE_TEST_EXISTS)) { pur_log_err ("Couldn't find trayicon icon: %s", trayicon_fname); g_free (trayicon_fname); return; } status_icon = egg_status_icon_new_from_file (trayicon_fname); g_signal_connect (G_OBJECT (status_icon), "activate", G_CALLBACK (trayicon_activate), NULL); g_free (trayicon_fname); } egg_status_icon_set_tooltip (EGG_STATUS_ICON (status_icon), tooltip, NULL); } void hide_status_icon (void) { if (!cfg.use_tray_icon || !status_icon) return; g_object_unref (G_OBJECT(status_icon)); status_icon = NULL; } void send_notification (const gchar *summary, const gchar *body) { GError *err = NULL; gchar *icon = g_build_filename (pixmapdir, "pureadmin-32x32.png", NULL); gchar *cmd = g_strdup_printf ("notify-send \"--icon=%s\" --urgency=low \"%s\" \"%s\"", icon, summary, body); if (!g_spawn_command_line_async(cmd, &err)) { pur_log_wrn ("Couldn't send notification: %s", err->message); g_error_free (err); err = NULL; } g_free (icon); g_free (cmd); } /* Statusbar functions, FIXME? Should contex-id be taken into account?? */ void statusbar_push (const gchar *msg) { static GtkWidget *statusbar = NULL; gchar *utf8_msg; if (!statusbar) statusbar = MW("main_statusbar"); utf8_msg = string_to_utf8 (msg); gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, utf8_msg); g_free (utf8_msg); } void statusbar_pop (void) { static GtkWidget *statusbar = NULL; if (!statusbar) statusbar = MW("main_statusbar"); gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0); } void gui_select_treeview_row (GtkTreeIter *iter, GtkTreeView *tree) { GtkTreePath *path; GtkTreeModel *model; GtkTreeSelection *sel; if (!iter || !tree) return; model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree)); sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); gtk_tree_selection_select_iter (GTK_TREE_SELECTION (sel), iter); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree), path, NULL, TRUE, 0.4, 0); gtk_tree_path_free (path); return; } void gui_update_server_status (void) { GtkWidget *mnu_label, *mnu_item; gchar *msg = NULL, *lbl = NULL; switch (ftp_runmode) { case RUNMODE_STANDALONE: msg = g_strdup (_("PureFTPd running (standalone)")); lbl = g_strdup (_("_Stop Server")); break; case RUNMODE_INETD: msg = g_strdup (_("PureFTPd running (inetd)")); lbl = g_strdup (_("_Stop Server")); break; case RUNMODE_STOPPED: msg = g_strdup (_("PureFTPd not running")); lbl = g_strdup (_("_Start Server")); break; } statusbar_pop (); statusbar_push (msg); g_free (msg); mnu_item = MW("mi_server_startstop"); mnu_label = gtk_bin_get_child (GTK_BIN (mnu_item)); gtk_label_set_text_with_mnemonic (GTK_LABEL (mnu_label), lbl); g_free (lbl); if (!cfg.cmd_startstop || *cfg.cmd_startstop == '\0' || ftp_runmode == RUNMODE_INETD) gtk_widget_set_sensitive (mnu_item, FALSE); else gtk_widget_set_sensitive (mnu_item, TRUE); } /* Creates and displays the popup-menu for the activities */ void gui_display_activity_popup (popup_src_t source) { static GtkWidget *mnu = NULL; GtkWidget *item; if (!mnu) mnu = DW("menu_act_popup"); /* Not all menu-items should be clickable (depending on where the user popped-up the menu) */ item = DW("mnu_close_tx"); if (source == POPUP_FROM_USRTREE) gtk_widget_set_sensitive (item, FALSE); else if (source == POPUP_FROM_ACTTREE) gtk_widget_set_sensitive (item, TRUE); gtk_menu_popup (GTK_MENU(mnu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ()); } /* Appends a row to the activities list... */ void gui_add_to_activities (PGuiActivityRow line) { GtkTreeIter iter; static GtkWidget *tree_activities = NULL; static GtkTreeModel *model = NULL; if (!tree_activities) { tree_activities = MW("tree_activity"); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_activities)); } gtk_list_store_append (GTK_LIST_STORE (model), &iter); gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_ACT_ICON, line.icon, COL_ACT_TEXT, line.text, COL_ACT_ID, line.id, -1); } void gui_add_to_online_users (PGuiUserRow line) { GtkTreeIter iter; static GtkWidget *tree_users = NULL; static GtkTreeModel *model = NULL; if (!tree_users) { tree_users = MW("tree_users"); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_users)); } gtk_list_store_append (GTK_LIST_STORE (model), &iter); gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_USR_ICON, line.icon, COL_USR_USER, line.user, COL_USR_HOST, line.host, COL_USR_NUM_CONNECTIONS, line.num_connections, -1); } void gui_clear_activity_list (void) { static GtkWidget *tree_activities = NULL; static GtkTreeModel *model = NULL; if (!tree_activities) { tree_activities = MW("tree_activity"); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_activities)); } gtk_list_store_clear (GTK_LIST_STORE (model)); } void gui_clear_users_list (void) { static GtkWidget *tree_users = NULL; static GtkTreeModel *model = NULL; if (!tree_users) { tree_users = MW("tree_users"); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_users)); } gtk_list_store_clear (GTK_LIST_STORE (model)); } void gui_update_info_frame (PActivity *a) { static GtkWidget *w_name = NULL; static GtkWidget *w_host = NULL; static GtkWidget *w_action = NULL; static GtkWidget *w_progress = NULL; static GtkWidget *w_elapsed = NULL; static GtkWidget *w_eta = NULL; static GtkWidget *p_progress = NULL; static GtkWidget *hbox_progress = NULL; gchar *tmp, *num_cur_str, *num_tot_str, *speed_str, *percent; #define DATE_STR_LEN 50 tmp = num_cur_str = num_tot_str = speed_str = percent = NULL; if (G_UNLIKELY (!w_name)) { w_name = MW("lbl_username"); w_host = MW("lbl_host"); w_action = MW("lbl_action"); w_progress = MW("lbl_progress"); w_elapsed = MW("lbl_elapsed_time"); w_eta = MW("lbl_remaining_time"); p_progress = MW("prog_progress"); hbox_progress = MW("hbox_advinfo_progress"); } if (!a) { /* Nothing sent, clear labels and hide progress bar */ gtk_label_set_text (GTK_LABEL (w_name), ""); gtk_label_set_text (GTK_LABEL (w_host), ""); gtk_label_set_text (GTK_LABEL (w_action), ""); gtk_label_set_text (GTK_LABEL (w_progress), ""); gtk_label_set_text (GTK_LABEL (w_elapsed), ""); gtk_label_set_text (GTK_LABEL (w_eta), ""); gtk_widget_hide (p_progress); return; } gtk_label_set_text (GTK_LABEL (w_name), a->username); gtk_label_set_text (GTK_LABEL (w_host), a->remote_addr); tmp = g_strdup_printf ("%s '%s'", a->state == FTPWHO_STATE_DOWNLOAD ? _("Downloading") : _("Uploading"), a->filename); gtk_label_set_text (GTK_LABEL (w_action), tmp); g_free (tmp); num_cur_str = get_size_string (a->download_current_size); // number_separate (a->download_current_size); speed_str = gen_speed_string (a->speed); /* If it's an upload, then we don't know the total size. */ if (a->state == FTPWHO_STATE_DOWNLOAD) { gdouble fraction = (gdouble) a->download_current_size / (gdouble) a->download_total_size; percent = g_strdup_printf ("%.2lf", 100.0 * fraction); num_tot_str = get_size_string (a->download_total_size); /* Translators: This will be come 'xx MiB of yy MiB (pp%) nn KiB/s * where 'xx' is current size, yy is total size, pp is percentage and nn is the current speed */ tmp = g_strdup_printf (_("%s of %s (%s%%) %s"), num_cur_str, num_tot_str, percent, speed_str); gtk_progress_bar_set_text (GTK_PROGRESS_BAR(p_progress), tmp); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(p_progress), fraction); /* Show the progress bar instead of label */ gtk_widget_hide (w_progress); gtk_widget_show (p_progress); } else { /* Translators: This will become 'xx MiB at yy KiB/s' where xx is current * uploaded size and yy is the upload speed */ tmp = g_strdup_printf (_("%s at %s"), num_cur_str, speed_str); gtk_label_set_text (GTK_LABEL (w_progress), tmp); gtk_widget_hide (p_progress); gtk_widget_show (w_progress); } g_free (percent); g_free (num_cur_str); g_free (num_tot_str); g_free (speed_str); g_free (tmp); tmp = sec_to_time (a->online_since); gtk_label_set_text (GTK_LABEL (w_elapsed), tmp); if (a->speed == 0) tmp = g_strdup ("\342\210\236"); /* Infinity */ else if (a->download_total_size == 0) /* Translators: This refers to the time remaining of an up/download */ tmp = g_strdup_printf ("%s", _("Unknown")); else tmp = sec_to_time ((a->download_total_size - a->download_current_size) / a->speed); gtk_label_set_markup (GTK_LABEL (w_eta), tmp); g_free (tmp); } GtkWidget *gui_create_system_account_menu (GList *l) { GList *node; GtkWidget *menu, *menu_item; menu = gtk_menu_new (); node = g_list_first (l); while (node) { SystemAccount *a = (SystemAccount *) node->data; gchar *label = g_strdup_printf ("%s (%d)", a->name, a->id); menu_item = gtk_menu_item_new_with_label (label); g_free (label); g_object_set_data (G_OBJECT(menu_item), "account_id", GINT_TO_POINTER(a->id)); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); node = g_list_next (node); } gtk_widget_show_all (menu); return menu; } GtkWidget *gui_create_menu_from_glist (GList *l) { GList *node; GtkWidget *menu, *menu_item; menu = gtk_menu_new (); node = g_list_first (l); while (node) { // SystemAccount *a = (SystemAccount *) node->data; // gchar *label = g_strdup_printf ("%s (%d)", a->name, a->id); menu_item = gtk_menu_item_new_with_label ((gchar *) node->data); // g_free (label); // g_object_set_data (G_OBJECT(menu_item), "account_id", GINT_TO_POINTER(a->id)); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); node = g_list_next (node); } gtk_widget_show_all (menu); return menu; } struct _alert_dlg_info { const gchar *primary, *secondary; const gchar *icon_type; GtkWidget *parent; }; static GtkWidget *create_msgdialog (const struct _alert_dlg_info *info, gboolean show_chkbox) { GtkWidget *dlg_alert; GtkWidget *icon; GtkWidget *lbl; gchar *text; dlg_alert = DW("dlg_msg"); if (info->parent) gtk_window_set_transient_for (GTK_WINDOW (dlg_alert), GTK_WINDOW (info->parent)); icon = DW("dlg_msg_icon"); gtk_image_set_from_stock (GTK_IMAGE(icon), info->icon_type, GTK_ICON_SIZE_DIALOG); text = g_strdup_printf ("%s\n\n%s", info->primary, info->secondary); lbl = DW("dlg_msg_label"); gtk_label_set_markup (GTK_LABEL (lbl), text); g_free (text); gtk_widget_show_all (dlg_alert); if (!show_chkbox) gtk_widget_hide (DW("dlg_msg_chk")); return dlg_alert; } void gui_display_msgdialog (const gchar *primary, const gchar *secondary, const gchar *icon_type, GtkWidget *parent) { GtkWidget *dlg; struct _alert_dlg_info dlginfo; dlginfo.primary = primary; dlginfo.secondary = secondary; dlginfo.icon_type = icon_type; dlginfo.parent = parent; dlg = create_msgdialog (&dlginfo, FALSE); gtk_dialog_run (GTK_DIALOG (dlg)); gtk_widget_hide (dlg); } void gui_display_msgdialog_checkbox (const gchar *primary, const gchar *secondary, const gchar *icon_type, GtkWidget *parent, gboolean *show_again) { GtkWidget *dlg; struct _alert_dlg_info dlginfo; g_return_if_fail (show_again != NULL); dlginfo.primary = primary; dlginfo.secondary = secondary; dlginfo.icon_type = icon_type; dlginfo.parent = parent; dlg = create_msgdialog (&dlginfo, TRUE); gtk_dialog_run (GTK_DIALOG (dlg)); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (DW("dlg_msg_chk")))) *show_again = FALSE; else *show_again = TRUE; gtk_widget_hide (dlg); } static GtkWidget *create_imgbutton (const gchar *text, const gchar *icon_type) { GtkWidget *btn, *img, *lbl; GtkWidget *hbox, *alignment; btn = gtk_button_new (); alignment = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_container_add (GTK_CONTAINER (btn), alignment); hbox = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (alignment), hbox); img = gtk_image_new_from_stock (icon_type, GTK_ICON_SIZE_BUTTON); gtk_box_pack_start (GTK_BOX (hbox), img, FALSE, FALSE, 0); lbl = gtk_label_new_with_mnemonic (text); gtk_box_pack_start (GTK_BOX (hbox), lbl, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (lbl), GTK_JUSTIFY_LEFT); gtk_widget_show_all (btn); return btn; } /* Creates and runs a HIGified confirmation dialog asking the user to confirm something. * Args: * primary: The 'header' text, keep short and descriptive * secondary: The longer description of the action to confirm * txt_btn_yes: Text to use instead of 'Yes' (if NULL, then stockbutton YES is used) * txt_btn_no: Text to use instead of 'No' (if NULL, then stockbutton NO is used) * parent: The parent window, can be NULL to ignore parent. * * Returns: * the GTK_RESPONSE_ID as returned by gtk_dialog_run(): * GTK_RESPONSE_YES : User pressed 'yes' * GTK_RESPONSE_NO : User pressed 'no' */ gint gui_display_confirmdialog (const gchar *primary, const gchar *secondary, const gchar *txt_btn_yes, const gchar *txt_btn_no, GtkWidget *parent) { GtkWidget *dlg_alert; GtkWidget *lbl; GtkWidget *btn_no, *btn_yes; gchar *text; gint ret; dlg_alert = DW("dlg_confirm"); if (parent) gtk_window_set_transient_for (GTK_WINDOW (dlg_alert), GTK_WINDOW (parent)); text = g_strdup_printf ("%s\n\n%s", primary, secondary); lbl = DW("dlg_confirm_label"); gtk_label_set_markup (GTK_LABEL (lbl), text); g_free (text); if (txt_btn_no == NULL) btn_no = gtk_button_new_from_stock (GTK_STOCK_NO); else btn_no = create_imgbutton (txt_btn_no, GTK_STOCK_NO); gtk_dialog_add_action_widget (GTK_DIALOG (dlg_alert), btn_no, GTK_RESPONSE_NO); GTK_WIDGET_SET_FLAGS (btn_no, GTK_CAN_DEFAULT); if (txt_btn_yes == NULL) btn_yes = gtk_button_new_from_stock (GTK_STOCK_YES); else btn_yes = create_imgbutton (txt_btn_yes, GTK_STOCK_YES); gtk_dialog_add_action_widget (GTK_DIALOG (dlg_alert), btn_yes, GTK_RESPONSE_YES); GTK_WIDGET_SET_FLAGS (btn_yes, GTK_CAN_DEFAULT); gtk_widget_show_all (dlg_alert); ret = gtk_dialog_run (GTK_DIALOG (dlg_alert)); gtk_widget_hide (dlg_alert); gtk_widget_destroy (btn_no); gtk_widget_destroy (btn_yes); return ret; } static gchar pixmap_directory[FILEPATH_MAX]; /* Use this function to set the directory containing installed pixmaps. */ void set_pixmap_directory (const gchar *directory) { strncpy (pixmap_directory, directory, FILEPATH_MAX); } /* This is an internally used function to create pixmaps. */ GdkPixbuf* create_pixbuf (const gchar *filename) { gchar *pathname = NULL; GdkPixbuf *pixbuf; GError *error = NULL; if (!filename || !filename[0]) return NULL; pathname = g_strdup_printf ("%s%s%s", pixmap_directory, G_DIR_SEPARATOR_S, filename); if (!g_file_test (pathname, G_FILE_TEST_EXISTS)) { g_warning ("Couldn't find pixmap file: %s", filename); g_free (pathname); return NULL; } pixbuf = gdk_pixbuf_new_from_file (pathname, &error); if (!pixbuf) { g_warning ("Failed to load pixbuf file: %s: %s\n", pathname, error->message); g_error_free (error); } g_free (pathname); return pixbuf; } void show_not_yet_implemented (GtkWindow *parent) { GtkWidget *dlg = gtk_message_dialog_new (parent, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "The requested feature is not yet implemented"); gtk_dialog_run (GTK_DIALOG(dlg)); gtk_widget_destroy (dlg); }