
/*
 * $Header: /cvs/gnome/gperfmeter/src/perfmeter.c,v 1.58 2005/03/11 22:07:43 richb Exp $
 *
 * Copyright (c) 1990-2004 Sun Microsystems, Inc. All Rights Reserved.
 *
 * 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, 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.
 *
 */

#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <locale.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "callbacks.h"
#include "access/factory.h"
#include "perfmeter.h"

#ifdef SOLARIS
#include <sys/systeminfo.h>
#else
#include <sys/utsname.h>
#endif /*SOLARIS*/

#include <X11/keysym.h>
#include <X11/cursorfont.h>

enum {
    M_CPU = 0, M_LOAD,     M_DISK,  M_PAGE, M_CNTX, 
    M_SWAP,    M_INTR,     M_PKTS,  M_COLL, M_ERRS, 
    M_EXIT,    M_BAR,      M_STRIP, M_BOTH, M_MBAR, 
    M_SETUP,   M_CONTENTS, M_ABOUT
};

static gchar *meter_actions[10] = {
    "CPU", "Load", "Disk", "Page", "Context", "JobSwaps", "Interrupts",
    "Packets", "Collisions", "Errors"
};
static void show_icons(gboolean);
static void menuitem_icon_visibility_notify(GConfClient *, guint,
                                            GConfEntry *, gpointer);

static void gperfmeter_set_atk_relation(GtkWidget *, 
                                        GtkWidget *, AtkRelationType);
static void create_MainWin(void);
static void create_PopupMenu(void);
static GdkPixbuf *create_pixbuf(gchar *, unsigned int *, unsigned int *);
static void mb_chart_radio_proc(GtkAction *, GtkRadioAction *);
static void set_gperfmeter_icon(void);
static GtkWidget *get_ui_widget(const char *);
static void menu_handler(GtkAction *);
static gboolean DismissPerfmeterCB(GtkWidget *, GdkEvent *, gpointer);
static gboolean MainWinKeyHandler(GtkWidget *, GdkEventKey *, gpointer);
static gboolean WindowHandler(GtkWidget *, GdkEventProperty *, gpointer);
static gboolean CanvasExpose(GtkWidget *, GdkEventExpose *, gpointer);
static gboolean CanvasButtonHandler(GtkWidget *, GdkEventButton *, gpointer);
static gboolean CanvasResize(GtkWidget *, GdkEventConfigure *, gpointer);
static gboolean WindowConfigure(GtkWidget *, GdkEventConfigure *, gpointer);
static void AdjustMenuBar(void);
static void ShowMeter(int, gboolean);
static void HideLiveActionBar(void);
static void HideStripChart(void);
static void PmShowHelp(void);
static void ShowBothChart(void);
static void style_set_callback(GtkWidget *);
static void ToggleMBWidget(gboolean);

static gint gperfmeter_accessible_get_n_children(AtkObject *);
static AtkObject *gperfmeter_accessible_ref_child(AtkObject *, gint);

static gboolean check_flag = FALSE;


/* Array for holding the AtkObject structures for the children */
AtkObject *pango_accessible[PmMAX_METERS];

GConfValue *GetGConfResource(char *, char *, char *);

void PutResource(char *, char *, char *, ResType, gpointer);


/* Menubar menu and popup menu entries. */

static GtkActionEntry entries[] = {
    { "FileMenu",       NULL, N_("_File") },
    { "EditMenu",       NULL, N_("_Edit") },
    { "ViewMenu",       NULL, N_("_View") },
    { "MetricsMenu",    NULL, N_("_Metrics") },
    { "HelpMenu",       NULL, N_("_Help") },

    { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q",
      N_("Quit the performance meter"), G_CALLBACK(menu_handler) },

    { "Preferences", GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL,
      N_("Show preferences panel"), G_CALLBACK(menu_handler) },

    { "Contents", GTK_STOCK_HELP, N_("_Contents"), "F1",
      N_("Show help contents"), G_CALLBACK(menu_handler) },
    { "About", GNOME_STOCK_ABOUT, N_("_About"), NULL,
      N_("Show the About Gperfmeter dialog"), G_CALLBACK(menu_handler) },
};
static guint n_entries = G_N_ELEMENTS(entries);

static GtkToggleActionEntry toggle_entries[] = {
    { "CPU",  NULL, N_("_CPU"),               "<control>1",
      N_("Show CPU metrics"),           G_CALLBACK(menu_handler), FALSE },
    { "Load",  NULL, N_("_Load"),             "<control>2",
      N_("Show Load metrics"),          G_CALLBACK(menu_handler), FALSE },
    { "Disk",  NULL, N_("_Disk"),             "<control>3",
      N_("Show Disk metrics"),          G_CALLBACK(menu_handler), FALSE },
    { "Page",  NULL, N_("_Page"),             "<control>4",
      N_("Show Page metrics"),          G_CALLBACK(menu_handler), FALSE },
    { "Context",  NULL, N_("C_ontext"),       "<control>5",
      N_("Show Context metrics"),       G_CALLBACK(menu_handler), FALSE },
    { "JobSwaps",  NULL, N_("_Job Swaps"),    "<control>6",
      N_("Show Job Swap metrics"),      G_CALLBACK(menu_handler), FALSE },
    { "Interrupts",  NULL, N_("_Interrupts"), "<control>7",
      N_("Show Interrupt metrics"),     G_CALLBACK(menu_handler), FALSE },
    { "Packets",  NULL, N_("Pac_kets"),       "<control>8",
      N_("Show Packet metrics"),        G_CALLBACK(menu_handler), FALSE },
    { "Collisions",  NULL, N_("Collisio_ns"), "<control>9",
      N_("Show Collision metrics"),     G_CALLBACK(menu_handler), FALSE },
    { "Errors",  NULL, N_("_Errors"),         "<control>0",
      N_("Show Error metrics"),         G_CALLBACK(menu_handler), FALSE },

    { "Menubar", NULL, N_("Men_u Bar"), "<control>M",
      N_("Show menu bar"), G_CALLBACK(menu_handler), TRUE },
};
static guint n_toggle_entries = G_N_ELEMENTS(toggle_entries);

static GtkRadioActionEntry chart_radio_entries[] = {
  { "BarChart",    NULL, N_("_Bar Chart"),   NULL,
    N_("Show bar chart"),    M_BAR },
  { "StripChart",  NULL, N_("_Strip Chart"), NULL,
    N_("Show strip chart"),  M_STRIP },
  { "BothCharts",  NULL, N_("B_oth Charts"), NULL,
    N_("Show both charts"),  M_BOTH },
};
static guint n_chart_radio_entries = G_N_ELEMENTS(chart_radio_entries);

PmUIStruct *UI;
PmStruct *PM;
static PangoLayout *layout;
static int doingShowMeter = FALSE;

static guint notify;
static GConfClient *client = NULL;

static void Update(void);
static void CalculateRC(void);
static void DrawGraph(int, gboolean);
static void DrawDead(void);
static void Initialize(void);
static void SetColorMode(void);
static void PostInitialize(void);
static void CreateInterface(void);
static void PmShutdown(void);
static void ResetPopupMenu(void);
static void PmSizeGraphArea(int, int);
static void PmActivate(gboolean);

/* LAB = Live Action Bar */
#define PmSEPARATOR_THICKNESS 2
#define PmLAB_SEPARATOR       5
#define PmLAB_WIDTH           16
#define PmHALF_LAB_WIDTH      8      /* Half the size LAB_WIDTH */

/* Inter-client communications */
static GdkAtom XA_WM_STATE;

static const gchar *ui_info =
"<ui>"
"  <menubar name='MenuBar'>"
"    <menu action='FileMenu'>"
"      <menuitem action='Quit'/>"
"    </menu>"
"    <menu action='EditMenu'>"
"      <menuitem action='Preferences'/>"
"    </menu>"
"    <menu action='ViewMenu'>"
"      <menu action='MetricsMenu'>"
"        <menuitem action='CPU'/>"
"        <menuitem action='Load'/>"
"        <menuitem action='Disk'/>"
"        <separator/>"
"        <menuitem action='Page'/>"
"        <menuitem action='Context'/>"
"        <menuitem action='JobSwaps'/>"
"        <separator/>"
"        <menuitem action='Interrupts'/>"
"        <menuitem action='Packets'/>"
"        <menuitem action='Collisions'/>"
"        <menuitem action='Errors'/>"
"      </menu>"
"      <menuitem action='BarChart'/>"
"      <menuitem action='StripChart'/>"
"      <menuitem action='BothCharts'/>"
"      <separator/>"
"      <menuitem action='Menubar'/>"
"    </menu>"
"    <menu action='HelpMenu'>"
"      <menuitem action='Contents'/>"
"      <menuitem action='About'/>"
"    </menu>"
"  </menubar>"
"  <popup name='PopupMenu'>"
"    <menuitem action='CPU'/>"
"    <menuitem action='Load'/>"
"    <menuitem action='Disk'/>"
"    <separator/>"
"    <menuitem action='Page'/>"
"    <menuitem action='Context'/>"
"    <menuitem action='JobSwaps'/>"
"    <separator/>"
"    <menuitem action='Interrupts'/>"
"    <menuitem action='Packets'/>"
"    <menuitem action='Collisions'/>"
"    <menuitem action='Errors'/>"
"    <separator/>"
"    <menuitem action='BarChart'/>"
"    <menuitem action='StripChart'/>"
"    <menuitem action='BothCharts'/>"
"    <separator/>"
"    <menuitem action='Menubar'/>"
"    <menuitem action='Preferences'/>"
"  </popup>"
"</ui>";


