/* 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 <config.h>
#endif
#include <gtk/gtk.h>
#include <time.h>
#include <string.h>
#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 ("<i>%s</i>", _("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 ("<span weight=\"bold\" size=\"larger\">%s</span>\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 ("<span weight=\"bold\" size=\"larger\">%s</span>\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);
}
syntax highlighted by Code2HTML, v. 0.9.1