diff --git a/app/actions/dashboard-actions.c b/app/actions/dashboard-actions.c index 6acf18c035..9ed2f4bbf5 100644 --- a/app/actions/dashboard-actions.c +++ b/app/actions/dashboard-actions.c @@ -43,7 +43,13 @@ static const GimpActionEntry dashboard_actions[] = { "dashboard-update-interval", NULL, NC_("dashboard-action", "Update Interval") }, { "dashboard-history-duration", NULL, - NC_("dashboard-action", "History Duration") } + NC_("dashboard-action", "History Duration") }, + + { "dashboard-reset", GIMP_ICON_RESET, + NC_("dashboard-action", "Reset"), NULL, + NC_("dashboard-action", "Reset cumulative data"), + G_CALLBACK (dashboard_reset_cmd_callback), + GIMP_HELP_DASHBOARD_RESET }, }; static const GimpToggleActionEntry dashboard_toggle_actions[] = @@ -149,7 +155,7 @@ dashboard_actions_update (GimpActionGroup *group, #define SET_ACTIVE(action,condition) \ gimp_action_group_set_action_active (group, action, (condition) != 0) - switch (dashboard->update_interval) + switch (gimp_dashboard_get_update_interval (dashboard)) { case GIMP_DASHBOARD_UPDATE_INTERVAL_0_25_SEC: SET_ACTIVE ("dashboard-update-interval-0-25-sec", TRUE); @@ -168,7 +174,7 @@ dashboard_actions_update (GimpActionGroup *group, break; } - switch (dashboard->history_duration) + switch (gimp_dashboard_get_history_duration (dashboard)) { case GIMP_DASHBOARD_HISTORY_DURATION_15_SEC: SET_ACTIVE ("dashboard-history-duration-15-sec", TRUE); @@ -188,7 +194,7 @@ dashboard_actions_update (GimpActionGroup *group, } SET_ACTIVE ("dashboard-low-swap-space-warning", - dashboard->low_swap_space_warning); + gimp_dashboard_get_low_swap_space_warning (dashboard)); #undef SET_ACTIVE } diff --git a/app/actions/dashboard-commands.c b/app/actions/dashboard-commands.c index c7b110b780..2259490fee 100644 --- a/app/actions/dashboard-commands.c +++ b/app/actions/dashboard-commands.c @@ -61,6 +61,15 @@ dashboard_history_duration_cmd_callback (GtkAction *action, gimp_dashboard_set_history_duration (dashboard, history_duration); } +void +dashboard_reset_cmd_callback (GtkAction *action, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + + gimp_dashboard_reset (dashboard); +} + void dashboard_low_swap_space_warning_cmd_callback (GtkAction *action, gpointer data) diff --git a/app/actions/dashboard-commands.h b/app/actions/dashboard-commands.h index 4314a144f1..2151238157 100644 --- a/app/actions/dashboard-commands.h +++ b/app/actions/dashboard-commands.h @@ -26,6 +26,9 @@ void dashboard_history_duration_cmd_callback (GtkAction *action, GtkAction *current, gpointer data); +void dashboard_reset_cmd_callback (GtkAction *action, + gpointer data); + void dashboard_low_swap_space_warning_cmd_callback (GtkAction *action, gpointer data); diff --git a/app/widgets/gimpdashboard.c b/app/widgets/gimpdashboard.c index 37793f9ddc..7f085a380b 100644 --- a/app/widgets/gimpdashboard.c +++ b/app/widgets/gimpdashboard.c @@ -37,8 +37,10 @@ #include "gimpdocked.h" #include "gimpdashboard.h" #include "gimpdialogfactory.h" +#include "gimphelp-ids.h" #include "gimpmeter.h" #include "gimpsessioninfo-aux.h" +#include "gimpwidgets-utils.h" #include "gimpwindowstrategy.h" #include "gimp-intl.h" @@ -51,35 +53,384 @@ #define LOW_SWAP_SPACE_WARNING_ON /* swap occupied is above */ 0.90 /* of swap limit */ #define LOW_SWAP_SPACE_WARNING_OFF /* swap occupied is below */ 0.85 /* of swap limit */ -#define CACHE_OCCUPIED_COLOR {0.3, 0.6, 0.3, 1.0} -#define SWAP_OCCUPIED_COLOR {0.8, 0.2, 0.2, 1.0} -#define SWAP_SIZE_COLOR {0.8, 0.6, 0.4, 1.0} -#define SWAP_LED_COLOR {0.8, 0.4, 0.4, 1.0} + +typedef enum +{ + VARIABLE_NONE, + FIRST_VARIABLE, -static void gimp_dashboard_docked_iface_init (GimpDockedInterface *iface); + /* cache */ + VARIABLE_CACHE_OCCUPIED = FIRST_VARIABLE, + VARIABLE_CACHE_MAXIMUM, + VARIABLE_CACHE_LIMIT, -static void gimp_dashboard_dispose (GObject *object); -static void gimp_dashboard_finalize (GObject *object); + VARIABLE_CACHE_COMPRESSION, + VARIABLE_CACHE_HIT_MISS, -static void gimp_dashboard_map (GtkWidget *widget); -static void gimp_dashboard_unmap (GtkWidget *widget); + /* swap */ + VARIABLE_SWAP_OCCUPIED, + VARIABLE_SWAP_SIZE, + VARIABLE_SWAP_LIMIT, -static void gimp_dashboard_set_aux_info (GimpDocked *docked, - GList *aux_info); -static GList * gimp_dashboard_get_aux_info (GimpDocked *docked); + VARIABLE_SWAP_BUSY, -static gboolean gimp_dashboard_update (GimpDashboard *dashboard); -static gboolean gimp_dashboard_low_swap_space (GimpDashboard *dashboard); -static gpointer gimp_dashboard_sample (GimpDashboard *dashboard); -static void gimp_dashboard_label_set_text (GtkLabel *label, - const gchar *text); -static void gimp_dashboard_label_set_size (GtkLabel *label, - guint64 size, - guint64 limit); + N_VARIABLES, -static guint64 gimp_dashboard_get_swap_limit (guint64 swap_size); + VARIABLE_SEPARATOR +} Variable; + +typedef enum +{ + VARIABLE_TYPE_BOOLEAN, + VARIABLE_TYPE_SIZE, + VARIABLE_TYPE_SIZE_RATIO, + VARIABLE_TYPE_INT_RATIO +} VariableType; + +typedef enum +{ + FIRST_GROUP, + + GROUP_CACHE = FIRST_GROUP, + GROUP_SWAP, + + N_GROUPS +} Group; + + +typedef struct _VariableInfo VariableInfo; +typedef struct _FieldInfo FieldInfo; +typedef struct _GroupInfo GroupInfo; +typedef struct _VariableData VariableData; +typedef struct _FieldData FieldData; +typedef struct _GroupData GroupData; + +typedef void (* VariableSampleFunc) (GimpDashboard *dashboard, + Variable variable); + + +struct _VariableInfo +{ + const gchar *name; + const gchar *title; + const gchar *description; + VariableType type; + GimpRGB color; + VariableSampleFunc sample_func; + gpointer data; +}; + +struct _FieldInfo +{ + Variable variable; + gboolean default_active; + gboolean meter_value; +}; + +struct _GroupInfo +{ + const gchar *name; + const gchar *title; + const gchar *description; + gboolean default_expanded; + gboolean has_meter; + Variable meter_limit; + Variable meter_led; + const FieldInfo *fields; +}; + +struct _VariableData +{ + gboolean available; + + union + { + gboolean boolean; + guint64 size; + struct + { + guint64 antecedent; + guint64 consequent; + } size_ratio; + struct + { + gint antecedent; + gint consequent; + } int_ratio; + } value; +}; + +struct _FieldData +{ + gboolean active; + + GtkCheckMenuItem *menu_item; + GtkLabel *value_label; +}; + +struct _GroupData +{ + gint n_fields; + gint n_meter_values; + + GtkExpander *expander; + GtkButton *menu_button; + GtkMenu *menu; + GimpMeter *meter; + GtkTable *table; + + FieldData *fields; +}; + +struct _GimpDashboardPrivate +{ + Gimp *gimp; + + VariableData variables[N_VARIABLES]; + GroupData groups[N_GROUPS]; + + GThread *thread; + GMutex mutex; + GCond cond; + gboolean quit; + gboolean update_now; + + gint update_idle_id; + gint low_swap_space_idle_id; + + GimpDashboardUpdateInteval update_interval; + GimpDashboardHistoryDuration history_duration; + gboolean low_swap_space_warning; +}; + + +/* local function prototypes */ + +static void gimp_dashboard_docked_iface_init (GimpDockedInterface *iface); + +static void gimp_dashboard_constructed (GObject *object); +static void gimp_dashboard_dispose (GObject *object); +static void gimp_dashboard_finalize (GObject *object); + +static void gimp_dashboard_map (GtkWidget *widget); +static void gimp_dashboard_unmap (GtkWidget *widget); + +static void gimp_dashboard_set_aux_info (GimpDocked *docked, + GList *aux_info); +static GList * gimp_dashboard_get_aux_info (GimpDocked *docked); + +static gboolean gimp_dashboard_group_expander_button_press (GimpDashboard *dashboard, + GdkEventButton *bevent, + GtkWidget *widget); + +static void gimp_dashboard_group_menu_item_toggled (GimpDashboard *dashboard, + GtkCheckMenuItem *item); + +static gpointer gimp_dashboard_sample (GimpDashboard *dashboard); + +static gboolean gimp_dashboard_update (GimpDashboard *dashboard); +static gboolean gimp_dashboard_low_swap_space (GimpDashboard *dashboard); + +static void gimp_dashboard_sample_gegl_config (GimpDashboard *dashboard, + Variable variable); +static void gimp_dashboard_sample_gegl_stats (GimpDashboard *dashboard, + Variable variable); +static void gimp_dashboard_sample_swap_limit (GimpDashboard *dashboard, + Variable variable); + +static void gimp_dashboard_sample_object (GimpDashboard *dashboard, + GObject *object, + Variable variable); + +static void gimp_dashboard_container_remove (GtkWidget *widget, + GtkContainer *container); + +static void gimp_dashboard_group_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data); + +static void gimp_dashboard_update_groups (GimpDashboard *dashboard); +static void gimp_dashboard_update_group (GimpDashboard *dashboard, + Group group); +static void gimp_dashboard_update_group_values (GimpDashboard *dashboard, + Group group); + +static void gimp_dashboard_field_set_active (GimpDashboard *dashboard, + Group group, + gint field, + gboolean active); + +static gboolean gimp_dashboard_variable_to_boolean (GimpDashboard *dashboard, + Variable variable); +static gdouble gimp_dashboard_variable_to_double (GimpDashboard *dashboard, + Variable variable); + + +/* static variables */ + +static const VariableInfo variables[] = +{ + /* cache variables */ + + [VARIABLE_CACHE_OCCUPIED] = + { .name = "cache-occupied", + .title = NC_("dashboard-variable", "Occupied"), + .description = N_("Tile cache occupied size"), + .type = VARIABLE_TYPE_SIZE, + .color = {0.3, 0.6, 0.3, 1.0}, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "tile-cache-total" + }, + + [VARIABLE_CACHE_MAXIMUM] = + { .name = "cache-maximum", + .title = NC_("dashboard-variable", "Maximum"), + .description = N_("Maximal tile cache occupied size during this session"), + .type = VARIABLE_TYPE_SIZE, + .color = {0.3, 0.7, 0.8, 1.0}, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "tile-cache-total-max" + }, + + [VARIABLE_CACHE_LIMIT] = + { .name = "cache-limit", + .title = NC_("dashboard-variable", "Limit"), + .description = N_("Tile cache size limit"), + .type = VARIABLE_TYPE_SIZE, + .sample_func = gimp_dashboard_sample_gegl_config, + .data = "tile-cache-size" + }, + + [VARIABLE_CACHE_COMPRESSION] = + { .name = "cache-compression", + .title = NC_("dashboard-variable", "Compression"), + .description = N_("Tile cache compression ratio"), + .type = VARIABLE_TYPE_SIZE_RATIO, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "tile-cache-total\0" + "tile-cache-total-uncloned" + }, + + [VARIABLE_CACHE_HIT_MISS] = + { .name = "cache-hit-miss", + .title = NC_("dashboard-variable", "Hit/Miss"), + .description = N_("Tile cache hit/miss ratio"), + .type = VARIABLE_TYPE_INT_RATIO, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "tile-cache-hits\0" + "tile-cache-misses" + }, + + + /* swap variables */ + + [VARIABLE_SWAP_OCCUPIED] = + { .name = "swap-occupied", + .title = NC_("dashboard-variable", "Occupied"), + .description = N_("Swap file occupied size"), + .type = VARIABLE_TYPE_SIZE, + .color = {0.8, 0.2, 0.2, 1.0}, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "swap-total" + }, + + [VARIABLE_SWAP_SIZE] = + { .name = "swap-size", + .title = NC_("dashboard-variable", "Size"), + .description = N_("Swap file size"), + .type = VARIABLE_TYPE_SIZE, + .color = {0.8, 0.6, 0.4, 1.0}, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "swap-file-size" + }, + + [VARIABLE_SWAP_LIMIT] = + { .name = "swap-limit", + .title = NC_("dashboard-variable", "Limit"), + .description = N_("Swap file size limit"), + .type = VARIABLE_TYPE_SIZE, + .sample_func = gimp_dashboard_sample_swap_limit, + }, + + [VARIABLE_SWAP_BUSY] = + { .name = "swap-busy", + .title = NC_("dashboard-variable", "Busy"), + .description = N_("Whether there is work queued for the swap file"), + .type = VARIABLE_TYPE_BOOLEAN, + .color = {0.8, 0.4, 0.4, 1.0}, + .sample_func = gimp_dashboard_sample_gegl_stats, + .data = "swap-busy" + } +}; + +static const GroupInfo groups[] = +{ + /* cache group */ + [GROUP_CACHE] = + { .name = "cache", + .title = NC_("dashboard-group", "Cache"), + .description = N_("In-memory tile cache"), + .default_expanded = TRUE, + .has_meter = TRUE, + .meter_limit = VARIABLE_CACHE_LIMIT, + .fields = (const FieldInfo[]) + { + { .variable = VARIABLE_CACHE_OCCUPIED, + .default_active = TRUE, + .meter_value = 2 + }, + { .variable = VARIABLE_CACHE_MAXIMUM, + .default_active = FALSE, + .meter_value = 1 + }, + { .variable = VARIABLE_CACHE_LIMIT, + .default_active = TRUE + }, + + { VARIABLE_SEPARATOR }, + + { .variable = VARIABLE_CACHE_COMPRESSION, + .default_active = FALSE + }, + { .variable = VARIABLE_CACHE_HIT_MISS, + .default_active = FALSE + }, + + {} + } + }, + + /* swap group */ + [GROUP_SWAP] = + { .name = "swap", + .title = NC_("dashboard-group", "Swap"), + .description = N_("On-disk tile swap"), + .default_expanded = TRUE, + .has_meter = TRUE, + .meter_limit = VARIABLE_SWAP_LIMIT, + .meter_led = VARIABLE_SWAP_BUSY, + .fields = (const FieldInfo[]) + { + { .variable = VARIABLE_SWAP_OCCUPIED, + .default_active = TRUE, + .meter_value = 2 + }, + { .variable = VARIABLE_SWAP_SIZE, + .default_active = TRUE, + .meter_value = 1 + }, + { .variable = VARIABLE_SWAP_LIMIT, + .default_active = TRUE + }, + + {} + } + } +}; G_DEFINE_TYPE_WITH_CODE (GimpDashboard, gimp_dashboard, GIMP_TYPE_EDITOR, @@ -100,27 +451,47 @@ gimp_dashboard_class_init (GimpDashboardClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - object_class->dispose = gimp_dashboard_dispose; - object_class->finalize = gimp_dashboard_finalize; + object_class->constructed = gimp_dashboard_constructed; + object_class->dispose = gimp_dashboard_dispose; + object_class->finalize = gimp_dashboard_finalize; - widget_class->map = gimp_dashboard_map; - widget_class->unmap = gimp_dashboard_unmap; + widget_class->map = gimp_dashboard_map; + widget_class->unmap = gimp_dashboard_unmap; + + g_type_class_add_private (klass, sizeof (GimpDashboardPrivate)); } static void gimp_dashboard_init (GimpDashboard *dashboard) { - GtkWidget *box; - GtkWidget *vbox; - GtkWidget *frame; - GtkWidget *table; - GtkWidget *meter; - GtkWidget *label; - gint content_spacing; + GimpDashboardPrivate *priv; + GtkWidget *box; + GtkWidget *vbox; + GtkWidget *expander; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *image; + GtkWidget *menu; + GtkWidget *item; + GtkWidget *frame; + GtkWidget *vbox2; + GtkWidget *meter; + GtkWidget *table; + GtkWidget *label; + gint content_spacing; + Group group; + gint field; - dashboard->update_interval = DEFAULT_UPDATE_INTERVAL; - dashboard->history_duration = DEFAULT_HISTORY_DURATION; - dashboard->low_swap_space_warning = DEFAULT_LOW_SWAP_SPACE_WARNING; + priv = dashboard->priv = G_TYPE_INSTANCE_GET_PRIVATE (dashboard, + GIMP_TYPE_DASHBOARD, + GimpDashboardPrivate); + + g_mutex_init (&priv->mutex); + g_cond_init (&priv->cond); + + priv->update_interval = DEFAULT_UPDATE_INTERVAL; + priv->history_duration = DEFAULT_HISTORY_DURATION; + priv->low_swap_space_warning = DEFAULT_LOW_SWAP_SPACE_WARNING; gtk_widget_style_get (GTK_WIDGET (dashboard), "content-spacing", &content_spacing, @@ -140,166 +511,183 @@ gimp_dashboard_init (GimpDashboard *dashboard) gtk_container_add (GTK_CONTAINER (box), vbox); gtk_widget_show (vbox); + /* construct the groups */ + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; - /* cache frame */ - frame = gimp_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); - gtk_widget_show (frame); + group_data->n_fields = 0; + group_data->n_meter_values = 0; - /* cache frame label */ - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_frame_set_label_widget (GTK_FRAME (frame), box); - gtk_widget_show (box); + for (field = 0; group_info->fields[field].variable; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; - label = gtk_label_new (_("Cache")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gimp_label_set_attributes (GTK_LABEL (label), - PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, - -1); - gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); - gtk_widget_show (label); + group_data->n_fields++; + group_data->n_meter_values = MAX (group_data->n_meter_values, + field_info->meter_value); + } - /* cache table */ - table = gtk_table_new (3, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), content_spacing); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_container_add (GTK_CONTAINER (frame), table); - gtk_widget_show (table); + group_data->fields = g_new0 (FieldData, group_data->n_fields); - /* cache meter */ - meter = dashboard->cache_meter = gimp_meter_new (1); - gimp_meter_set_color (GIMP_METER (meter), - 0, &(GimpRGB) CACHE_OCCUPIED_COLOR); - gimp_meter_set_history_resolution (GIMP_METER (meter), - dashboard->update_interval / 1000.0); - gimp_meter_set_history_duration (GIMP_METER (meter), - dashboard->history_duration / 1000.0); - gtk_table_attach (GTK_TABLE (table), meter, 0, 2, 0, 1, - GTK_EXPAND | GTK_FILL, 0, 1, 0); - gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2 * content_spacing); - gtk_widget_show (meter); + /* group expander */ + expander = gtk_expander_new (NULL); + group_data->expander = GTK_EXPANDER (expander); + gtk_expander_set_expanded (GTK_EXPANDER (expander), + group_info->default_expanded); + gtk_expander_set_label_fill (GTK_EXPANDER (expander), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0); + gtk_widget_show (expander); - /* cache occupied field */ - label = gtk_label_new (_("Occupied:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, - GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + g_object_set_data (G_OBJECT (expander), + "gimp-dashboard-group", GINT_TO_POINTER (group)); + g_signal_connect_swapped (expander, "button-press-event", + G_CALLBACK (gimp_dashboard_group_expander_button_press), + dashboard); - label = dashboard->cache_occupied_label = gtk_label_new (_("N/A")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 1, 2, 1, 2, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group expander label box */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gimp_help_set_help_data (hbox, + g_dgettext (NULL, group_info->description), + NULL); + gtk_expander_set_label_widget (GTK_EXPANDER (expander), hbox); + gtk_widget_show (hbox); - /* cache limit field */ - label = gtk_label_new (_("Limit:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, - GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group expander label */ + label = gtk_label_new (g_dpgettext2 (NULL, "dashboard-group", + group_info->title)); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); - label = dashboard->cache_limit_label = gtk_label_new (_("N/A")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 1, 2, 2, 3, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group expander menu button */ + button = gtk_button_new (); + group_data->menu_button = GTK_BUTTON (button); + gimp_help_set_help_data (button, _("Select fields"), + NULL); + gtk_widget_set_can_focus (button, FALSE); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + image = gtk_image_new_from_icon_name (GIMP_ICON_MENU_LEFT, + GTK_ICON_SIZE_MENU); + gtk_image_set_pixel_size (GTK_IMAGE (image), 12); + gtk_image_set_from_icon_name (GTK_IMAGE (image), GIMP_ICON_MENU_LEFT, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); - /* swap frame */ - frame = gimp_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); - gtk_widget_show (frame); + /* group menu */ + menu = gtk_menu_new (); + group_data->menu = GTK_MENU (menu); + gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL); - /* swap frame label */ - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_frame_set_label_widget (GTK_FRAME (frame), box); - gtk_widget_show (box); + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + FieldData *field_data = &group_data->fields[field]; - label = gtk_label_new (_("Swap")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gimp_label_set_attributes (GTK_LABEL (label), - PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, - -1); - gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); - gtk_widget_show (label); + if (field_info->variable != VARIABLE_SEPARATOR) + { + const VariableInfo *variable_info = &variables[field_info->variable]; - /* swap table */ - table = gtk_table_new (4, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), content_spacing); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_container_add (GTK_CONTAINER (frame), table); - gtk_widget_show (table); + item = gtk_check_menu_item_new_with_label ( + g_dpgettext2 (NULL, "dashboard-field", variable_info->title)); + field_data->menu_item = GTK_CHECK_MENU_ITEM (item); + gimp_help_set_help_data (item, + g_dgettext (NULL, variable_info->description), + NULL); - /* swap meter */ - meter = dashboard->swap_meter = gimp_meter_new (2); - gimp_meter_set_color (GIMP_METER (meter), - 0, &(GimpRGB) SWAP_SIZE_COLOR); - gimp_meter_set_color (GIMP_METER (meter), - 1, &(GimpRGB) SWAP_OCCUPIED_COLOR); - gimp_meter_set_history_resolution (GIMP_METER (meter), - dashboard->update_interval / 1000.0); - gimp_meter_set_history_duration (GIMP_METER (meter), - dashboard->history_duration / 1000.0); - gimp_meter_set_led_color (GIMP_METER (meter), - &(GimpRGB) SWAP_LED_COLOR); - gtk_table_attach (GTK_TABLE (table), meter, 0, 2, 0, 1, - GTK_EXPAND | GTK_FILL, 0, 1, 0); - gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2 * content_spacing); - gtk_widget_show (meter); + g_object_set_data (G_OBJECT (item), + "gimp-dashboard-group", GINT_TO_POINTER (group)); + g_object_set_data (G_OBJECT (item), + "gimp-dashboard-field", GINT_TO_POINTER (field)); + g_signal_connect_swapped (item, "toggled", + G_CALLBACK (gimp_dashboard_group_menu_item_toggled), + dashboard); - /* swap occupied field */ - label = gtk_label_new (_("Occupied:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, - GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + gimp_dashboard_field_set_active (dashboard, group, field, + field_info->default_active); + } + else + { + item = gtk_separator_menu_item_new (); + } - label = dashboard->swap_occupied_label = gtk_label_new (_("N/A")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 1, 2, 1, 2, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + } - /* swap size field */ - label = gtk_label_new (_("Size:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, - GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group frame */ + frame = gimp_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (expander), frame); + gtk_widget_show (frame); - label = dashboard->swap_size_label = gtk_label_new (_("N/A")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 1, 2, 2, 3, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group vbox */ + vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2 * content_spacing); + gtk_container_add (GTK_CONTAINER (frame), vbox2); + gtk_widget_show (vbox2); - /* swap limit field */ - label = gtk_label_new (_("Limit:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, - GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + /* group meter */ + if (group_info->has_meter) + { + meter = gimp_meter_new (group_data->n_meter_values); + group_data->meter = GIMP_METER (meter); + gimp_help_set_help_data (meter, + g_dgettext (NULL, group_info->description), + NULL); + gimp_meter_set_history_resolution (GIMP_METER (meter), + priv->update_interval / 1000.0); + gimp_meter_set_history_duration (GIMP_METER (meter), + priv->history_duration / 1000.0); + gtk_box_pack_start (GTK_BOX (vbox2), meter, FALSE, FALSE, 0); + gtk_widget_show (meter); - label = dashboard->swap_limit_label = gtk_label_new (_("N/A")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 1, 2, 3, 4, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - gtk_widget_show (label); + if (group_info->meter_led) + { + gimp_meter_set_led_color (GIMP_METER (meter), + &variables[group_info->meter_led].color); + } + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; - /* sampler thread */ - g_mutex_init (&dashboard->mutex); - g_cond_init (&dashboard->cond); + if (field_info->meter_value) + { + const VariableInfo *variable_info = &variables[field_info->variable]; - /* we use a separate thread for sampling, so that data is sampled even when + gimp_meter_set_value_color (GIMP_METER (meter), + field_info->meter_value - 1, + &variable_info->color); + } + } + } + + /* group table */ + table = gtk_table_new (1, 1, FALSE); + group_data->table = GTK_TABLE (table); + gtk_table_set_row_spacings (GTK_TABLE (table), content_spacing); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + gimp_dashboard_update_group (dashboard, group); + } + + /* sampler thread + * + * we use a separate thread for sampling, so that data is sampled even when * the main thread is busy */ - dashboard->thread = g_thread_new ("dashboard", - (GThreadFunc) gimp_dashboard_sample, - dashboard); + priv->thread = g_thread_new ("dashboard", + (GThreadFunc) gimp_dashboard_sample, + dashboard); } static void @@ -315,32 +703,44 @@ gimp_dashboard_docked_iface_init (GimpDockedInterface *iface) } static void -gimp_dashboard_dispose (GObject *object) +gimp_dashboard_constructed (GObject *object) { GimpDashboard *dashboard = GIMP_DASHBOARD (object); - if (dashboard->thread) + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_editor_add_action_button (GIMP_EDITOR (dashboard), "dashboard", + "dashboard-reset", NULL); +} + +static void +gimp_dashboard_dispose (GObject *object) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (object); + GimpDashboardPrivate *priv = dashboard->priv; + + if (priv->thread) { - g_mutex_lock (&dashboard->mutex); + g_mutex_lock (&priv->mutex); - dashboard->quit = TRUE; - g_cond_signal (&dashboard->cond); + priv->quit = TRUE; + g_cond_signal (&priv->cond); - g_mutex_unlock (&dashboard->mutex); + g_mutex_unlock (&priv->mutex); - g_clear_pointer (&dashboard->thread, g_thread_join); + g_clear_pointer (&priv->thread, g_thread_join); } - if (dashboard->timeout_id) + if (priv->update_idle_id) { - g_source_remove (dashboard->timeout_id); - dashboard->timeout_id = 0; + g_source_remove (priv->update_idle_id); + priv->update_idle_id = 0; } - if (dashboard->low_swap_space_idle_id) + if (priv->low_swap_space_idle_id) { - g_source_remove (dashboard->low_swap_space_idle_id); - dashboard->low_swap_space_idle_id = 0; + g_source_remove (priv->low_swap_space_idle_id); + priv->low_swap_space_idle_id = 0; } G_OBJECT_CLASS (parent_class)->dispose (object); @@ -349,10 +749,15 @@ gimp_dashboard_dispose (GObject *object) static void gimp_dashboard_finalize (GObject *object) { - GimpDashboard *dashboard = GIMP_DASHBOARD (object); + GimpDashboard *dashboard = GIMP_DASHBOARD (object); + GimpDashboardPrivate *priv = dashboard->priv; + gint i; - g_mutex_clear (&dashboard->mutex); - g_cond_clear (&dashboard->cond); + for (i = FIRST_GROUP; i < N_GROUPS; i++) + g_free (priv->groups[i].fields); + + g_mutex_clear (&priv->mutex); + g_cond_clear (&priv->cond); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -364,25 +769,25 @@ gimp_dashboard_map (GtkWidget *widget) GTK_WIDGET_CLASS (parent_class)->map (widget); - if (! dashboard->timeout_id) - { - dashboard->timeout_id = g_timeout_add (dashboard->update_interval, - (GSourceFunc) gimp_dashboard_update, - dashboard); - } + gimp_dashboard_update (dashboard); } static void gimp_dashboard_unmap (GtkWidget *widget) { - GimpDashboard *dashboard = GIMP_DASHBOARD (widget); + GimpDashboard *dashboard = GIMP_DASHBOARD (widget); + GimpDashboardPrivate *priv = dashboard->priv; - if (dashboard->timeout_id) + g_mutex_lock (&priv->mutex); + + if (priv->update_idle_id) { - g_source_remove (dashboard->timeout_id); - dashboard->timeout_id = 0; + g_source_remove (priv->update_idle_id); + priv->update_idle_id = 0; } + g_mutex_unlock (&priv->mutex); + GTK_WIDGET_CLASS (parent_class)->unmap (widget); } @@ -394,8 +799,10 @@ static void gimp_dashboard_set_aux_info (GimpDocked *docked, GList *aux_info) { - GimpDashboard *dashboard = GIMP_DASHBOARD (docked); - GList *list; + GimpDashboard *dashboard = GIMP_DASHBOARD (docked); + GimpDashboardPrivate *priv = dashboard->priv; + gchar *name; + GList *list; parent_docked_iface->set_aux_info (docked, aux_info); @@ -432,269 +839,447 @@ gimp_dashboard_set_aux_info (GimpDocked *docked, gimp_dashboard_set_low_swap_space_warning (dashboard, ! strcmp (aux->value, "yes")); } + else + { + Group group; + gint field; + + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; + + name = g_strdup_printf ("%s-expanded", group_info->name); + + if (! strcmp (aux->name, name)) + { + gboolean expanded = ! strcmp (aux->value, "yes"); + + gtk_expander_set_expanded (group_data->expander, expanded); + + g_free (name); + goto next_aux_info; + } + + g_free (name); + + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + + if (field_info->variable != VARIABLE_SEPARATOR) + { + const VariableInfo *variable_info = &variables[field_info->variable]; + + name = g_strdup_printf ("%s-%s-active", + group_info->name, + variable_info->name); + + if (! strcmp (aux->name, name)) + { + gboolean active = ! strcmp (aux->value, "yes"); + + gimp_dashboard_field_set_active (dashboard, + group, field, + active); + + g_free (name); + goto next_aux_info; + } + + g_free (name); + } + } + } + } +next_aux_info: ; } + + gimp_dashboard_update_groups (dashboard); } static GList * gimp_dashboard_get_aux_info (GimpDocked *docked) { - GimpDashboard *dashboard = GIMP_DASHBOARD (docked); - GList *aux_info; - GimpSessionInfoAux *aux; - gchar *value; + GimpDashboard *dashboard = GIMP_DASHBOARD (docked); + GimpDashboardPrivate *priv = dashboard->priv; + GList *aux_info; + GimpSessionInfoAux *aux; + gchar *name; + gchar *value; + Group group; + gint field; aux_info = parent_docked_iface->get_aux_info (docked); - value = g_strdup_printf ("%d", dashboard->update_interval); - aux = gimp_session_info_aux_new (AUX_INFO_UPDATE_INTERVAL, value); - aux_info = g_list_append (aux_info, aux); - g_free (value); + if (priv->update_interval != DEFAULT_UPDATE_INTERVAL) + { + value = g_strdup_printf ("%d", priv->update_interval); + aux = gimp_session_info_aux_new (AUX_INFO_UPDATE_INTERVAL, value); + aux_info = g_list_append (aux_info, aux); + g_free (value); + } - value = g_strdup_printf ("%d", dashboard->history_duration); - aux = gimp_session_info_aux_new (AUX_INFO_HISTORY_DURATION, value); - aux_info = g_list_append (aux_info, aux); - g_free (value); + if (priv->history_duration != DEFAULT_HISTORY_DURATION) + { + value = g_strdup_printf ("%d", priv->history_duration); + aux = gimp_session_info_aux_new (AUX_INFO_HISTORY_DURATION, value); + aux_info = g_list_append (aux_info, aux); + g_free (value); + } - value = dashboard->low_swap_space_warning ? "yes" : "no"; - aux = gimp_session_info_aux_new (AUX_INFO_LOW_SWAP_SPACE_WARNING, value); - aux_info = g_list_append (aux_info, aux); + if (priv->low_swap_space_warning != DEFAULT_LOW_SWAP_SPACE_WARNING) + { + value = priv->low_swap_space_warning ? "yes" : "no"; + aux = gimp_session_info_aux_new (AUX_INFO_LOW_SWAP_SPACE_WARNING, value); + aux_info = g_list_append (aux_info, aux); + } + + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; + gboolean expanded = gtk_expander_get_expanded (group_data->expander); + + if (expanded != group_info->default_expanded) + { + name = g_strdup_printf ("%s-expanded", group_info->name); + value = expanded ? "yes" : "no"; + aux = gimp_session_info_aux_new (name, value); + aux_info = g_list_append (aux_info, aux); + g_free (name); + } + + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + FieldData *field_data = &group_data->fields[field]; + gboolean active = field_data->active; + + if (field_info->variable != VARIABLE_SEPARATOR) + { + const VariableInfo *variable_info = &variables[field_info->variable]; + + if (active != field_info->default_active) + { + name = g_strdup_printf ("%s-%s-active", + group_info->name, + variable_info->name); + value = active ? "yes" : "no"; + aux = gimp_session_info_aux_new (name, value); + aux_info = g_list_append (aux_info, aux); + g_free (name); + } + } + } + } return aux_info; } static gboolean -gimp_dashboard_update (GimpDashboard *dashboard) +gimp_dashboard_group_expander_button_press (GimpDashboard *dashboard, + GdkEventButton *bevent, + GtkWidget *widget) { - /* cache */ - { - guint64 cache_occupied; - guint64 cache_limit; + GimpDashboardPrivate *priv = dashboard->priv; + Group group; + GroupData *group_data; + GtkAllocation expander_allocation; + GtkAllocation allocation; - g_object_get (gegl_stats (), - "tile-cache-total", &cache_occupied, - NULL); - g_object_get (gegl_config (), - "tile-cache-size", &cache_limit, - NULL); + group = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "gimp-dashboard-group")); + group_data = &priv->groups[group]; - gimp_meter_set_range (GIMP_METER (dashboard->cache_meter), - 0.0, cache_limit); + gtk_widget_get_allocation (GTK_WIDGET (group_data->expander), + &expander_allocation); + gtk_widget_get_allocation (GTK_WIDGET (group_data->menu_button), + &allocation); - gimp_dashboard_label_set_size (GTK_LABEL (dashboard->cache_occupied_label), - cache_occupied, cache_limit); - gimp_dashboard_label_set_size (GTK_LABEL (dashboard->cache_limit_label), - cache_limit, 0); - } + allocation.x -= expander_allocation.x; + allocation.y -= expander_allocation.y; - /* swap */ - { - guint64 swap_occupied; - guint64 swap_size; - guint64 swap_limit; - gboolean swap_busy; - - g_mutex_lock (&dashboard->mutex); - - g_object_get (gegl_stats (), - "swap-total", &swap_occupied, - "swap-file-size", &swap_size, - "swap-busy", &swap_busy, - NULL); - swap_limit = gimp_dashboard_get_swap_limit (swap_size); - - g_mutex_unlock (&dashboard->mutex); - - gimp_meter_set_range (GIMP_METER (dashboard->swap_meter), - 0.0, swap_limit ? swap_limit : swap_size); - gimp_meter_set_led_visible (GIMP_METER (dashboard->swap_meter), swap_busy); - - gimp_dashboard_label_set_size (GTK_LABEL (dashboard->swap_occupied_label), - swap_occupied, swap_limit); - gimp_dashboard_label_set_size (GTK_LABEL (dashboard->swap_size_label), - swap_size, swap_limit); - - if (swap_limit) - { - gimp_dashboard_label_set_size (GTK_LABEL (dashboard->swap_limit_label), - swap_limit, 0); - } - else - { - gimp_dashboard_label_set_text (GTK_LABEL (dashboard->swap_limit_label), - _("N/A")); - } - } - - return G_SOURCE_CONTINUE; -} - -static gboolean -gimp_dashboard_low_swap_space (GimpDashboard *dashboard) -{ - if (dashboard->gimp) + if (bevent->button == 1 && + bevent->x >= allocation.x && + bevent->x < allocation.x + allocation.width && + bevent->y >= allocation.y && + bevent->y < allocation.y + allocation.height) { - GdkScreen *screen; - gint monitor; + gtk_menu_popup (group_data->menu, + NULL, NULL, + gimp_dashboard_group_menu_position, + group_data->menu_button, + bevent->button, bevent->time); - monitor = gimp_get_monitor_at_pointer (&screen); - - gimp_window_strategy_show_dockable_dialog ( - GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (dashboard->gimp)), - dashboard->gimp, - gimp_dialog_factory_get_singleton (), - screen, monitor, - "gimp-dashboard"); - - gimp_dashboard_update (dashboard); - - g_mutex_lock (&dashboard->mutex); - - dashboard->low_swap_space_idle_id = 0; - - g_mutex_unlock (&dashboard->mutex); + return TRUE; } - return G_SOURCE_REMOVE; + return FALSE; +} + +static void +gimp_dashboard_group_menu_item_toggled (GimpDashboard *dashboard, + GtkCheckMenuItem *item) +{ + GimpDashboardPrivate *priv = dashboard->priv; + Group group; + GroupData *group_data; + gint field; + FieldData *field_data; + + group = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), + "gimp-dashboard-group")); + group_data = &priv->groups[group]; + + field = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), + "gimp-dashboard-field")); + field_data = &group_data->fields[field]; + + field_data->active = gtk_check_menu_item_get_active (item); + + gimp_dashboard_update_group (dashboard, group); } static gpointer gimp_dashboard_sample (GimpDashboard *dashboard) { - GimpDashboardUpdateInteval update_interval; - gint64 end_time; - gboolean seen_low_swap_space = FALSE; + GimpDashboardPrivate *priv = dashboard->priv; + GimpDashboardUpdateInteval update_interval; + gint64 end_time; + gboolean seen_low_swap_space = FALSE; - g_mutex_lock (&dashboard->mutex); + g_mutex_lock (&priv->mutex); - update_interval = dashboard->update_interval; + update_interval = priv->update_interval; end_time = g_get_monotonic_time (); - while (! dashboard->quit) + while (! priv->quit) { - if (! g_cond_wait_until (&dashboard->cond, &dashboard->mutex, end_time)) + if (! g_cond_wait_until (&priv->cond, &priv->mutex, end_time) || + priv->update_now) { - /* cache */ - { - guint64 cache_occupied; - gdouble sample[1]; + gboolean varaibles_changed = FALSE; + Variable variable; + Group group; + gint field; - g_object_get (gegl_stats (), - "tile-cache-total", &cache_occupied, - NULL); + /* sample all varaibles */ + for (variable = FIRST_VARIABLE; variable < N_VARIABLES; variable++) + { + const VariableInfo *variable_info = &variables[variable]; + const VariableData *variable_data = &priv->variables[variable]; + VariableData prev_variable_data = *variable_data; - sample[0] = cache_occupied; - gimp_meter_add_sample (GIMP_METER (dashboard->cache_meter), sample); - } + variable_info->sample_func (dashboard, variable); - /* swap */ - { - guint64 swap_occupied; - guint64 swap_size; - gdouble sample[2]; + varaibles_changed = varaibles_changed || + memcmp (variable_data, &prev_variable_data, + sizeof (VariableData)); + } - g_object_get (gegl_stats (), - "swap-total", &swap_occupied, - "swap-file-size", &swap_size, - NULL); + /* add samples to meters */ + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; + gdouble *sample; - sample[0] = swap_size; - sample[1] = swap_occupied; - gimp_meter_add_sample (GIMP_METER (dashboard->swap_meter), sample); + if (! group_info->has_meter) + continue; - if (dashboard->low_swap_space_warning) - { - guint64 swap_limit = gimp_dashboard_get_swap_limit (swap_size); + sample = g_new (gdouble, group_data->n_meter_values); - if (swap_limit) - { - if (! seen_low_swap_space && - swap_occupied >= LOW_SWAP_SPACE_WARNING_ON * swap_limit) - { - if (! dashboard->low_swap_space_idle_id) - { - dashboard->low_swap_space_idle_id = - g_idle_add_full (G_PRIORITY_HIGH, - (GSourceFunc) gimp_dashboard_low_swap_space, - dashboard, NULL); - } + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; - seen_low_swap_space = TRUE; - } - else if (seen_low_swap_space && - swap_occupied <= LOW_SWAP_SPACE_WARNING_OFF * swap_limit) - { - if (dashboard->low_swap_space_idle_id) - { - g_source_remove (dashboard->low_swap_space_idle_id); + if (field_info->meter_value) + { + sample[field_info->meter_value - 1] = + gimp_dashboard_variable_to_double (dashboard, + field_info->variable); + } + } - dashboard->low_swap_space_idle_id = 0; - } + gimp_meter_add_sample (group_data->meter, sample); - seen_low_swap_space = FALSE; - } - } - } - } + g_free (sample); + } + + if (varaibles_changed) + { + /* check for low swap space */ + if (priv->low_swap_space_warning && + priv->variables[VARIABLE_SWAP_OCCUPIED].available && + priv->variables[VARIABLE_SWAP_LIMIT].available) + { + guint64 swap_occupied; + guint64 swap_limit; + + swap_occupied = priv->variables[VARIABLE_SWAP_OCCUPIED].value.size; + swap_limit = priv->variables[VARIABLE_SWAP_LIMIT].value.size; + + if (! seen_low_swap_space && + swap_occupied >= LOW_SWAP_SPACE_WARNING_ON * swap_limit) + { + if (! priv->low_swap_space_idle_id) + { + priv->low_swap_space_idle_id = + g_idle_add_full (G_PRIORITY_HIGH, + (GSourceFunc) gimp_dashboard_low_swap_space, + dashboard, NULL); + } + + seen_low_swap_space = TRUE; + } + else if (seen_low_swap_space && + swap_occupied <= LOW_SWAP_SPACE_WARNING_OFF * swap_limit) + { + if (priv->low_swap_space_idle_id) + { + g_source_remove (priv->low_swap_space_idle_id); + + priv->low_swap_space_idle_id = 0; + } + + seen_low_swap_space = FALSE; + } + } + + /* enqueue update source */ + if (! priv->update_idle_id && + gtk_widget_get_mapped (GTK_WIDGET (dashboard))) + { + priv->update_idle_id = g_idle_add_full ( + G_PRIORITY_DEFAULT, + (GSourceFunc) gimp_dashboard_update, + dashboard, NULL); + } + } + + priv->update_now = FALSE; end_time = g_get_monotonic_time () + update_interval * G_TIME_SPAN_SECOND / 1000; } - if (dashboard->update_interval != update_interval) + if (priv->update_interval != update_interval) { - update_interval = dashboard->update_interval; + update_interval = priv->update_interval; end_time = g_get_monotonic_time () + update_interval * G_TIME_SPAN_SECOND / 1000; } } - g_mutex_unlock (&dashboard->mutex); + g_mutex_unlock (&priv->mutex); return NULL; } -static void -gimp_dashboard_label_set_text (GtkLabel *label, - const gchar *text) +static gboolean +gimp_dashboard_update (GimpDashboard *dashboard) { - /* the strcmp() reduces the overhead of gtk_label_set_text() when the text - * isn't changed - */ - if (strcmp (gtk_label_get_text (label), text)) - gtk_label_set_text (label, text); -} + GimpDashboardPrivate *priv = dashboard->priv; + Group group; -static void -gimp_dashboard_label_set_size (GtkLabel *label, - guint64 size, - guint64 limit) -{ - gchar *text; + g_mutex_lock (&priv->mutex); - text = g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS); - - if (limit) + for (group = FIRST_GROUP; group < N_GROUPS; group++) { - gchar *temp; + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; - temp = g_strdup_printf ("%s (%d%%)", text, ROUND (100.0 * size / limit)); - g_free (text); + if (group_info->has_meter && group_info->meter_led) + { + gboolean active; - text = temp; + active = gimp_dashboard_variable_to_boolean (dashboard, + group_info->meter_led); + + gimp_meter_set_led_active (group_data->meter, active); + } + + gimp_dashboard_update_group_values (dashboard, group); } - gimp_dashboard_label_set_text (label, text); + priv->update_idle_id = 0; - g_free (text); + g_mutex_unlock (&priv->mutex); + + return G_SOURCE_REMOVE; } -static guint64 -gimp_dashboard_get_swap_limit (guint64 swap_size) +static gboolean +gimp_dashboard_low_swap_space (GimpDashboard *dashboard) { - static guint64 free_space = 0; - static gboolean has_free_space = FALSE; - static gint64 last_check_time = 0; - gint64 time; + GimpDashboardPrivate *priv = dashboard->priv; + + if (priv->gimp) + { + GdkScreen *screen; + gint monitor; + gint field; + + gtk_expander_set_expanded (priv->groups[GROUP_SWAP].expander, TRUE); + + for (field = 0; field < priv->groups[GROUP_SWAP].n_fields; field++) + { + const FieldInfo *field_info = &groups[GROUP_SWAP].fields[field]; + + if (field_info->variable == VARIABLE_SWAP_OCCUPIED || + field_info->variable == VARIABLE_SWAP_LIMIT) + { + gimp_dashboard_field_set_active (dashboard, + GROUP_SWAP, field, TRUE); + } + } + + gimp_dashboard_update_groups (dashboard); + + monitor = gimp_get_monitor_at_pointer (&screen); + + gimp_window_strategy_show_dockable_dialog ( + GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (priv->gimp)), + priv->gimp, + gimp_dialog_factory_get_singleton (), + screen, monitor, + "gimp-dashboard"); + + g_mutex_lock (&priv->mutex); + + priv->low_swap_space_idle_id = 0; + + g_mutex_unlock (&priv->mutex); + } + + return G_SOURCE_REMOVE; +} + +static void +gimp_dashboard_sample_gegl_config (GimpDashboard *dashboard, + Variable variable) +{ + gimp_dashboard_sample_object (dashboard, G_OBJECT (gegl_config ()), variable); +} + +static void +gimp_dashboard_sample_gegl_stats (GimpDashboard *dashboard, + Variable variable) +{ + gimp_dashboard_sample_object (dashboard, G_OBJECT (gegl_stats ()), variable); +} + +static void +gimp_dashboard_sample_swap_limit (GimpDashboard *dashboard, + Variable variable) +{ + GimpDashboardPrivate *priv = dashboard->priv; + VariableData *variable_data = &priv->variables[variable]; + static guint64 free_space = 0; + static gboolean has_free_space = FALSE; + static gint64 last_check_time = 0; + gint64 time; /* we don't have a config option for limiting the swap size, so we simply * return the free space available on the filesystem containing the swap @@ -742,10 +1327,522 @@ gimp_dashboard_get_swap_limit (guint64 swap_size) last_check_time = time; } - /* the swap limit is the sum of free_space and swap_size, since the swap - * itself occupies space in the filesystem - */ - return has_free_space ? free_space + swap_size : 0; + variable_data->available = has_free_space; + + if (has_free_space) + { + variable_data->value.size = free_space; + + if (priv->variables[VARIABLE_SWAP_SIZE].available) + { + /* the swap limit is the sum of free_space and swap_size, since the + * swap itself occupies space in the filesystem + */ + variable_data->value.size += + priv->variables[VARIABLE_SWAP_SIZE].value.size; + } + } +} + +static void +gimp_dashboard_sample_object (GimpDashboard *dashboard, + GObject *object, + Variable variable) +{ + GimpDashboardPrivate *priv = dashboard->priv; + GObjectClass *klass = G_OBJECT_GET_CLASS (object); + const VariableInfo *variable_info = &variables[variable]; + VariableData *variable_data = &priv->variables[variable]; + + variable_data->available = FALSE; + + switch (variable_info->type) + { + case VARIABLE_TYPE_BOOLEAN: + if (g_object_class_find_property (klass, variable_info->data)) + { + variable_data->available = TRUE; + + g_object_get (object, + variable_info->data, &variable_data->value.boolean, + NULL); + } + break; + + case VARIABLE_TYPE_SIZE: + if (g_object_class_find_property (klass, variable_info->data)) + { + variable_data->available = TRUE; + + g_object_get (object, + variable_info->data, &variable_data->value.size, + NULL); + } + break; + + case VARIABLE_TYPE_SIZE_RATIO: + { + const gchar *antecedent = variable_info->data; + const gchar *consequent = antecedent + strlen (antecedent) + 1; + + if (g_object_class_find_property (klass, antecedent) && + g_object_class_find_property (klass, consequent)) + { + variable_data->available = TRUE; + + g_object_get (object, + antecedent, &variable_data->value.size_ratio.antecedent, + consequent, &variable_data->value.size_ratio.consequent, + NULL); + } + } + break; + + case VARIABLE_TYPE_INT_RATIO: + { + const gchar *antecedent = variable_info->data; + const gchar *consequent = antecedent + strlen (antecedent) + 1; + + if (g_object_class_find_property (klass, antecedent) && + g_object_class_find_property (klass, consequent)) + { + variable_data->available = TRUE; + + g_object_get (object, + antecedent, &variable_data->value.int_ratio.antecedent, + consequent, &variable_data->value.int_ratio.consequent, + NULL); + } + } + break; + } +} + +static void +gimp_dashboard_container_remove (GtkWidget *widget, + GtkContainer *container) +{ + gtk_container_remove (container, widget); +} + +static void +gimp_dashboard_group_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + gimp_button_menu_position (user_data, menu, GTK_POS_LEFT, x, y); +} + +static void +gimp_dashboard_update_groups (GimpDashboard *dashboard) +{ + Group group; + + for (group = FIRST_GROUP; group < N_GROUPS; group++) + gimp_dashboard_update_group (dashboard, group); +} + +static void +gimp_dashboard_update_group (GimpDashboard *dashboard, + Group group) +{ + GimpDashboardPrivate *priv = dashboard->priv; + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; + gint n_rows; + gboolean add_separator; + gint field; + + n_rows = 0; + add_separator = FALSE; + + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + const FieldData *field_data = &group_data->fields[field]; + + if (field_info->variable != VARIABLE_SEPARATOR) + { + if (group_info->has_meter && field_info->meter_value) + { + gimp_meter_set_value_active (group_data->meter, + field_info->meter_value - 1, + field_data->active); + } + + if (field_data->active) + { + if (add_separator) + { + add_separator = FALSE; + n_rows++; + } + + n_rows++; + } + } + else + { + if (n_rows > 0) + add_separator = TRUE; + } + } + + gtk_container_foreach (GTK_CONTAINER (group_data->table), + (GtkCallback) gimp_dashboard_container_remove, + group_data->table); + gtk_table_resize (group_data->table, MAX (n_rows, 1), 3); + + n_rows = 0; + add_separator = FALSE; + + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + FieldData *field_data = &group_data->fields[field]; + + if (field_info->variable != VARIABLE_SEPARATOR) + { + const VariableInfo *variable_info = &variables[field_info->variable]; + GtkWidget *separator; + GtkWidget *color_area; + GtkWidget *label; + const gchar *description; + gchar *str; + + if (! field_data->active) + continue; + + description = g_dgettext (NULL, variable_info->description); + + if (add_separator) + { + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_table_attach (group_data->table, separator, + 0, 3, n_rows, n_rows + 1, + GTK_EXPAND | GTK_FILL, 0, + 0, 0); + gtk_widget_show (separator); + + add_separator = FALSE; + n_rows++; + } + + if (variable_info->color.a) + { + color_area = gimp_color_area_new (&variable_info->color, + GIMP_COLOR_AREA_FLAT, 0); + gimp_help_set_help_data (color_area, description, + NULL); + gtk_widget_set_size_request (color_area, 5, 5); + gtk_table_attach (group_data->table, color_area, + 0, 1, n_rows, n_rows + 1, + 0, 0, + 0, 0); + gtk_widget_show (color_area); + } + + str = g_strdup_printf ("%s:", + g_dpgettext2 (NULL, "dashboard-variable", + variable_info->title)); + + label = gtk_label_new (str); + gimp_help_set_help_data (label, description, + NULL); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (group_data->table, label, + 1, 2, n_rows, n_rows + 1, + GTK_FILL, 0, + 0, 0); + gtk_widget_show (label); + + g_free (str); + + label = gtk_label_new (NULL); + field_data->value_label = GTK_LABEL (label); + gimp_help_set_help_data (label, description, + NULL); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (group_data->table, label, + 2, 3, n_rows, n_rows + 1, + GTK_EXPAND | GTK_FILL, 0, + 0, 0); + gtk_widget_show (label); + + n_rows++; + } + else + { + if (n_rows > 0) + add_separator = TRUE; + } + } + + g_mutex_lock (&priv->mutex); + + gimp_dashboard_update_group_values (dashboard, group); + + g_mutex_unlock (&priv->mutex); +} + +static void +gimp_dashboard_update_group_values (GimpDashboard *dashboard, + Group group) +{ + GimpDashboardPrivate *priv = dashboard->priv; + const GroupInfo *group_info = &groups[group]; + GroupData *group_data = &priv->groups[group]; + gdouble limit = 0.0; + gint field; + + if (group_info->has_meter) + { + if (group_info->meter_limit) + { + const VariableData *variable_data = &priv->variables[group_info->meter_limit]; + + if (variable_data->available) + { + limit = gimp_dashboard_variable_to_double (dashboard, + group_info->meter_limit); + } + else + { + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + const FieldData *field_data = &group_data->fields[field]; + + if (field_info->meter_value && field_data->active) + { + gdouble value; + + value = gimp_dashboard_variable_to_double (dashboard, + field_info->variable); + + limit = MAX (limit, value); + } + } + } + + gimp_meter_set_range (group_data->meter, 0.0, limit); + } + + if (group_info->meter_led) + { + gimp_meter_set_led_active ( + group_data->meter, + gimp_dashboard_variable_to_boolean (dashboard, + group_info->meter_led)); + } + } + + for (field = 0; field < group_data->n_fields; field++) + { + const FieldInfo *field_info = &group_info->fields[field]; + const FieldData *field_data = &group_data->fields[field]; + + if (field_data->active) + { + const VariableInfo *variable_info = &variables[field_info->variable]; + const VariableData *variable_data = &priv->variables[field_info->variable]; + /* Tranlators: "N/A" is an abbreviation for "not available" */ + const gchar *str = C_("dashboard-value", "N/A"); + gboolean free_str = FALSE; + + if (variable_data->available) + { + switch (variable_info->type) + { + case VARIABLE_TYPE_BOOLEAN: + str = variable_data->value.boolean ? C_("dashboard-value", "Yes") : + C_("dashboard-value", "No"); + break; + + case VARIABLE_TYPE_SIZE: + str = g_format_size_full (variable_data->value.size, + G_FORMAT_SIZE_IEC_UNITS); + free_str = TRUE; + break; + + case VARIABLE_TYPE_SIZE_RATIO: + { + if (variable_data->value.size_ratio.consequent) + { + gdouble value; + + value = 100.0 * variable_data->value.size_ratio.antecedent / + variable_data->value.size_ratio.consequent; + + str = g_strdup_printf ("%d%%", SIGNED_ROUND (value)); + free_str = TRUE; + } + } + break; + + case VARIABLE_TYPE_INT_RATIO: + { + gdouble min; + gdouble max; + gdouble antecedent; + gdouble consequent; + + antecedent = variable_data->value.int_ratio.antecedent; + consequent = variable_data->value.int_ratio.consequent; + + min = MIN (ABS (antecedent), ABS (consequent)); + max = MAX (ABS (antecedent), ABS (consequent)); + + if (min) + { + antecedent /= min; + consequent /= min; + } + else if (max) + { + antecedent /= max; + consequent /= max; + } + + if (max) + { + str = g_strdup_printf ("%g:%g", + RINT (100.0 * antecedent) / 100.0, + RINT (100.0 * consequent) / 100.0); + free_str = TRUE; + } + } + break; + } + + if (field_info->meter_value && limit) + { + gdouble value; + gchar *tmp; + + value = gimp_dashboard_variable_to_double (dashboard, + field_info->variable); + + tmp = g_strdup_printf ("%s (%d%%)", + str, + SIGNED_ROUND (100.0 * value / limit)); + + if (free_str) + g_free ((gpointer) str); + + str = tmp; + free_str = TRUE; + } + } + + /* the strcmp() reduces the overhead of gtk_label_set_text() when the + * text hasn't changed + */ + if (strcmp (gtk_label_get_text (field_data->value_label), str)) + gtk_label_set_text (field_data->value_label, str); + + if (free_str) + g_free ((gpointer) str); + } + } +} + +static void +gimp_dashboard_field_set_active (GimpDashboard *dashboard, + Group group, + gint field, + gboolean active) +{ + GimpDashboardPrivate *priv = dashboard->priv; + GroupData *group_data = &priv->groups[group]; + FieldData *field_data = &group_data->fields[field]; + + if (active != field_data->active) + { + field_data->active = active; + + g_signal_handlers_block_by_func (field_data->menu_item, + gimp_dashboard_group_menu_item_toggled, + dashboard); + + gtk_check_menu_item_set_active (field_data->menu_item, active); + + g_signal_handlers_unblock_by_func (field_data->menu_item, + gimp_dashboard_group_menu_item_toggled, + dashboard); + } +} + +static gboolean +gimp_dashboard_variable_to_boolean (GimpDashboard *dashboard, + Variable variable) +{ + GimpDashboardPrivate *priv = dashboard->priv; + const VariableInfo *variable_info = &variables[variable]; + const VariableData *variable_data = &priv->variables[variable]; + + if (variable_data->available) + { + switch (variable_info->type) + { + case VARIABLE_TYPE_BOOLEAN: + return variable_data->value.boolean; + + case VARIABLE_TYPE_SIZE: + return variable_data->value.size > 0; + + case VARIABLE_TYPE_SIZE_RATIO: + return variable_data->value.size_ratio.antecedent != 0 && + variable_data->value.size_ratio.consequent != 0; + + case VARIABLE_TYPE_INT_RATIO: + return variable_data->value.int_ratio.antecedent != 0 && + variable_data->value.int_ratio.consequent != 0; + } + } + + return FALSE; +} + +static gdouble +gimp_dashboard_variable_to_double (GimpDashboard *dashboard, + Variable variable) +{ + GimpDashboardPrivate *priv = dashboard->priv; + const VariableInfo *variable_info = &variables[variable]; + const VariableData *variable_data = &priv->variables[variable]; + + if (variable_data->available) + { + switch (variable_info->type) + { + case VARIABLE_TYPE_BOOLEAN: + return variable_data->value.boolean ? 1.0 : 0.0; + + case VARIABLE_TYPE_SIZE: + return variable_data->value.size; + + case VARIABLE_TYPE_SIZE_RATIO: + if (variable_data->value.size_ratio.consequent) + { + return (gdouble) variable_data->value.size_ratio.antecedent / + (gdouble) variable_data->value.size_ratio.consequent; + } + break; + + case VARIABLE_TYPE_INT_RATIO: + if (variable_data->value.int_ratio.consequent) + { + return (gdouble) variable_data->value.int_ratio.antecedent / + (gdouble) variable_data->value.int_ratio.consequent; + } + break; + } + } + + return 0.0; } @@ -764,72 +1861,151 @@ gimp_dashboard_new (Gimp *gimp, "ui-path", "/dashboard-popup", NULL); - dashboard->gimp = gimp; + dashboard->priv->gimp = gimp; return GTK_WIDGET (dashboard); } +void +gimp_dashboard_reset (GimpDashboard *dashboard) +{ + GimpDashboardPrivate *priv; + Group group; + + g_return_if_fail (GIMP_IS_DASHBOARD (dashboard)); + + priv = dashboard->priv; + + g_mutex_lock (&priv->mutex); + + gegl_reset_stats (); + + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + GroupData *group_data = &priv->groups[group]; + + if (group_data->meter) + gimp_meter_clear_history (group_data->meter); + } + + priv->update_now = TRUE; + g_cond_signal (&priv->cond); + + g_mutex_unlock (&priv->mutex); +} + void gimp_dashboard_set_update_interval (GimpDashboard *dashboard, GimpDashboardUpdateInteval update_interval) { + GimpDashboardPrivate *priv; + g_return_if_fail (GIMP_IS_DASHBOARD (dashboard)); - if (update_interval != dashboard->update_interval) + priv = dashboard->priv; + + if (update_interval != priv->update_interval) { - g_mutex_lock (&dashboard->mutex); + Group group; - dashboard->update_interval = update_interval; + g_mutex_lock (&priv->mutex); - gimp_meter_set_history_resolution (GIMP_METER (dashboard->cache_meter), - update_interval / 1000.0); - gimp_meter_set_history_resolution (GIMP_METER (dashboard->swap_meter), - update_interval / 1000.0); + priv->update_interval = update_interval; - if (dashboard->timeout_id) + for (group = FIRST_GROUP; group < N_GROUPS; group++) { - g_source_remove (dashboard->timeout_id); + GroupData *group_data = &priv->groups[group]; - dashboard->timeout_id = g_timeout_add (update_interval, - (GSourceFunc) gimp_dashboard_update, - dashboard); + if (group_data->meter) + { + gimp_meter_set_history_resolution (group_data->meter, + update_interval / 1000.0); + } } - g_cond_signal (&dashboard->cond); + priv->update_now = TRUE; + g_cond_signal (&priv->cond); - g_mutex_unlock (&dashboard->mutex); + g_mutex_unlock (&priv->mutex); } } +GimpDashboardUpdateInteval +gimp_dashboard_get_update_interval (GimpDashboard *dashboard) +{ + g_return_val_if_fail (GIMP_IS_DASHBOARD (dashboard), DEFAULT_UPDATE_INTERVAL); + + return dashboard->priv->update_interval; +} + void gimp_dashboard_set_history_duration (GimpDashboard *dashboard, GimpDashboardHistoryDuration history_duration) { + GimpDashboardPrivate *priv; + g_return_if_fail (GIMP_IS_DASHBOARD (dashboard)); - if (history_duration != dashboard->history_duration) - { - dashboard->history_duration = history_duration; + priv = dashboard->priv; - gimp_meter_set_history_duration (GIMP_METER (dashboard->cache_meter), - history_duration / 1000.0); - gimp_meter_set_history_duration (GIMP_METER (dashboard->swap_meter), - history_duration / 1000.0); + if (history_duration != priv->history_duration) + { + Group group; + + g_mutex_lock (&priv->mutex); + + priv->history_duration = history_duration; + + for (group = FIRST_GROUP; group < N_GROUPS; group++) + { + GroupData *group_data = &priv->groups[group]; + + if (group_data->meter) + { + gimp_meter_set_history_duration (group_data->meter, + history_duration / 1000.0); + } + } + + priv->update_now = TRUE; + g_cond_signal (&priv->cond); + + g_mutex_unlock (&priv->mutex); } } +GimpDashboardHistoryDuration +gimp_dashboard_get_history_duration (GimpDashboard *dashboard) +{ + g_return_val_if_fail (GIMP_IS_DASHBOARD (dashboard), DEFAULT_HISTORY_DURATION); + + return dashboard->priv->history_duration; +} + void gimp_dashboard_set_low_swap_space_warning (GimpDashboard *dashboard, gboolean low_swap_space_warning) { + GimpDashboardPrivate *priv; + g_return_if_fail (GIMP_IS_DASHBOARD (dashboard)); - if (low_swap_space_warning != dashboard->low_swap_space_warning) + priv = dashboard->priv; + + if (low_swap_space_warning != priv->low_swap_space_warning) { - g_mutex_lock (&dashboard->mutex); + g_mutex_lock (&priv->mutex); - dashboard->low_swap_space_warning = low_swap_space_warning; + priv->low_swap_space_warning = low_swap_space_warning; - g_mutex_unlock (&dashboard->mutex); + g_mutex_unlock (&priv->mutex); } } + +gboolean +gimp_dashboard_get_low_swap_space_warning (GimpDashboard *dashboard) +{ + g_return_val_if_fail (GIMP_IS_DASHBOARD (dashboard), DEFAULT_LOW_SWAP_SPACE_WARNING); + + return dashboard->priv->low_swap_space_warning; +} diff --git a/app/widgets/gimpdashboard.h b/app/widgets/gimpdashboard.h index dc6ee3aca9..e8e92493cb 100644 --- a/app/widgets/gimpdashboard.h +++ b/app/widgets/gimpdashboard.h @@ -33,34 +33,14 @@ #define GIMP_DASHBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DASHBOARD, GimpDashboardClass)) -typedef struct _GimpDashboardClass GimpDashboardClass; +typedef struct _GimpDashboardPrivate GimpDashboardPrivate; +typedef struct _GimpDashboardClass GimpDashboardClass; struct _GimpDashboard { - GimpEditor parent_instance; + GimpEditor parent_instance; - Gimp *gimp; - - GtkWidget *cache_meter; - GtkWidget *cache_occupied_label; - GtkWidget *cache_limit_label; - - GtkWidget *swap_meter; - GtkWidget *swap_occupied_label; - GtkWidget *swap_size_label; - GtkWidget *swap_limit_label; - - gint timeout_id; - gint low_swap_space_idle_id; - - GThread *thread; - GMutex mutex; - GCond cond; - gboolean quit; - - GimpDashboardUpdateInteval update_interval; - GimpDashboardHistoryDuration history_duration; - gboolean low_swap_space_warning; + GimpDashboardPrivate *priv; }; struct _GimpDashboardClass @@ -69,17 +49,24 @@ struct _GimpDashboardClass }; -GType gimp_dashboard_get_type (void) G_GNUC_CONST; +GType gimp_dashboard_get_type (void) G_GNUC_CONST; -GtkWidget * gimp_dashboard_new (Gimp *gimp, - GimpMenuFactory *menu_factory); +GtkWidget * gimp_dashboard_new (Gimp *gimp, + GimpMenuFactory *menu_factory); -void gimp_dashboard_set_update_interval (GimpDashboard *dashboard, - GimpDashboardUpdateInteval update_interval); -void gimp_dashboard_set_history_duration (GimpDashboard *dashboard, - GimpDashboardHistoryDuration history_duration); -void gimp_dashboard_set_low_swap_space_warning (GimpDashboard *dashboard, - gboolean low_swap_space_warning); +void gimp_dashboard_reset (GimpDashboard *dashboard); + +void gimp_dashboard_set_update_interval (GimpDashboard *dashboard, + GimpDashboardUpdateInteval update_interval); +GimpDashboardUpdateInteval gimp_dashboard_get_update_interval (GimpDashboard *dashboard); + +void gimp_dashboard_set_history_duration (GimpDashboard *dashboard, + GimpDashboardHistoryDuration history_duration); +GimpDashboardHistoryDuration gimp_dashboard_get_history_duration (GimpDashboard *dashboard); + +void gimp_dashboard_set_low_swap_space_warning (GimpDashboard *dashboard, + gboolean low_swap_space_warning); +gboolean gimp_dashboard_get_low_swap_space_warning (GimpDashboard *dashboard); #endif /* __GIMP_DASHBOARD_H__ */ diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h index f8cec3142e..19f55cd151 100644 --- a/app/widgets/gimphelp-ids.h +++ b/app/widgets/gimphelp-ids.h @@ -665,6 +665,7 @@ #define GIMP_HELP_DASHBOARD_DIALOG "gimp-dashboard-dialog" #define GIMP_HELP_DASHBOARD_UPDATE_INTERVAL "gimp-dashboard-update-interval" #define GIMP_HELP_DASHBOARD_HISTORY_DURATION "gimp-dashboard-history-duration" +#define GIMP_HELP_DASHBOARD_RESET "gimp-dashboard-reset" #define GIMP_HELP_DASHBOARD_LOW_SWAP_SPACE_WARNING "gimp-dashboard-low-swap-space-warning" #define GIMP_HELP_DOCK "gimp-dock" diff --git a/menus/dashboard-menu.xml b/menus/dashboard-menu.xml index 9bdc05c696..30548a79c4 100644 --- a/menus/dashboard-menu.xml +++ b/menus/dashboard-menu.xml @@ -17,6 +17,7 @@ +