int 
main(int argc, char **argv)
{
    int i, pargc;
    char **pargv;

    SETLOCALE(LC_ALL, "");
    BINDTEXTDOMAIN(GETTEXT_PACKAGE, GNOMELOCALEDIR);
    TEXTDOMAIN(GETTEXT_PACKAGE);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
    BIND_TEXTDOMAIN_CODESET(GETTEXT_PACKAGE, "UTF-8");
#endif

    /* Copy the command arguments before the call to 
     * gnome_init_with_popt_table() in ParseCmdOptions().
     */
    pargc = argc;
    pargv = g_malloc0((argc+1) * sizeof(char *));
    for (i = 0; i < argc; i++) {
        pargv[i] = argv[i];
    }

    /* Initialize the perfmeter data structures */
    Initialize();

    /* Save the original command line arguments */
    UI->Argc = argc;
    UI->Argv = argv;

    /* Initialize and load the application resources, plus parse cmd line. */
    UI->initializing = TRUE;
    PmLoadResources(pargc, pargv);
    g_free(pargv);
    XA_WM_STATE = gdk_atom_intern("WM_STATE", FALSE);

    PmLock();

    /* Set the color mode */
    SetColorMode();

    /* Create the main window */
    create_MainWin();

    UI->DeadPM = create_pixbuf(GPERF_PNGDIR "/dead.png", 
                               &PM->DeadWp, &PM->DeadHp);
    UI->SickPM = create_pixbuf(GPERF_PNGDIR "/sick.png",
                               &PM->SickWp, &PM->SickHp);

    /* Calculate numbers of rows and columns */
    CalculateRC();

    set_gperfmeter_icon();        /* Set the icon for the main window. */

    /* Create the canvas popup menu. */
    create_PopupMenu();
 
    /* Set the mininimum canvas size */
    PmSetMinSize();

    PmShow(TRUE);

    CreateInterface();

    /* Start to monitor */
    if (!PmMonitor()) {
        GtkWidget *dialog;

        dialog = gtk_message_dialog_new(NULL,
                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                     GTK_MESSAGE_ERROR,
                     GTK_BUTTONS_CLOSE,
                     (" Please enable rstatd. "));

        gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy(dialog);
        return(1);
    }
    
    PmUnLock();
    ValidateStripBarMenu();
    PostInitialize();
    UI->initializing = FALSE;
    setup_factory();
    gtk_main();

    PmSignalHandler(0);
    return(0);
}


static GdkPixbuf *
create_pixbuf(gchar *name, unsigned int *width, unsigned int *height)
{
    GError *error = NULL;
    GdkPixbuf *pixbuf;

    pixbuf = gdk_pixbuf_new_from_file(name, &error);
    if (error) {
        g_warning("Could not load image: %s\n", error->message);
        g_error_free(error);
        error = NULL;
    }

    *width = 68;
    *height = 64;

    return(pixbuf);
}


static void
show_icons(gboolean use_image)
{
    GtkWidget *quit_image, *about_image, *pref_image, *help_image;

    quit_image = gtk_image_menu_item_get_image(
        GTK_IMAGE_MENU_ITEM(get_ui_widget("/MenuBar/FileMenu/Quit")));
    pref_image = gtk_image_menu_item_get_image
        (GTK_IMAGE_MENU_ITEM(get_ui_widget("/MenuBar/EditMenu/Preferences")));
    help_image = gtk_image_menu_item_get_image
        (GTK_IMAGE_MENU_ITEM(get_ui_widget("/MenuBar/HelpMenu/Contents")));
    about_image = gtk_image_menu_item_get_image
        (GTK_IMAGE_MENU_ITEM(get_ui_widget("/MenuBar/HelpMenu/About")));

    if (use_image) {
        gtk_widget_show(quit_image);
        gtk_widget_show(pref_image);
        gtk_widget_show(help_image);
        gtk_widget_show(about_image);
    } else {
        gtk_widget_hide(quit_image);
        gtk_widget_hide(pref_image);
        gtk_widget_hide(help_image);
        gtk_widget_hide(about_image);
    }
}


/*ARGSUSED*/
static void
menuitem_icon_visibility_notify(GConfClient *client, guint cnxn_id,
                                GConfEntry *entry, gpointer user_data)
{
    show_icons(gconf_value_get_bool(entry->value));
}


static void
SetBackingStoreInfo(int meter)
{
    XSetWindowAttributes swattr;
    XWindowAttributes wattr;
    GdkWindow *cw = gnome_perfmeter_get_chart_win(
                        GNOME_PERFMETER(UI->GMeters[meter]));

    /* Turn on backing store on the chart window */
    swattr.backing_store = WhenMapped;

    XChangeWindowAttributes(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(cw),
                            CWBackingStore, &swattr);

    /* Get the current canvas window attributes */
    XGetWindowAttributes(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(cw), &wattr);

    if (wattr.backing_store == NotUseful) {
        /* Turn off backingstore flag */
        UI->BackingStore = FALSE;
#ifdef DEBUG
        g_print("Backing Store is OFF\n");
#endif
    }
}


static void
MakeNewMeter(int n)
{
    gchar *buf;
    GtkWidget *chart;

    UI->GMeters[n] = gnome_perfmeter_new();
    gnome_perfmeter_set_maxval(GNOME_PERFMETER(UI->GMeters[n]),
                               PM->Meters[n].TRName);
    if (n == PM_CPU) {
        buf = g_strdup_printf("%1d%%", PM->Meters[n].CurMax);
    } else {
        buf = g_strdup_printf("%1d", PM->Meters[n].CurMax);
    }
    gnome_perfmeter_set_meter_type(GNOME_PERFMETER(UI->GMeters[n]), buf);
    gtk_widget_hide_all(UI->GMeters[n]);
    gtk_widget_ref(UI->GMeters[n]);

    chart = gnome_perfmeter_get_chart(GNOME_PERFMETER(UI->GMeters[n]));
    g_signal_connect(G_OBJECT(chart), "expose_event",
                     GTK_SIGNAL_FUNC(CanvasExpose), (gpointer) n);
    g_signal_connect(G_OBJECT(chart), "button_press_event",
                     GTK_SIGNAL_FUNC(CanvasButtonHandler), (gpointer) n);
    g_signal_connect(G_OBJECT(chart), "configure_event",
                     GTK_SIGNAL_FUNC(CanvasResize), (gpointer) n);
}


static void
AdjustMeterDisplay(int meter, int addMeter)
{
    GtkWidget *box;
    GtkWidget *GMeter = UI->GMeters[meter];

    if (PM->Orientation == PM_VERTICAL) {
        box = UI->MeterVBox;
        gnome_perfmeter_show_vsep(GNOME_PERFMETER(GMeter), (meter > 0));
    } else {
        box = UI->MeterHBox;
        gnome_perfmeter_show_hsep(GNOME_PERFMETER(GMeter), (meter > 0));
    }

    if (addMeter == TRUE) {
        gtk_box_pack_start(GTK_BOX(box), GMeter, TRUE, TRUE, 0);
        gtk_widget_show_all(GMeter);
    } else {
        gtk_container_remove(GTK_CONTAINER(box), GMeter);
    }
}


void
ToggleDirection(int newDir)
{
    int i, meter;

    if (PM->Orientation != newDir) {
        for (i = 0; i < PM->NMeters; i++) {
	    meter = PM->DisplayList[i];
            AdjustMeterDisplay(meter, FALSE);
        }
        gtk_widget_hide(newDir == PM_VERTICAL ? UI->MeterHBox : UI->MeterVBox);

        PM->Orientation = newDir;

        for (i = 0; i < PM->NMeters; i++) {
	    meter = PM->DisplayList[i];
            AdjustMeterDisplay(meter, TRUE);
        }
        gtk_widget_show(newDir == PM_VERTICAL ? UI->MeterVBox : UI->MeterHBox);
    }
}


static void
create_MainWin(void)
{
    AtkObject *access_object;
    AtkObjectClass *access_object_class;
    GConfValue *xgptr, *ygptr, *wgptr, *hgptr;
    gboolean use_image;
    GError *error = NULL;
    int i, x, y, width, height;

    UI->MainWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    UI->accel = gtk_accel_group_new();
    g_signal_connect(G_OBJECT(UI->MainWin), "key_press_event",
                     GTK_SIGNAL_FUNC(MainWinKeyHandler), NULL);
    g_signal_connect(G_OBJECT(UI->MainWin), "delete_event",
                     GTK_SIGNAL_FUNC(DismissPerfmeterCB), NULL);
    g_signal_connect(G_OBJECT(UI->MainWin), "property_notify_event",
                     GTK_SIGNAL_FUNC(WindowHandler), NULL);

    /* This gets your window automatically resizing down on hiding of its
     * parts.
     */
    gtk_window_set_resizable(GTK_WINDOW(UI->MainWin), TRUE);
    gtk_window_set_default_size(GTK_WINDOW(UI->MainWin), 250, -1);

    /* Get the initial position of the gperfmeter window. */
    if ((xgptr = GetGConfResource(NULL, NULL, "WindowXPos"))) {
        x = gconf_value_get_int(xgptr);
    }

    if ((ygptr = GetGConfResource(NULL, NULL, "WindowYPos"))) {
        y = gconf_value_get_int(ygptr);
    }
    if (xgptr && ygptr) {
        gtk_window_move(GTK_WINDOW(UI->MainWin), x, y);
    }

    /* Get the initial width/height of the gperfmeter window. */
    if ((wgptr = GetGConfResource(NULL, NULL, "WindowWidth"))) {
	width = gconf_value_get_int(wgptr);
    }

    if ((hgptr = GetGConfResource(NULL, NULL, "WindowHeight"))) {
	height = gconf_value_get_int(hgptr);
    }
    if (wgptr && hgptr) {
        gtk_window_set_default_size(GTK_WINDOW(UI->MainWin), width, height);
    }

    /* Main vbox */
    UI->VBox = gtk_vbox_new(FALSE, 0);
    gtk_widget_show(UI->VBox);
    gtk_container_add(GTK_CONTAINER(UI->MainWin), UI->VBox);

    UI->actions = gtk_action_group_new("Actions");
    gtk_action_group_set_translation_domain(UI->actions, NULL);
    gtk_action_group_add_actions(UI->actions, entries, n_entries, NULL);
    gtk_action_group_add_toggle_actions(UI->actions,
                                        toggle_entries, n_toggle_entries,
                                        NULL);
    gtk_action_group_add_radio_actions(UI->actions,
                             chart_radio_entries, n_chart_radio_entries,
                             M_BOTH, G_CALLBACK(mb_chart_radio_proc),
                             NULL);

    UI->ui = gtk_ui_manager_new();
    gtk_ui_manager_insert_action_group(UI->ui, UI->actions, 0);
    gtk_window_add_accel_group(GTK_WINDOW(UI->MainWin),
                               gtk_ui_manager_get_accel_group(UI->ui));

    if (!gtk_ui_manager_add_ui_from_string(UI->ui, ui_info, -1, &error)) {
        g_message("Building menus failed: %s", error->message);
        g_error_free(error);
    }

    UI->MenuBar = get_ui_widget("/MenuBar"),

    client = gconf_client_get_default();
    error = NULL;
    use_image = gconf_client_get_bool(client,
                    "/desktop/gnome/interface/menus_have_icons",&error);
    if (error) {
        g_printerr(_("There was an error loading config value for whether to use image in menus. (%s)\n"), error->message);
        g_error_free(error);
    } else {
       show_icons(use_image);
    }

    notify = gconf_client_notify_add(client, 
                 "/desktop/gnome/interface/menus_have_icons",
                 menuitem_icon_visibility_notify, NULL, NULL, &error);
    if (error) {
        g_printerr(_("There was an error subscribing to notification of menu icon visibilty changes. (%s)\n"), error->message);
        g_error_free(error);
    }

    gtk_widget_show(UI->MenuBar);
    gtk_box_pack_start(GTK_BOX(UI->VBox), UI->MenuBar, FALSE, FALSE, 0);

    UI->MeterHBox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(UI->VBox), UI->MeterHBox, TRUE, TRUE, 0);
    if (PM->Orientation == PM_HORIZONTAL) {
        gtk_widget_show(UI->MeterHBox);
    }

    UI->MeterVBox = gtk_vbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(UI->VBox), UI->MeterVBox, TRUE, TRUE, 0);
    if (PM->Orientation == PM_VERTICAL) {
        gtk_widget_show(UI->MeterVBox);
    }

    for (i = 0; i < PmMAX_METERS; i++) {
        MakeNewMeter(i);
    }

    g_signal_connect(G_OBJECT(UI->MeterHBox), "button_press_event",
                     GTK_SIGNAL_FUNC(CanvasButtonHandler), NULL);
    g_signal_connect(G_OBJECT(UI->MeterVBox), "button_press_event",
                     GTK_SIGNAL_FUNC(CanvasButtonHandler), NULL);

    UI->HostName = gtk_label_new(NULL);
    if (PM->ShowHostName) {
        gtk_widget_show(UI->HostName);
    } else {
        gtk_widget_hide(UI->HostName);
    }
    gtk_box_pack_end(GTK_BOX(UI->VBox), UI->HostName, FALSE, FALSE, 0);

    set_hostname();

    for (i = 0; i < PM->NMeters; i++) {
	int meter = PM->DisplayList[i];

        AdjustMeterDisplay(meter, TRUE);
    }

    access_object = gtk_widget_get_accessible(UI->VBox);

    atk_object_set_name(access_object, _("Container VBox"));

    access_object_class = ATK_OBJECT_GET_CLASS(access_object);
    access_object_class->get_n_children = gperfmeter_accessible_get_n_children;
    access_object_class->ref_child = gperfmeter_accessible_ref_child;

    g_signal_connect(G_OBJECT(UI->VBox), "style_set",
                     G_CALLBACK(style_set_callback), UI->VBox);
    g_signal_connect(G_OBJECT(UI->MainWin), "configure_event",
                     G_CALLBACK(WindowConfigure), NULL);

    gtk_widget_realize(UI->MainWin);

    gperfmeter_set_atk_relation(get_ui_widget("/MenuBar/ViewMenu/Menubar"), 
                                UI->MenuBar, ATK_RELATION_CONTROLLER_FOR);
    gperfmeter_set_atk_relation(UI->MenuBar,
                                get_ui_widget("/MenuBar/ViewMenu/Menubar"), 
                                ATK_RELATION_CONTROLLED_BY);
    /* This will listen for our menubar keys and stuff */
    gtk_window_add_accel_group(GTK_WINDOW(UI->MainWin), UI->accel);
}


/*ARGSUSED*/
static void
mb_chart_radio_proc(GtkAction *action, GtkRadioAction *current)
{
    gint val;

    if (UI->initializing) {
        return;
    }

    val = gtk_radio_action_get_current_value(GTK_RADIO_ACTION(action));

    if (val == M_BAR) {
        HideLiveActionBar();
    } else if (val == M_STRIP) {
        HideStripChart();
    } else {
        ShowBothChart();
    }
}


static void
create_PopupMenu()
{
    UI->PopupMenu = get_ui_widget("/PopupMenu");
    gtk_container_set_border_width(GTK_CONTAINER(UI->PopupMenu), 1);
}


GtkWidget *
get_ui_widget(const char *path)
{
    return(gtk_ui_manager_get_widget(UI->ui, path));
}


/* Callback to handle delete events for the "About Perfmeter" popup window. */
 
/*ARGSUSED*/
gboolean
DismissAbout(GtkWidget *widget, GdkEvent  *event, gpointer  user_data)
{
    UI->Aboutdialog = NULL;
 
    return(FALSE);
}


static void
AboutPerf(void)
{
    static GtkWidget *about = NULL;
    gchar *authors[] = {
                   "Rich Burridge <rich.burridge@sun.com>",
                   "Ryan Lovett <ryan@ocf.berkeley.edu>",
                   NULL
    };
    gchar *documenters[] = {
                   NULL
    };

    /* Translator credits */
    gchar *translator_credits = _("translator_credits");

    PmBusy(TRUE);
    if (about != NULL)
    {
        gdk_window_show(about->window);
        gdk_window_raise(about->window);
        PmBusy(FALSE);
        return;
    }
    about = gnome_about_new(_("The Performance Meter"), VERSION,
                            "(C) 1998 the Free Software Foundation",
                            _("Monitor disk, memory, CPU and network usage"),
                            (const char **) authors,
                            (const char **) documenters,
                            strcmp(translator_credits, "translator_credits") !=0 ? translator_credits : NULL, NULL);
    g_signal_connect(G_OBJECT(about), "destroy",
                     G_CALLBACK(gtk_widget_destroyed), &about);
    gtk_widget_show(about);

    PmBusy(FALSE);
}


/* Handle all the various menu items. */

/*ARGSUSED*/
void
menu_handler(GtkAction *widget)
{
    gboolean set;
    gint i;
    const gchar *name = gtk_action_get_name(widget);
    gchar *meter;
    GtkAction *action;

/* We don't want to do stupid things while we are setting defaults. */
    if (UI->initializing) {
        return;
    }

    if (EQUAL(name, "Quit")) {
        PmShutdown();

    } else if (EQUAL(name, "Menubar")) {

        /* This is the *ONLY* place where you should call AdjustMenuBar
         * because everything else simply toggles the checkbutton in the
         * menu, causing it to come here and do its job.
         */
        AdjustMenuBar();

    } else if (EQUAL(name, "Preferences")) {
        PmShowProps();

    } else if (EQUAL(name, "Contents")) {
        PmShowHelp();

    } else if (EQUAL(name, "About")) {
        AboutPerf();
    } else {
        /* If we are a meter action, show or hide the meter */
        for (i = 0; i < PmMAX_METERS; i++) {
            /* Get the name of meter i */
            meter = meter_actions[i];
            /* Are we the menu item chosen ? */
            if (g_ascii_strcasecmp(name, meter) == 0) {
                /* Did the user enable or disable us */
                action = gtk_action_group_get_action(UI->actions, meter);
                set = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
                /* Show or hide the meter */
                ShowMeter(i, set);
                /* Only one item can be checked at a time */
                break;
            }
        }
    }

    PmSaveResources();    
}


/* Initialize the application data structures . */

static void 
Initialize()
{
#ifndef SOLARIS
    struct utsname utsbuf;
#endif /*SOLARIS*/
    char buf[1000];
    int count;

    /* Perfmeter resource structure */
    PM = g_new0(PmStruct, 1);

    /* Set the application class name */
    PM->AppClass = "Gperfmeter";
    PM->AppName  = "gnome-perfmeter";

    /* Perfmeter meter data structures */
    PM->Meters = g_new0(PmMeterStruct, PmMAX_METERS);

    /* Perfmeter host data structure */
    PMD = g_new0(PmDataStruct, 1);

    /* Interface data structure */
    UI = g_new0(PmUIStruct, 1);

    /* Interface meter data structures */
    UI->Meters = g_new0(PmUIMeterStruct, PmMAX_METERS);

    /* Size & reset the meter data cache */
    PmResizeMeterData(500);
    PmResetMeterData();

    /* Get the name of the current host */
    buf[0] = '\0';
#ifdef SOLARIS
    count = sysinfo(SI_HOSTNAME, buf, 1000);
#else
    uname(&utsbuf);
    STRCPY(buf, utsbuf.nodename);
    count = strlen(buf);
#endif /*SOLARIS*/

    PM->LocalMachine = g_strndup(buf, count);
    PM->ParentPID = getpid();
}


static void 
SetColorMode()
{
    GdkVisual *visual;

    visual = gdk_visual_get_system();
    if ((visual->depth > 1) &&
        ((visual->type == GDK_VISUAL_PSEUDO_COLOR) ||
         (visual->type == GDK_VISUAL_STATIC_COLOR) ||
         (visual->type == GDK_VISUAL_TRUE_COLOR) ||
         (visual->type == GDK_VISUAL_DIRECT_COLOR))) {
        UI->UseColor = TRUE;
    } else {
        /* staticgray and grayscale */
        UI->UseColor = FALSE;
    }

#ifdef DEBUG
    g_print("Use Color = %d\n", UI->UseColor);
#endif
}


static void 
PostInitialize()
{
    GnomePerfmeter *GMeter = GNOME_PERFMETER(UI->GMeters[PM->DisplayList[0]]);
    int i, state;
    gint width, height;

    /* Set the signal handlers */
    PmSetSignalHandlers();

    /* Set the backing store flag */
    state = DoesBackingStore(UI->RScreen);

    if (state == WhenMapped || state == Always) {
        UI->BackingStore = TRUE;
#ifdef DEBUG
        g_print("Backing Store is ON\n");
#endif
    }
    else {
        UI->BackingStore = FALSE;
#ifdef DEBUG
        g_print("Backing Store is OFF\n");
#endif
    }

    if (UI->BackingStore) {
        for (i = 0; i < PM->NMeters; i++) {
    	    int meter = PM->DisplayList[i];

            SetBackingStoreInfo(meter);
        }
    }

    UI->WaitCursor = gdk_cursor_new(XC_watch);

    /* Set the meter colors */
    for (i = 0; i < PM->NMeters; i++) {
        PmSetMeterColor(PM->DisplayList[i]);
    }

    /* Set the graph type */
    PmSetGraphType(PM->GraphType);

    /* Calculate the graph size based on the canvas size */
    width = gnome_perfmeter_get_chart_width(GMeter);
    height = gnome_perfmeter_get_chart_height(GMeter);

    if (width < 10 || height < 10) {
        /* We assume no canvas size was specified */
        if (PM->ShowMenubar == TRUE) {
            int mwidth = UI->MenuBar->allocation.width;

            /* So that menubar doesn't appear squished */
            PM->AreaWp = mwidth;
        } else {
            PM->AreaWp = 64;
        }
        PM->AreaHp = 64;
#if DEBUG
        g_print("What no canvas size specified!\n");
#endif
    } else {
        PmSizeGraphArea(width, height);
    }

    ResetPopupMenu();
    PmSaveCommand();
}


/* Create user interface. Ie. do all the things that Glade can't. */

static void 
CreateInterface()
{
    GnomePerfmeter *GMeter;
    GdkWindow *cw;

    /* There will always be at least one meter in the display list. */
    GMeter = GNOME_PERFMETER(UI->GMeters[PM->DisplayList[0]]);
    cw = gnome_perfmeter_get_chart_win(GMeter);

/* This doesn't do the callback so we have to call ToggleMBWidget() to 
 * hide/show it.
 */

    ToggleMenuBar(PM->ShowMenubar);
    ToggleMBWidget(PM->ShowMenubar);

    UI->FgPixel = GMeter->chart->style->fg[GTK_STATE_NORMAL];
    UI->BgPixel = GMeter->chart->style->bg[GTK_STATE_NORMAL];

    /* Sets Label and Canvas Color. */
    PM->CanvasColor = UI->BgPixel;
    PM->LabelColor =  UI->FgPixel;

    UI->CanvasGC = gdk_gc_new(cw);
    gdk_gc_set_exposures(UI->CanvasGC, TRUE);

    UI->FillGC = gdk_gc_new(cw);
    gdk_gc_set_fill(UI->FillGC, GDK_SOLID);

    PmSetColors();
    gperfmeter_set_atk_relation(get_ui_widget("/PopupMenu/Menubar"),
                                UI->MenuBar, ATK_RELATION_CONTROLLER_FOR);
    gperfmeter_set_atk_relation(UI->MenuBar,
                                get_ui_widget("/PopupMenu/Menubar"),
                                ATK_RELATION_CONTROLLED_BY);

    UI->UICreated = TRUE;
}


/*ARGSUSED*/
static gboolean
CanvasExpose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
    DrawGraph((int) user_data, TRUE);

    return(FALSE);
}


/* Handle configure events on the meter drawing canvas. */

/*ARGSUSED*/
static gboolean
CanvasResize(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
{
    gint width  = widget->allocation.width;
    gint height = widget->allocation.height;

    if (UI->UICreated == FALSE) {
        return(TRUE);
    }

    PmLock();

#ifdef DEBUG
    g_print("Resize width = %d, height = %d\n", width, height);
#endif

    PmSetMinSize();
    PmSizeGraphArea(width, height);
    PmRefresh(TRUE);
    DrawGraph((int) user_data, TRUE);
    PmUnLock();

    return(TRUE);
}


/*ARGSUSED*/
static gboolean
WindowConfigure(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
{
    gint x, y;
    gint width	= widget->allocation.width;
    gint height = widget->allocation.height;

    gtk_window_get_position(GTK_WINDOW(widget), &x, &y);

    /* Save current x/y position of the gperfmeter window. */
    PutResource(NULL, NULL, "WindowXPos", RES_INT, GINT_TO_POINTER(x));
    PutResource(NULL, NULL, "WindowYPos", RES_INT, GINT_TO_POINTER(y));

    /* Save current width/height of the gperfmeter window. */
    PutResource(NULL, NULL, "WindowWidth", RES_INT, GINT_TO_POINTER(width));
    PutResource(NULL, NULL, "WindowHeight", RES_INT, GINT_TO_POINTER(height));

    return(FALSE);
}


/* Handle other events on the meter canvas. */

/*ARGSUSED*/
static gboolean
CanvasButtonHandler(GtkWidget *widget, GdkEventButton *event, 
                    gpointer user_data)
{
    if (event->button == 3) {
        gtk_menu_popup(GTK_MENU(UI->PopupMenu),
                       NULL, NULL, NULL, NULL, event->button, event->time);
    } else if (event->button == 1) {
        if (!PM->inFrontPanel) {
            if (!GTK_WIDGET_VISIBLE(UI->MenuBar)) {

                /* Not in front panel and menubar is hidden. Show menubar. */
                PM->ShowMenubar = TRUE;
                PmSetMinSize();
                ToggleMenuBar(PM->ShowMenubar);
                gtk_widget_map(UI->MenuBar);
                PmResetPropsDecor();
            }
        }
    }

    return(FALSE);
}


/* Show the specified meter. */

static void
ShowMeter(gint meter, gboolean set)
{
    if (doingShowMeter == FALSE) {
        doingShowMeter = TRUE;

#ifdef DEBUG
        g_print("ShowMeter: Set meter %d to %s\n", 
                meter, set ? "TRUE": "FALSE");
#endif

        AdjustMeterDisplay(meter, set);
        if (set) {
            SetBackingStoreInfo(meter);
            PmSetMeterColor(meter);
        }

        PmResetMeter(meter, set);
        PmPropsSetDisplayList();
        ResetPopupMenu();

        PmRedisplay();
        doingShowMeter = FALSE;
    }
}


static void
HideLiveActionBar(void)
{
    PM->ShowLiveActionBar = TRUE;
    PM->ShowStripChart = FALSE;
    PM->StripChartActivity = TRUE;
    PmSetMinSize();
    PmRedisplay();
    PmResetPropsDecor();
    PM->StripChartActivity = FALSE;
    ValidateStripBarMenu();
}


/* Hide strip chart. */

static void
HideStripChart(void)
{
    PM->ShowLiveActionBar = FALSE;
    PM->ShowStripChart = TRUE;
    PM->StripChartActivity = TRUE;
    PmSetMinSize();
    PmRedisplay();
    PmResetPropsDecor();
    PM->StripChartActivity = FALSE;
    ValidateStripBarMenu();
}

static void
ShowBothChart(void)
{
    PM->ShowLiveActionBar = TRUE;
    PM->ShowStripChart = TRUE;
    PM->StripChartActivity = TRUE;
    PmSetMinSize();
    PmRedisplay();
    PmResetPropsDecor();
    PM->StripChartActivity = FALSE;
    ValidateStripBarMenu();
}


/* This function needs a better name - but what this does is handles toggling
 * the menubar status. This is *the* function you want to call when you want
 * to do anything with the menubar. This will toggle the menu item which in
 * turn will generate a "toggled" callback which will go to
 * menu_handler(), M_MBAR, which will call ToggleMenuBar() which will call
 * AdjustMenuBar(), and this is what you want. Perhaps AdjustMenuBar can be
 * done away with and this function can take its name or something. Or code
 * from ToggleMenuBar can live in AdjustMenuBar and this function renamed to
 * ToggleMenuBar().
 */

void
ToggleMenuBar(int visible)
{
    GtkAction *action;

    /* Toggle the Menubar in View and popup menus */
    action = gtk_action_group_get_action(UI->actions, "Menubar");
    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), visible);
}


static void
ToggleMBWidget(gboolean makeVisible)
{
    if (makeVisible) {
        gtk_widget_show(UI->MenuBar);
    } else {
        gtk_widget_hide(UI->MenuBar);
    }
}


static void
AdjustMenuBar()
{
    if (GTK_WIDGET_VISIBLE(UI->MenuBar)) {
        PM->ShowMenubar = FALSE;
        PmSetMinSize();
        ToggleMenuBar(FALSE);
        ToggleMBWidget(FALSE);
    } else {
        PM->ShowMenubar = TRUE;
        PmSetMinSize();
        ToggleMenuBar(TRUE);
        ToggleMBWidget(TRUE);
    }
    PmResetPropsDecor();
}


/*ARGSUSED*/
static gboolean
DismissPerfmeterCB(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    g_object_unref(G_OBJECT(layout));
    PmShutdown();

    return(FALSE);
}


static void
PmShowHelp()
{
    GError *error = NULL;

    gnome_help_display("gnome-perfmeter", NULL, &error);
    if (error) {
        g_warning("help error: %s\n", error->message);
        g_error_free(error);
        error = NULL;
    }
}


/* Shutdown the application. */

static void 
PmShutdown()
{
    gtk_main_quit();
}


typedef struct _PropWMState {
    CARD32    state;
    BITS32    icon;
} PropWMState;

#define PROP_WM_STATE_ELEMENTS 2


/*ARGSUSED*/
static gboolean 
WindowHandler(GtkWidget *widget, GdkEventProperty *event, gpointer user_data)
{
    static gboolean setupShowing = FALSE;

    if (event->atom == XA_WM_STATE) {
        PropWMState *property = NULL;
        Atom actual_type;
        int actual_format, ret_val;
        unsigned long nitems, leftover;

#ifdef DEBUG
        g_print("PropertyNotify WM_STATE Atom = %d, State = %d\n",
                event->atom, event->state);
#endif

        /* Get the property data */
        ret_val = XGetWindowProperty(GDK_DISPLAY(),
                                     GDK_WINDOW_XWINDOW(widget->window),
                                     gdk_x11_atom_to_xatom (XA_WM_STATE), 0L,
                                     PROP_WM_STATE_ELEMENTS, FALSE,
                                     gdk_x11_atom_to_xatom (XA_WM_STATE),
                                     &actual_type, &actual_format,
                                     &nitems, &leftover, 
                                     (unsigned char **) &property);
 
        if (!((ret_val == Success) &&
            (actual_type == gdk_x11_atom_to_xatom (XA_WM_STATE)) &&
            (nitems == PROP_WM_STATE_ELEMENTS))) {

            /* The property could not be retrieved or is not
             * correctly set up. Therefore, we will assume
             * there is no window manager running and do nothing.
             */
            if (property) {
                XFree((char *) property);
 
                return(FALSE);
            }
        } 

        /* Set the new display state */
        switch (property->state) {
            case NormalState:
                PM->Showing = TRUE;
                PmActivate(TRUE);
                PmRedisplay();

                /* Popup prop sheet if it was showing earlier */
                if (setupShowing) {
                    PmShowProps();
                }
                break;

            case WithdrawnState:
            break;

            case IconicState:
                PM->Showing = FALSE;
                if (PM->CollectWhenIconized == FALSE) {
                    PmActivate(FALSE);
                }
                setupShowing = PmHideProps();
                break;
        }
 
        /* Free the property */
        XFree((char *) property);
    }

    return(TRUE);
}


/* Reset the toggles in the view menu and the canvas popup menus to reflect 
 * the correct meter state.
 */

static void 
ResetPopupMenu()
{
    int i, j, match;
    GtkAction *action;

    if (!UI->PopupMenu) {
        return;
    }

#ifdef DEBUG
    g_print("ResetPopupMenu: ");
#endif
    for (i = 0; i < PmMAX_METERS; i++) {
        match = 0;
        for (j = 0; j < PM->NMeters; j++) {
            if (i == PM->DisplayList[j]) {
                match = 1;
                break;
            }
        }


        action = gtk_action_group_get_action(UI->actions, meter_actions[i]);
        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), match);

#ifdef DEBUG
        g_print("%d ", match);
#endif
    }
#ifdef DEBUG
    g_print("\n");
#endif

    gdk_flush();
}


/* Checks the three "Show" items on the option menu depending upon whether
 * these options are set.
 */

extern void 
ValidateStripBarMenu()
{
    GtkAction *action;

    /* Check_flag stops the recursive call */  
    check_flag = TRUE;
    
    if (PM->ShowLiveActionBar && PM->ShowStripChart) {
        action = gtk_action_group_get_action(UI->actions, "BothCharts");
    } else if (PM->ShowLiveActionBar) {
        action = gtk_action_group_get_action(UI->actions, "BarChart");
    } else {
        action = gtk_action_group_get_action(UI->actions, "StripChart");
    }

    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);

    check_flag = FALSE;    
}


/* Timer callback to get the next host datapoint and redisplay the meters. */

/*ARGSUSED*/
static gint
UpdateTimer(gpointer data)
{
    if (UI->TimerId == 0) {
        return(TRUE);
    }

    /* Update the meter data */
    if (!PM->IsDead) {
        if (PmUpdateData()) {
            PmScaleData();
        }

        Update();

        if (PM->Log) {
            PMLOGDATA();
        }
    }

    return(TRUE);
}


/* Interrupt timer callback to reset the meters. */

/*ARGSUSED*/
extern gint 
InterruptTimer(gpointer data)
{
    PmActivate(FALSE);
    PmActivate(TRUE);

    return(TRUE);
}


/* Set an interrupt timer to cause a full reset and redisplay. */

void 
PmSetRedisplayInterrupt()
{
    g_timeout_add_full(0, 0, InterruptTimer, NULL, NULL);
}


/* Update the meter with the new data point. */

static void
Update(void)
{
    int i, meter;

    if (PM->LockCount > 0 || !PM->Showing) {
        return;
    }

    gdk_flush();

    if (PM->IsDead || PM->IsSick) {
        DrawDead();
	return;
    }

    for (i = 0; i < PM->NMeters; i++) {
        meter = PM->DisplayList[i];
        DrawGraph(meter, FALSE);
    }
}


static void
DrawDead()
{
    int i, x, y;

    if (PM->IsDead || PM->IsSick) {
        PmRefresh(FALSE);

        for (i = 0; i < PM->NMeters; i++) {
            int meter = PM->DisplayList[i];
            GnomePerfmeter *gp = GNOME_PERFMETER(UI->GMeters[meter]);
            GdkWindow *cw = gnome_perfmeter_get_chart_win(gp);
            int width = gnome_perfmeter_get_chart_width(gp);
            int height = gnome_perfmeter_get_chart_height(gp);

            if (PM->IsDead) {
                x = (width - PM->DeadWp) / 2;
                y = (height - PM->DeadHp) / 2;
                PmAssignMax(x, 0);
                PmAssignMax(y, 0);

                gdk_draw_pixbuf(cw, UI->CanvasGC, UI->DeadPM,
                                0, 0, x, y, PM->DeadWp, PM->DeadHp,
                                GDK_RGB_DITHER_NONE, 0, 0);
            } else {
                x = (width - PM->SickWp) / 2;
                y = (height - PM->SickHp) / 2;
                PmAssignMax(x, 0);
                PmAssignMax(y, 0);

                gdk_draw_pixbuf(cw, UI->CanvasGC, UI->SickPM,
                                0, 0, x, y, PM->SickWp, PM->SickHp,
                                GDK_RGB_DITHER_NONE, 0, 0);
            }
        }
    }
}


/* Calculate the rows and columns. */

static void 
CalculateRC()
{
    double ratio;

    if (PM->Orientation == PM_HORIZONTAL) {
        if (PM->NMeters <= PM->WrapCount) {
            PM->Cols = PM->NMeters;
            PM->Rows = 1;
        } else {
            PM->Cols = PM->WrapCount;
            ratio = (double) PM->NMeters / PM->Cols;
            PM->Rows = (int) ceil(ratio);
        }
    } else {                                       /* PM_VERTICAL */
        if (PM->NMeters <= PM->WrapCount) {
            PM->Rows = PM->NMeters;
            PM->Cols = 1;
        } else {
            PM->Rows = PM->WrapCount;
            ratio = (double) PM->NMeters / PM->Rows;
            PM->Cols = (int) ceil(ratio);
        }
    }
}


/* Set the graph type data. */

void 
PmSetGraphType(PmGraphType GraphType)
{
    PM->GraphType = GraphType;            /* LINE or SOLID */
}


/* Calculate the minimum canvas size. */

void
PmSetMinSize()
{
    GdkWindowHints geometry_mask;
    GdkGeometry geometry;
    int chartw = 0, charth = 0, i, minh, minw, neww, newh;
    GnomePerfmeter *GMeter;
    int mbarw = UI->MenuBar->requisition.width;	  /* Width of the menubar. */
    int mbarh = UI->MenuBar->requisition.height;  /* Height of the menubar. */

    CalculateRC();

    for (i = 0; i < PM->NMeters; i++) {
        GMeter = GNOME_PERFMETER(UI->GMeters[PM->DisplayList[i]]);

        neww = gnome_perfmeter_get_min_width(GMeter);
        chartw = PmMax(chartw, neww);

        newh = gnome_perfmeter_get_min_height(GMeter);
        charth = PmMax(charth, newh);
    }

    if (PM->Orientation == PM_HORIZONTAL) {
	minw = chartw * PM->NMeters;
	minh = charth;

	if (PM->ShowMenubar == TRUE) {
            minw = PmMax(minw, mbarw);
            minh = minh + mbarh;
	}
    } else {
	minw = chartw;
	minh = charth * PM->NMeters;

	if (PM->ShowMenubar == TRUE) {
            minw = PmMax(minw, mbarw);
            minh = minh + mbarh;
	}
    }

    geometry_mask = GDK_HINT_MIN_SIZE;
    geometry.min_width = minw;
    geometry.min_height = minh;
    gtk_window_set_geometry_hints(GTK_WINDOW(UI->MainWin),
                                  UI->MainWin, &geometry, geometry_mask);

#ifdef DEBUG
    g_print("SetMinSize %d,%d\n", minw, minh);
#endif
}


/* Size the graph area. */

static void
PmSizeGraphArea(int Wp, int Hp)
{
    PM->AreaWp = Wp;
    PM->MeterHp = PM->AreaHp = Hp;

    if (PM->ShowLiveActionBar) {
        PM->MeterWp = PM->AreaWp - 2 * PmLAB_SEPARATOR - PmLAB_WIDTH;
    } else {
        PM->MeterWp = PM->AreaWp;
    }

    if (PM->ShowStripChart == FALSE) {
        /* Don't show meter */
        PM->LabStart = (int) (PM->AreaWp / 2) - PmHALF_LAB_WIDTH;
        PM->MeterWp = 0;
    }

#ifdef DEBUG
    g_print("SizeGraphArea %d,%d\n", PM->AreaWp, PM->AreaHp);
#endif

    PmResizeMeterData(PM->MeterWp);
}

/* Scale the meter data to the visible area. */

static int 
ScaleValue(PmMeterStruct *m, int num)
{
    int i, p, sp;

    /* get the data point */
    if (num > PM->CData) {
        i = PM->NData - (num - PM->CData);
    } else {
        i = PM->CData - num;
    }

    p = m->Data[i];

    if (p > m->CurMax * FSCALE) {
        sp = 0;
    } else {
        sp = PM->MeterHp - 
            (int) (((double) (p * PM->MeterHp)) / 
            ((double) (FSCALE * m->CurMax)));
    }

#ifdef DEBUGDATA
    g_print("Raw Data = %d, Scaled Data = %d\n", p, sp);
#endif

    return(sp);
}


/* Show or hide the specified meter. */

void 
PmResetMeter(int meter, gboolean show)
{
    int i, j;

    if (PM->Meters[meter].Showing == show) {
        return;
    }

    if (show == TRUE) {
        PM->Meters[meter].Showing = TRUE;
        PM->Meters[meter].Redisplay = TRUE;

        /* add to the display list */
        for (i = 0; i < PmMAX_METERS; i++) {
            if (PM->DisplayList[i] < 0) {
                PM->DisplayList[i] = meter;
                PM->NMeters++;
                break;
            }    
        }    
    } else {
        PM->Meters[meter].Showing = FALSE;

        /* Remove from the display list */
        for (i = 0; i < PmMAX_METERS; i++) {
            if (PM->DisplayList[i] == meter) {
                /* move the rest to the left */
                for (j = i + 1; j < PmMAX_METERS; j++, i++) {
                    PM->DisplayList[i] = PM->DisplayList[j];
                }
                PM->DisplayList[PmMAX_METERS-1] = -1;
                PM->NMeters--;
                break;
            }
        }
    }

    if (PM->NMeters == 0) {
        /* Always show at least one meter */
        PmResetMeter(PM_CPU, TRUE);
        return;
    } else if (PM->LockCount == 0) {
        PmSetMinSize();
        PmRedisplay();
    }
}


void
PmRedisplay()
{
    int i;

    if (PM->LockCount > 0 || !PM->Showing) {
        return;
    }

    if (PM->IsDead || PM->IsSick) {
        DrawDead();
        return;
    }

    for (i = 0; i < PM->NMeters; i++) {
        int meter = PM->DisplayList[i];

	PM->Meters[meter].Redisplay = TRUE;
        DrawGraph(meter, TRUE);
        PM->Meters[meter].Redisplay = FALSE;
    }
}


/* Show a warning message dialog popup. */

void 
PmWarning(int code, char *arg)
{
    char *charset, *msg, *tmp_msg;
    GtkWidget *dialog;

/* If this is the child process, just return as no graphics operations
 * should be attempted from a fork()'ed process.
 */

   if (PM->ParentPID != getpid()) {
       return;
   }

    switch (code) {
        case PmWRN_HOSTNOTFOUND:
            msg = _("Host \"%s\" is unknown\n");
            break;

        case PmWRN_HELPSELECT:
            msg = _("%s: Select a component within the application.");
            break;

        case PmWRN_HOSTCONNECT:
            if (PM->IsRemote) {
                msg = _("Unable to connect to the remote host:\n");
            } else {
                msg = _("Unable to connect to the local host:\n");
            }
            break;

        default:
            msg = _("%s: Unknown message code.\n");
    }

    if (!UI->MainWin) {
        charset = g_locale_from_utf8(msg, -1, NULL, NULL, NULL);
        g_printerr(charset, arg);
        g_free(charset);
        return;
    }

    tmp_msg = g_strdup_printf(msg, arg);
    dialog = gtk_message_dialog_new(NULL, 
                             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                             GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
                             tmp_msg);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog); 
    g_free(tmp_msg);
}


/* Show an error message. */

void 
PmError(int code, char *arg)
{
    char *charset, *msg, *tmp_msg;
    GtkWidget *dialog;

    switch (code) {
        case PmERR_OPENDISPLAY:
            msg = _("%s: Error opening X Display %s\n");
            break;

        case PmERR_LABELFONT:
            msg = _("%s: Invalid label font specified.\n");
            break;

        case PmERR_HELPSELECT:
            msg = _("%s: Help Selection Error, Operation terminated.\n");
            break;

        case PmERR_SAVERESFILE:
            msg = _("%s: Error saving resource file.\n");
            break;

        case PmERR_OPENRESFILE:
            msg = _("%s: Error opening resource file.\n");
            break;

        default:
            msg = _("%s: Unknown message code.\n");
    }

    if (!UI->MainWin) {
        charset = g_locale_from_utf8(msg, -1, NULL, NULL, NULL);
        g_printerr(charset, PM->AppClass, arg);
        g_free(charset);
        return;
    }

    tmp_msg = g_strdup_printf(msg, PM->AppClass, arg);
    dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
                                    tmp_msg);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog); 

    g_free(tmp_msg);
}


static void
DrawGraph(int meter, gboolean redraw)
{
    PmUIMeterStruct *M;
    int i, end;
    int x1 = 0, y1, x2 = 0, y2, yc, base;
    GdkWindow *cw;

    GnomePerfmeter *gp = GNOME_PERFMETER(UI->GMeters[meter]);
    int AreaWp, AreaHp, MeterWp, MeterHp, LabStart;

    cw = gnome_perfmeter_get_chart_win(gp);

    AreaWp = gnome_perfmeter_get_chart_width(gp)-1;
    MeterHp = AreaHp = gnome_perfmeter_get_chart_height(gp)-1;

    if (PM->ShowLiveActionBar) {
	MeterWp = AreaWp - 2 * PmLAB_SEPARATOR - PmLAB_WIDTH;
    } else {
	MeterWp = AreaWp;
    }

    if (PM->ShowStripChart == FALSE) {
	/* Don't show meter */
	LabStart = (int) (AreaWp / 2) - PmHALF_LAB_WIDTH;
	MeterWp = 0;
    }

#ifdef DEBUGDRAW
    g_print("Draw Graph %d\n", meter);
#endif

    M = &(UI->Meters[meter]);
    if (M->DrawGC == NULL || M->LimitGC == NULL) {
        PmSetMeterColor(meter);
    }

    base = MeterHp;                       /* base line of graph */
    end = PmMin(PM->NData, MeterWp - 1);  /* number of points to draw */

    if (redraw || PM->Meters[meter].Redisplay) {
        /* Clear the entire meter */
        redraw = TRUE;

        /* Redraw the background */
        /* Live action bar should also get cleared */
        gdk_draw_rectangle(cw, UI->FillGC, TRUE, 0, 0, AreaWp+1, AreaHp+1);

#ifdef DEBUGDRAW
    g_print("Clear meter %d\n", meter);
#endif
    } else {
        if (UI->BackingStore) {
            /* Move the existing points to the left */
            gdk_draw_drawable(cw, UI->FillGC, cw, 1, 0, 0, 0, 
                              MeterWp+1, MeterHp+1);

            gdk_draw_rectangle(cw, UI->FillGC, TRUE, MeterWp, 0, 1, MeterHp+1);

            /* Clear live action bar */
            gdk_draw_rectangle(cw, UI->FillGC, TRUE,
                               MeterWp+PmLAB_SEPARATOR, 0,
                               PmLAB_WIDTH+1, MeterHp+1);

            /* Draw the last two points */
            end = PmMin(end, 2);

#ifdef DEBUGDRAW
            g_print("Move graph %d\n", meter);
#endif
        } else {
            /* Move the existing points to the left */
            gdk_draw_drawable(cw, UI->FillGC, cw, 1, 0, 0, 0, 
                              MeterWp+1, MeterHp+1);

            gdk_draw_rectangle(cw, UI->FillGC, TRUE, MeterWp, 0, 
                               1, MeterHp+1);

/* XXX:richb - need to correctly handle when there is no backing store. */
            /* g_print("DrawGraph: NO BACKING STORE ... problems!\n"); */

            /* redraw all of the points to fill in any blank areas
             * generated from the CopyArea
             */

#ifdef DEBUGDRAW
            g_print("Move graph %d\n", meter);
#endif
        }
    }

    /* Scale the ceiling point */
    if (PM->Meters[meter].Limit < PM->Meters[meter].CurMax) {
        yc = base - (int) (((double)
            PM->Meters[meter].Limit / (double) PM->Meters[meter].CurMax) *
            MeterHp);
    } else {
        /* Set the ceiling position out of bounds */
        yc = 0;
    }

#ifdef DEBUGDRAW
    g_print("Graph %d Base = %d, Ceiling = %d, Num Points = %d\n",
            meter, base, yc, end);
#endif

    if (PM->ShowStripChart == TRUE) {       /* Draw stripchart */
        if (PM->GraphType == PM_LINE && PM->NData > 1) {
            /* Calculate the first point (must have at least two data points) */
            x1 = MeterWp - 1;
            y1 = ScaleValue(&PM->Meters[meter], 0);

            x2 = y2 = 0;

            for (i = 1; i < end; i++, x1 = x2, y1 = y2) {
                x2 = x1 - 1;
                y2 = ScaleValue(&PM->Meters[meter], i);

                if (y1 <= yc && y2 <= yc) {
                    /* Both points are above the line */
                    gdk_draw_line(cw, M->LimitGC, x1, y1, x2, y2);
#ifdef DEBUGDRAW
                    g_print("Graph BOTH ABOVE (%d,%d),(%d,%d)\n",
                            x1, y1, x2, y2);
#endif
                } else if (y1 <= yc) {
                    /* New point is above limit */
                    gdk_draw_line(cw, M->LimitGC, x1, y1, x2, yc);
                    gdk_draw_line(cw, M->DrawGC, x2, yc, x2, y2);

#ifdef DEBUGDRAW
                    g_print("Graph NEW  ABOVE (%d,%d),(%d,%d),(%d,%d)\n",
                            x1, y1, x2, yc, x2, y2);
#endif
                } else if (y2 <= yc) {
                    /* Previous point is above Limit */
                    gdk_draw_line(cw, M->DrawGC, x1, y1, x2, yc);
                    gdk_draw_line(cw, M->LimitGC, x2, yc, x2, y2);

#ifdef DEBUGDRAW
                    g_print("Graph PREV ABOVE (%d,%d),(%d,%d),(%d,%d)\n",
                            x1, y1, x2, yc, x2, y2);
#endif
                } else {
                    /* None are above the limit */
                    gdk_draw_line(cw, M->DrawGC, x1, y1, x2, y2);

#ifdef DEBUGDRAW
                    g_print("Graph NORMAL (%d,%d), (%d,%d)\n", x1, y1, x2, y2);
#endif
                }
            }
        } else if (PM->GraphType == PM_SOLID) /* SOLID */ {
            for (i = 0, x1 = MeterWp-1, y1 = base; i < end; i++, x1--) {
                x2 = x1;
                y2 = ScaleValue(&PM->Meters[meter], i);

                if (y2 <= yc) {
                    gdk_draw_line(cw, M->DrawGC, x1, y1, x2, yc);
                    gdk_draw_line(cw, M->LimitGC, x2, yc, x2, y2);
#ifdef DEBUGDRAW
                    g_print("Graph LIMIT (%d,%d), (%d,%d), (%d,%d)\n",
                            x1, y1, x2, yc, x2, y2);
#endif
                } else {
                    gdk_draw_line(cw, M->DrawGC, x1, y1, x2, y2);

#ifdef DEBUGDRAW
                    g_print("Graph NORMAL (%d,%d), (%d,%d)\n", x1, y1, x2, y2);
#endif
                }
            }
        }
    }

    /* Fill out live action bar */
    if (PM->ShowLiveActionBar) {
        if (PM->ShowStripChart == TRUE) {
            x1 = MeterWp + PmLAB_SEPARATOR + PmLAB_WIDTH - 1;
        } else {
            x1 = LabStart +  PmLAB_WIDTH - 1;
        }
        y1 = base;
        x2 = x1;
        y2 = ScaleValue(&PM->Meters[meter], 0);

        for (i = 0; i < PmLAB_WIDTH; i++, x1--) {
            x2 = x1;

            if (y2 <= yc) {
                gdk_draw_line(cw, M->DrawGC, x1, y1, x2, yc);
                gdk_draw_line(cw, M->LimitGC, x1, yc, x2, y2);
            } else {
                gdk_draw_line(cw, M->DrawGC, x1, y1, x2, y2);
            }

            /* clear above the bar */
            gdk_draw_line(cw, UI->FillGC, x1, y2, x2, y1 - PM->MeterHp);
        }
    }

    /* Draw the limit line */
    if ((PM->ShowLimit) &&
        (PM->Meters[meter].Limit < PM->Meters[meter].CurMax)) {
        if (PM->ShowStripChart == TRUE) {
            x1 = 0;
            x2 = MeterWp - 1;
            y1 = y2 = yc;
            gdk_draw_line(cw, M->LimitGC, x1, y1, x2, y2);
        }

        if ((PM->ShowStripChart) && (PM->ShowLiveActionBar))  {
            x1 = MeterWp + PmLAB_SEPARATOR - 1;
            x2 = MeterWp + PmLAB_SEPARATOR + PmLAB_WIDTH - 1;
        }
        if (!PM->ShowStripChart) {
            x1 = LabStart;
            x2 = x1 + PmLAB_WIDTH - 1;
        }
        y1 = y2 = yc;
        gdk_draw_line(cw, M->LimitGC, x1, y1, x2, y2);
    }
}


/* Set the new display list and update the visible meters. */

gboolean 
PmSetDisplayList(int *DL)
{
    int i, meter;
    gboolean update = FALSE;

    PmLock();

    if (DL) {
        for (i = 0; i < PmMAX_METERS; i++) {
            if (PM->DisplayList[i] != DL[i]) {
                /* Something has changed */
                update = TRUE;
            }
        }
    } else {
        update = TRUE;
    }
  
    if (update) {
        /* Initialize the visible meter counter */
        PM->NMeters = 0;
      
        /* Turn all meters off */
        for (i = 0; i < PmMAX_METERS; i++) {
            PM->Meters[i].Redisplay = FALSE;
            PM->Meters[i].Showing = FALSE;
        }

        if (DL) {
            /* Reset the visible meters & counter */
            for (i = 0; i < PmMAX_METERS; i++) {
                meter = PM->DisplayList[i] = DL[i];
          
                if (meter >= 0) {
                    PM->Meters[meter].Redisplay = TRUE;
                    PM->Meters[meter].Showing = TRUE;
                    PM->NMeters++;
                }
            }
        } else {
            for (i = 0; i < PmMAX_METERS; i++) {
                PM->DisplayList[i] = -1;
            }
        }
    }

    /* If no meters are visible, make the CPU meter visible */
    if (PM->NMeters < 1) {
        PmResetMeter(PM_CPU, TRUE);
    }

#ifdef DEBUG
    g_print("SetDisplayList ");
    for (i = 0; i < PmMAX_METERS; i++) {
        g_print("%d ", PM->DisplayList[i]);
    }
    g_print("\n");
#endif

    ResetPopupMenu();
    PmUnLock();

    return(update);
}


/* Increment the lock count. */

void 
PmLock()
{
    PM->LockCount++;
#ifdef DEBUG
    g_print("LOCK Count = %d\n", PM->LockCount);
#endif
}


/* Decrement the lock count. */

void 
PmUnLock()
{
    if (PM->LockCount > 0) {
        if (PM->LockCount == 1 && UI->initializing == FALSE) {
            gdk_flush();
        }
        PM->LockCount--;
    }
#ifdef DEBUG
    g_print("UNLOCK Count = %d\n", PM->LockCount);
#endif
}


void
PmSetMeterColor(int meter)
{
    GnomePerfmeter *GMeter = GNOME_PERFMETER(UI->GMeters[meter]);
    GdkWindow *cw = gnome_perfmeter_get_chart_win(GMeter);
    GdkGC *DrawGC, *LimitGC;
    GdkGCValues values;
    GdkGCValuesMask mask;
    GdkColor MeterColor;
    GdkColor LimitColor;
    GdkColor black;

    if (cw != NULL) {
        gdk_color_parse("black", &black);

        /* Save the existing ones */
        DrawGC  = UI->Meters[meter].DrawGC;
        LimitGC = UI->Meters[meter].LimitGC;

        /* Set the colors */
        if (UI->UseColor) {
            MeterColor = PM->Meters[meter].MeterColor;
            LimitColor = PM->Meters[meter].LimitColor;
            gdk_colormap_alloc_color(UI->ColorMap, &MeterColor, FALSE, TRUE);
            gdk_colormap_alloc_color(UI->ColorMap, &LimitColor, FALSE, TRUE);
        } else {
            MeterColor = LimitColor = black;
            gdk_colormap_alloc_color(UI->ColorMap, &MeterColor, FALSE, TRUE);
            gdk_colormap_alloc_color(UI->ColorMap, &LimitColor, FALSE, TRUE);
        }

        mask = GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_LINE_WIDTH |
               GDK_GC_LINE_STYLE | GDK_GC_CAP_STYLE | GDK_GC_JOIN_STYLE;
        values.line_width = 0;
        values.line_style = LineSolid;
        values.cap_style = CapButt;
        values.join_style = JoinMiter;

        values.foreground = MeterColor;
        values.background = PM->CanvasColor;
        UI->Meters[meter].DrawGC = gdk_gc_new_with_values(cw, &values, mask);
        values.foreground = LimitColor;
        values.background = PM->CanvasColor;
        UI->Meters[meter].LimitGC = gdk_gc_new_with_values(cw, &values, mask);

        /* Release the old GC's */
        if (DrawGC) {
            g_object_unref(DrawGC);
        }
        if (LimitGC) {
            g_object_unref(LimitGC);
        }
    }
}


void
PmSetColors()
{
    int i;

    if (UI->UseColor) {
#if DEBUG
        g_print("PM->CanvasColor=%d\n", PM->CanvasColor);
#endif

        for (i = 0; i < PmMAX_METERS; i++) {
            gnome_perfmeter_set_chart_bg_color(GNOME_PERFMETER(UI->GMeters[i]), 
                                               PM->CanvasColor);
        }

        gdk_gc_set_foreground(UI->CanvasGC, &PM->CanvasColor);
        gdk_gc_set_background(UI->CanvasGC, &PM->CanvasColor);

        gdk_gc_set_foreground(UI->FillGC, &PM->CanvasColor);
    }
}


void
PmRefresh(gboolean redisplay)
{
    int i;

    for (i = 0; i < PM->NMeters; i++) {
        int meter = PM->DisplayList[i];

        gnome_perfmeter_clear_chart(GNOME_PERFMETER(UI->GMeters[meter]),
                                    redisplay);
    }
}


/* Activate the monitoring. */

static void 
PmActivate(gboolean active)
{
    guint32 interval = 0;

    if (PM->Active == active) {
        return;
    }

    if (PM->Active) {
        /* Stop collecting */
        if (UI->TimerId) {
            g_source_remove(UI->TimerId);
            UI->TimerId = 0;
        }

        /* Reset the collect data */
        PmResetMeterData();

#ifdef DEBUG
        g_print("Stop Collecting\n");
#endif
    } else {
        /* Start collecting */
        if (!PM->IsDead) {
            PmRefresh(TRUE);

            /* Update the meter data */
            if (PmUpdateData()) {
                PmScaleData();
            }

            /* Redisplay the meters */
            Update();

            if (PM->Log) {
                PMLOGDATA();
            }
        }

        /* Set a new timer */
        interval = PM->SampleInterval * PM->SampleModifier * 1000;
        UI->TimerId = gtk_timeout_add(interval, UpdateTimer, NULL);

#ifdef DEBUG
        g_print("Start Collecting\n");
#endif
    }
    PM->Active = active;
}


static void
SetSampleTime(int value)
{
    PM->SampleInterval = value; 
    PM->SampleModifier = 1; 
    PmAssignMax(PM->SampleInterval, 1); 
    PmRedisplay(); 
    PmResetProps(); 
}


/* Handle the window keyboard accelerators:
 *
 *              a   - show all meters
 *              n   - toggle monitoring mode (local/remote)
 *              s   - toggle graph style
 *              ^   - toggle showing the limit line
 *              t   - toggle writing to the log file
 *              y   - toggle orientation (horizontal/vertical)
 *              r   - toggle showing strip chart
 *              1-9 - set sampletime to a range from 1-9 seconds
 *              +   - increment the sample time by 1 second
 *              -   - decrement the sample time by 1 second
 */

/*ARGSUSED*/
static gboolean
MainWinKeyHandler(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
    int i, dl[PmMAX_METERS];

    if ((event->state) && (event->keyval != GDK_F10) ||
        (!event->state) && (event->keyval == GDK_F10)) {
        return(FALSE);
    }

    switch (event->keyval) {
        case GDK_a:                                    /* Show all meters. */ 
            for (i = 0; i < PmMAX_METERS; i++) {
                dl[i] = i;
            }
            PmLock();
            PMSETDISPLAYLIST(dl);
            PmSetMinSize();
            PmUnLock();
            PmRedisplay();
            break;


        case GDK_n:              /* Toggle monitoring mode (local/remote). */ 
            if (PM->IsRemote) {
                if (PM->AutoLoadByHost) {
                    PmShow(FALSE);
                }

                PM->IsRemote = FALSE;
                PMMONITOR();
 
                if (PM->AutoLoadByHost) {
                    PmReset(NULL);
                    PmShow(TRUE);
                }
            } else {
                if (PM->AutoLoadByHost) {
                    PmShow(FALSE);
                }
             
                PM->IsRemote = TRUE;
                PMMONITOR();
             
                if (PM->AutoLoadByHost) {
                    PmReset(NULL);
                    PmShow(TRUE);
                }
            }    
            PmResetProps(); 
            break;

        case GDK_s:                    /* Toggle graph style (line/solid). */
#if DEBUG
            g_print("GraphType=%s\n",
                    (PM->GraphType == PM_LINE) ? "LINE" : "SOLID");
#endif
            if (PM->ShowStripChart == TRUE) {
                if ((PM->GraphType == PM_LINE) ||
                    (PM->GraphType == PM_SOLID)) {
                    if (PM->GraphType == PM_LINE) {
                        PmSetGraphType(PM_SOLID);
                    } else {
                        PmSetGraphType(PM_LINE);
                    }
                    PmSetMinSize();
                    PmRedisplay();
                }
            }
            PmResetProps();
            break;

        case GDK_acute:                  /* Toggle showing the limit line. */
            PM->ShowLimit = !PM->ShowLimit;
            PmResetProps();
            break;

        case GDK_t:                     /* Toggle writing to the log file. */
            if (PM->Log) {
                PmLogClose();
            } else {
                PM->Log = PmLogOpen();
            }
            PmResetProps(); 
            break; 

        case GDK_y:                                 /* Toggle orientation. */
            if (PM->Orientation == PM_HORIZONTAL) {
                PM->Orientation = PM_VERTICAL;
            } else {
                PM->Orientation = PM_HORIZONTAL;
            }
            PmSetMinSize();
            PmRedisplay();
            PmResetProps();
            break;
 
        case GDK_r:                         /* Toggle showing strip chart. */
            PM->ShowStripChart = (PM->ShowStripChart) ? FALSE : TRUE;
            PM->StripChartActivity = TRUE;
            PmSetMinSize();
            PmRedisplay();
            PM->StripChartActivity = FALSE;
            PmResetPropsDecor();
            break;

        case GDK_1:                        /* Set sampletime to 1 seconds. */  
            SetSampleTime(1);
            break;

        case GDK_2:                        /* Set sampletime to 2 seconds. */  
            SetSampleTime(2);
            break;

        case GDK_3:                        /* Set sampletime to 3 seconds. */  
            SetSampleTime(3);
            break;

        case GDK_4:                        /* Set sampletime to 4 seconds. */  
            SetSampleTime(4);
            break;

        case GDK_5:                        /* Set sampletime to 5 seconds. */  
            SetSampleTime(5);
            break;

        case GDK_6:                        /* Set sampletime to 6 seconds. */  
            SetSampleTime(6);
            break;

        case GDK_7:                        /* Set sampletime to 7 seconds. */  
            SetSampleTime(7);
            break;

        case GDK_8:                        /* Set sampletime to 8 seconds. */  
            SetSampleTime(8);
            break;

        case GDK_9:                        /* Set sampletime to 9 seconds. */
            SetSampleTime(9);
            break;

        case GDK_minus:          /* Increment the sample time by 1 second. */
            SetSampleTime(PM->SampleInterval++);
            break;

        case GDK_plus:           /* Decrement the sample time by 1 second. */
            SetSampleTime(PM->SampleInterval--);
            break;

        case GDK_F10:           /* Popup Menu */
            if (event->state & GDK_SHIFT_MASK) {
                gtk_menu_popup(GTK_MENU(UI->PopupMenu),
                               NULL, NULL, NULL, NULL, 0, event->time);
            }
            break;

        default:
            /* We didn't handle it - let something else work it out. */
            return(FALSE);
    }

    return(TRUE);
}


static void
style_set_callback(GtkWidget *widget)
{
    GnomePerfmeter *GMeter = GNOME_PERFMETER(UI->GMeters[PM->DisplayList[0]]);
    GtkWidget *chart = gnome_perfmeter_get_chart(GMeter);

    if (GTK_WIDGET_REALIZED(widget)){
        create_MainWin();
        PM->CanvasColor = chart->style->bg[GTK_STATE_NORMAL];
        PmSetColors();
        PmRedisplay();
    }
}


static void
set_gperfmeter_icon(void)
{
    gchar *icon_name = g_strdup_printf("perfmeter");
    GdkPixbuf *icon;
    GtkIconTheme *icon_theme = gtk_icon_theme_get_default();

    if (gtk_icon_theme_has_icon(icon_theme, icon_name)) {

        icon = gtk_icon_theme_load_icon(icon_theme, icon_name, 48, 0, NULL);

        if (icon != NULL) {
            gtk_window_set_icon(GTK_WINDOW(UI->MainWin), icon);

            g_object_unref(icon);
        } else {
            g_warning("unable to load icon '%s'", icon_name);
        }
    }

    g_free(icon_name);
}


/* Set the remote host name (or "localhost") in the titlebar of 
 * the window and below the graph area (which is show/hidden via
 * a checkbox setting on the Preferences sheet.
 */

void
set_hostname()
{
    gchar *title, *label;

    title = g_strdup_printf("%s - %s",
                            (PM->IsRemote ? PM->Hostname : _("localhost")),
                            _("Performance Meter"));

    gtk_window_set_title(GTK_WINDOW(UI->MainWin), title);

    g_free(title);

    label = g_strdup_printf("%s", PM->IsRemote ? PM->Hostname : _("localhost"));

    gtk_label_set_text(GTK_LABEL(UI->HostName), label);

    g_free(label);
}


/* Start a new monitor. */

gboolean 
PmMonitor()
{
    /* Deactivate the monitor */
    PmActivate(FALSE);

    /* Connect to the new host */
    if (PmConnectToHost()) {      
        /* Activate the monitor */
        PmActivate(TRUE);

        set_hostname();
        if (PM->IsRemote) {
            PmAddHostToList(PM->Hostname);
        }
     
        PmRedisplay();
        return(TRUE);
    }

    return(FALSE);
}


/* Show/Hide the meters. */

void 
PmShow(gboolean show)
{
    if (show) {
        gdk_flush();
        gtk_widget_show(UI->MainWin);
        PM->Showing = TRUE;
    } else {
        gtk_widget_hide(UI->MainWin);
        PM->Showing = FALSE;
    }
}


/* Reset the application to its last saved state. */

void 
PmReset(char *filename)
{
    int i;

    if (PmResetResources(filename)) {
        /* General colors */
        PmSetColors();

        /* Meter colors */
        for (i = 0; i < PM->NMeters; i++) {
            PmSetMeterColor(PM->DisplayList[i]);
        }

        /* Graph type */
        PmSetGraphType(PM->GraphType);
        PmSetMinSize();

        PmSetMinSize();
        PmLogClose();
        PmResetProps();
        PmRedisplay();
    }
}


/* Set the busy cursor on the main & props windows. */

void 
PmBusy(gboolean busy)
{
    if (busy) {
        UI->Busy++;
        gdk_window_set_cursor(UI->MainWin->window, UI->WaitCursor);
    } else if (UI->Busy > 0) {
        UI->Busy--;

        if (UI->Busy == 0) {
            gdk_window_set_cursor(UI->MainWin->window, NULL);
        }
    }

    PmPropsBusy(busy);
}


static void
gperfmeter_set_atk_relation(GtkWidget *obj1, GtkWidget *obj2,
                            AtkRelationType rel_type)
{
    AtkObject *atk_obj1, *atk_obj2;
    AtkRelationSet *relation_set;
    AtkObject *targets[1];
    AtkRelation *relation;

    atk_obj1 = gtk_widget_get_accessible(obj1);
    atk_obj2 = gtk_widget_get_accessible(obj2);

    if (!(GTK_IS_ACCESSIBLE(atk_obj1)) || !(GTK_IS_ACCESSIBLE(atk_obj2))) {
        return;
    }

    relation_set = atk_object_ref_relation_set(atk_obj1);
    targets[0] = atk_obj2;

    relation = atk_relation_new(targets, 1, rel_type);
    atk_relation_set_add(relation_set, relation);

    g_object_unref(G_OBJECT(relation));
}


/*ARGSUSED*/
static gint 
gperfmeter_accessible_get_n_children(AtkObject *obj)
{
    return(PmMAX_METERS);
}


static
AtkObject *
gperfmeter_accessible_ref_child(AtkObject *obj, gint i)
{
    AtkRegistry *default_registry;
    AtkObjectFactory *factory;
    GtkWidget *widget;
    gint j;

    static gint first_time = 1;

    widget = GTK_ACCESSIBLE(obj)->widget;
    g_return_val_if_fail(widget != NULL, NULL);
    g_return_val_if_fail(i < PmMAX_METERS, NULL);

    if (first_time) {
        default_registry = atk_get_default_registry();
        factory = atk_registry_get_factory(default_registry, 
                                           PANGO_TYPE_LAYOUT);

        for (j = 0; j < PmMAX_METERS; j++) {
            pango_accessible[j] = atk_object_factory_create_accessible(factory,
                                                             G_OBJECT(layout));
            atk_object_set_parent(pango_accessible[j], obj);
            atk_object_set_name(pango_accessible[j], _(PM->Meters[j].RName));
        }
        first_time--;
    }

    return(pango_accessible[i]);
}
