/*------------------------------------------------------------------------- * Copyright (c) 2004-2005 Kenneth W. Sodemann (stuffle@mac.com) *------------------------------------------------------------------------- * archive_dlg * * Synopsis: * Archive problem reports based on user specified criteria. * * $Id: archive_dlg.c,v 1.8 2005/02/22 02:12:14 stuffle Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * *------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "defs.h" #include "error_chks.h" #include "gstr_utils.h" #include "gtkutils.h" #include "sqlstr.h" #define ALL_ITEMS_PK -1 #define INVALID_PK -2 #define ARCH_B4_DATE "dted" #define LIMITS_VBOX "lim_vbox" #define ADD_BUTTON "add_button" #define RM_BUTTON "rm_button" #define COUNT_LBL "count_lbl" #define PREV_SEL "prev_sel" #define NELS "number_of_elements" /*--------------------------------------------------------------------------- * Local data and type definitions. *--------------------------------------------------------------------------- */ /* * There is only ONE archive dialog per instance of PRepS. This is that * one dialog. */ static GtkWidget *archive_dlg = NULL; /* * Store the limiter widgets in a dynamic list. Also, store the info * that will be needed from the database in lists so we don't have to * access the database more than once. */ typedef struct LimitWidgets { GtkWidget *hbox; GtkWidget *plus_btn; GtkWidget *minus_btn; GtkWidget *attrib_combo; GtkWidget *value_combo; gboolean allow_rm; } LimitWidgets; static GList *limiters = NULL; static gint num_limiters = 0; typedef struct LimAttr { GString *name; gint pk; gboolean include; } LimAttr; static GList *projects = NULL; static GList *stats = NULL; static GList *sevrs = NULL; static GList *ptypes = NULL; /* * These are the types of attributes that are shown in the attrib_combo * for each of the limiters. Obviously, the AttrTypes enumerated type * and the attr_types array of strings need to stay in sync. */ typedef enum { PROJECTS, PROBLEM_TYPES, SEVERITIES, STATUSES, NUM_ATTR_TYPES } AttrTypes; static const gchar *attr_types[] = {N_("Project"), N_("Problem Type"), N_("Severity"), N_("Status")}; /* * For each limiter, we are going to store the previous selection for the * value combo. However, since the combo value has multiple sets * of values, one set for each attribute type, we need keys for each of * the attribute types. This list needs to stay in sync with the * AttrTypes enumeration defined above. */ static const gchar *prev_sel[] = {"prj_prev_sel", "pt_prev_sel", "sevr_prev_sel", "stat_prev_sel"}; /*--------------------------------------------------------------------------- * Local Function Prototypes *--------------------------------------------------------------------------- */ static void add_limiter (GtkWidget *window, AttrTypes atype, gboolean orig); static void clear_include_flag (gpointer lim, gpointer user_data); static GtkWidget *create_archive_dlg (GtkWidget *parent, PGconn *conn); static GString *create_arch_sql (gboolean dml, time_t max_date); static void create_attr_lists (PGconn *conn, GConfClient *client); static void destroy_attr_lists (void); static LimAttr *create_lim_attr (const gchar *name, gint pk); static void destroy_lim_attr (LimAttr *lim); static void destroy_lim_attr_list (GList *list); static void destroy_limiters (void); #ifdef USE_NEW_GTK static void fill_attr_combo (GtkComboBox *combo, AttrTypes init); static void fill_value_combo (GtkComboBox *combo, AttrTypes attr_type); #else static void fill_attr_combo (GtkOptionMenu *combo, AttrTypes init); static void fill_value_combo (GtkOptionMenu *combo, AttrTypes attr_type); #endif static void on_add_clicked (GtkWidget *window, gpointer user_data); static void on_arch_dlg_clicked (GtkDialog *dlg, gint btn_cd, gpointer user_data); static void on_arch_dlg_destroy (GtkWidget *window, gpointer user_data); static void on_arch_dlg_show (GtkWidget *window, gpointer user_data); static void on_attrib_combo_changed (GtkWidget *combo, gpointer user_data); static void on_rm_clicked (GtkWidget *window, gpointer user_data); static void on_value_combo_changed (GtkWidget *combo, gpointer user_data); static void refresh_count (GtkWidget *widget, gpointer user_data); static void update_include_flag (AttrTypes atype, gint sel, gboolean include); /*--------------------------------------------------------------------------- * Function Bodies *--------------------------------------------------------------------------- */ static void clear_include_flag (gpointer lim, gpointer user_data) { LimAttr *l = lim; l->include = FALSE; } /* * Create the SQL for the archiving. If arch is TRUE, the returned * the string will be the DML that will actually do the archive. * Otherwise, the string is just a counter string that will count * the number of items that will be archived. * * It is the callers responsibility to free the GString. */ static GString * create_arch_sql (gboolean dml, time_t max_date) { GString *sql; LimAttr *attr; GList *iter; struct tm the_time; sql = g_string_new (""); if (dml) { sql = g_string_append (sql, "update problem_report "); sql = g_string_append (sql, "set archived = true "); } else { sql = g_string_append (sql, "select count (*) "); sql = g_string_append (sql, "from problem_report "); } sql = g_string_append (sql, "where archived = false "); /* * Append the time restrictions... */ gmtime_r (&max_date, &the_time); g_string_append_printf (sql, "and date_trunc( 'day', creation_date ) <= '%04d-%02d-%02d' ", the_time.tm_year + 1900, the_time.tm_mon + 1, the_time.tm_mday); /* * Projects */ g_string_append_printf (sql, "and project_num in (%d", INVALID_PK); for (iter = g_list_first (projects)->next; iter != NULL; iter = iter->next) { attr = iter->data; if (attr->include) { g_string_append_printf (sql, ", %d", attr->pk); } } sql = g_string_append (sql, ") "); /* * Problem Types */ g_string_append_printf (sql, "and type_num in (%d", INVALID_PK); for (iter = g_list_first (ptypes)->next; iter != NULL; iter = iter->next) { attr = iter->data; if (attr->include) { g_string_append_printf (sql, ", %d", attr->pk); } } sql = g_string_append (sql, ") "); /* * Severities */ g_string_append_printf (sql, "and severity_num in (%d", INVALID_PK); for (iter = g_list_first (sevrs)->next; iter != NULL; iter = iter->next) { attr = iter->data; if (attr->include) { g_string_append_printf (sql, ", %d", attr->pk); } } sql = g_string_append (sql, ") "); /* * Statuses */ g_string_append_printf (sql, "and status_num in (%d", INVALID_PK); for (iter = g_list_first (stats)->next; iter != NULL; iter = iter->next) { attr = iter->data; if (attr->include) { g_string_append_printf (sql, ", %d", attr->pk); } } sql = g_string_append (sql, ") "); return sql; } static void create_attr_lists (PGconn *conn, GConfClient *client) { GString *sql; PGresult *res; LimAttr *lim; gint i; gint n; sql = g_string_new (""); pr_type_sql_str (sql, ALL_ITEMS, client, 0); res = PQexec (conn, sql->str); if (chk_sql_error (res, "get problem types")) { lim = create_lim_attr (_(""), ALL_ITEMS_PK); ptypes = g_list_append (ptypes, lim); n = PQntuples (res); for (i = 0; i < n; i++) { lim = create_lim_attr (PQgetvalue (res, i, SQLSTR_NAME_POS), atoi (PQgetvalue (res, i, SQLSTR_PK_POS))); ptypes = g_list_append (ptypes, lim); } } PQclear (res); stat_sql_str (sql, ALL_ITEMS, client, 0); res = PQexec (conn, sql->str); if (chk_sql_error (res, "get statuses")) { lim = create_lim_attr (_(""), ALL_ITEMS_PK); stats = g_list_append (stats, lim); n = PQntuples (res); for (i = 0; i < n; i++) { lim = create_lim_attr (PQgetvalue (res, i, SQLSTR_NAME_POS), atoi (PQgetvalue (res, i, SQLSTR_PK_POS))); stats = g_list_append (stats, lim); } } PQclear (res); sevr_sql_str (sql, ALL_ITEMS, client, 0); res = PQexec (conn, sql->str); if (chk_sql_error (res, "get severities")) { lim = create_lim_attr (_(""), ALL_ITEMS_PK); sevrs = g_list_append (sevrs, lim); n = PQntuples (res); for (i = 0; i < n; i++) { lim = create_lim_attr (PQgetvalue (res, i, SQLSTR_NAME_POS), atoi (PQgetvalue (res, i, SQLSTR_PK_POS))); sevrs = g_list_append (sevrs, lim); } } PQclear (res); prj_sql_str (sql, ALL_ITEMS, TRUE, FALSE, FALSE, TRUE, client, 0); res = PQexec (conn, sql->str); if (chk_sql_error (res, "get projects")) { lim = create_lim_attr (_(""), ALL_ITEMS_PK); projects = g_list_append (projects, lim); n = PQntuples (res); for (i = 0; i < n; i++) { lim = create_lim_attr (PQgetvalue (res, i, SQLSTR_NAME_POS), atoi (PQgetvalue (res, i, SQLSTR_PK_POS))); projects = g_list_append (projects, lim); } } PQclear (res); g_string_free (sql, TRUE); } static void destroy_attr_lists (void) { if (projects != NULL) { destroy_lim_attr_list (projects); projects = NULL; } if (stats != NULL) { destroy_lim_attr_list (stats); stats = NULL; } if (sevrs != NULL) { destroy_lim_attr_list (sevrs); sevrs = NULL; } if (ptypes != NULL) { destroy_lim_attr_list (ptypes); ptypes = NULL; } } static LimAttr * create_lim_attr (const gchar *name, gint pk) { LimAttr *lim; lim = g_malloc (sizeof (LimAttr)); lim->name = g_string_new (name); lim->pk = pk; lim->include = FALSE; return lim; } static void update_include_flag (AttrTypes atype, gint sel, gboolean include) { GList *the_list = NULL; LimAttr *item; switch (atype) { case PROJECTS: the_list = g_list_first (projects); break; case PROBLEM_TYPES: the_list = g_list_first (ptypes); break; case SEVERITIES: the_list = g_list_first (sevrs); break; case STATUSES: the_list = g_list_first (stats); break; default: g_assert_not_reached (); } item = g_list_nth_data (the_list, sel); if (item->pk == ALL_ITEMS_PK) { for (the_list = the_list; the_list != NULL; the_list = the_list->next) { item = the_list->data; item->include = include; } } else { item->include = include; } } static void destroy_lim_attr (LimAttr *lim) { g_string_free (lim->name, TRUE); g_free (lim); } static void destroy_lim_attr_list (GList *list) { GList *iter; list = g_list_first (list); for (iter = list; iter != NULL; iter = iter->next) { destroy_lim_attr (iter->data); } g_list_free (list); } static void destroy_limiters (void) { GList *iter; LimitWidgets *lim; for (iter = g_list_first (limiters); iter != NULL; iter = iter->next) { lim = iter->data; gtk_widget_destroy (lim->plus_btn); gtk_widget_destroy (lim->minus_btn); gtk_widget_destroy (lim->attrib_combo); gtk_widget_destroy (lim->value_combo); gtk_widget_destroy (lim->hbox); g_free (lim); } g_list_free (limiters); limiters = NULL; num_limiters = 0; } #ifdef USE_NEW_GTK static void fill_value_combo (GtkComboBox *combo, AttrTypes atype) { LimAttr *attr; GList *iter = NULL; gint sel; gint nels; gint i; nels = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), NELS)); g_signal_handlers_block_by_func (combo, G_CALLBACK (refresh_count), NULL); for (i = 0; i < nels; i++) { gtk_combo_box_remove_text (combo, nels - i - 1); } switch (atype) { case PROJECTS: iter = g_list_first (projects); break; case PROBLEM_TYPES: iter = g_list_first (ptypes); break; case SEVERITIES: iter = g_list_first (sevrs); break; case STATUSES: iter = g_list_first (stats); break; default: g_assert_not_reached (); } nels = 0; for (iter = iter; iter != NULL; iter = iter->next) { attr = iter->data; gtk_combo_box_append_text (combo, attr->name->str); nels++; } g_signal_handlers_unblock_by_func (combo, G_CALLBACK (refresh_count), NULL); sel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), prev_sel[atype])); g_object_set_data (G_OBJECT (combo), NELS, GINT_TO_POINTER (nels)); gtk_combo_box_set_active (combo, sel); } #else static void fill_value_combo (GtkOptionMenu *combo, AttrTypes atype) { GtkWidget *menu; GtkWidget *item; LimAttr *attr; GList *iter = NULL; gint sel; if (gtk_option_menu_get_menu (combo) != NULL) { gtk_option_menu_remove_menu (combo); } menu = gtk_menu_new (); switch (atype) { case PROJECTS: iter = g_list_first (projects); break; case PROBLEM_TYPES: iter = g_list_first (ptypes); break; case SEVERITIES: iter = g_list_first (sevrs); break; case STATUSES: iter = g_list_first (stats); break; default: g_assert_not_reached (); } for (iter = iter; iter != NULL; iter = iter->next) { attr = iter->data; item = gtk_menu_item_new_with_label (attr->name->str); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); } sel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), prev_sel[atype])); gtk_option_menu_set_menu (combo, menu); gtk_option_menu_set_history (combo, sel); } #endif /* * NOTE: This should only need to be called when a set of limiter * widgets are first created. */ #ifdef USE_NEW_GTK static void fill_attr_combo (GtkComboBox *combo, AttrTypes init) { gint i; for (i = 0; i < NUM_ATTR_TYPES; i++) { gtk_combo_box_append_text (combo, attr_types[i]); } /* * Force a redraw of the value combo via the "changed" handler by * making sure that the current selection and what we have stored * in the prev selection are different. */ g_object_set_data (G_OBJECT (combo), PREV_SEL, GINT_TO_POINTER (-1)); gtk_combo_box_set_active (combo, init); } #else static void fill_attr_combo (GtkOptionMenu *combo, AttrTypes init) { GtkWidget *menu; GtkWidget *item; gint i; menu = gtk_menu_new (); for (i = 0; i < NUM_ATTR_TYPES; i++) { item = gtk_menu_item_new_with_label (attr_types[i]); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); } /* * Force a redraw of the value combo via the "changed" handler by * making sure that the current selection and what we have stored * in the prev selection are different. */ g_object_set_data (G_OBJECT (combo), PREV_SEL, GINT_TO_POINTER (-1)); gtk_option_menu_set_menu (combo, menu); gtk_option_menu_set_history (combo, init); } #endif /* * Add a limiter value to the window, using the specified type as the * initial type. When we draw the window, we start with a default * limiter for each attribute. If this is one of the original 4, * do not allow the user to change anything except the value combo. */ static void add_limiter (GtkWidget *window, AttrTypes atype, gboolean orig) { LimitWidgets *widgets; GtkWidget *hbox; GtkWidget *plus_btn; GtkWidget *minus_btn; GtkWidget *attrib_combo; GtkWidget *value_combo; GtkWidget *vbox; GSList *grp = NULL; g_assert (window != NULL); vbox = lookup_widget (window, LIMITS_VBOX); g_assert (vbox != NULL); hbox = gtk_hbox_new (FALSE, 5); gtk_widget_show (hbox); plus_btn = gtk_radio_button_new_with_label (grp, "+"); gtk_widget_show (plus_btn); gtk_box_pack_start (GTK_BOX (hbox), plus_btn, FALSE, FALSE, 0); grp = gtk_radio_button_get_group (GTK_RADIO_BUTTON (plus_btn)); minus_btn = gtk_radio_button_new_with_label (grp, "-"); gtk_widget_show (minus_btn); gtk_box_pack_start (GTK_BOX (hbox), minus_btn, FALSE, FALSE, 0); grp = gtk_radio_button_get_group (GTK_RADIO_BUTTON (minus_btn)); #ifdef USE_NEW_GTK attrib_combo = gtk_combo_box_new_text (); #else attrib_combo = gtk_option_menu_new (); #endif gtk_box_pack_start (GTK_BOX (hbox), attrib_combo, FALSE, FALSE, 0); gtk_widget_show (attrib_combo); #ifdef USE_NEW_GTK value_combo = gtk_combo_box_new_text (); #else value_combo = gtk_option_menu_new (); #endif gtk_box_pack_start (GTK_BOX (hbox), value_combo, TRUE, TRUE, 0); gtk_widget_show (value_combo); widgets = g_malloc (sizeof (LimitWidgets)); widgets->hbox = hbox; widgets->plus_btn = plus_btn; widgets->minus_btn = minus_btn; widgets->attrib_combo = attrib_combo; widgets->value_combo = value_combo; widgets->allow_rm = !orig; if (orig) { gtk_widget_set_sensitive (plus_btn, FALSE); gtk_widget_set_sensitive (minus_btn, FALSE); gtk_widget_set_sensitive (attrib_combo, FALSE); } limiters = g_list_append (limiters, widgets); g_signal_connect (attrib_combo, "changed", G_CALLBACK (on_attrib_combo_changed), GINT_TO_POINTER (num_limiters)); g_signal_connect (value_combo, "changed", G_CALLBACK (on_value_combo_changed), GINT_TO_POINTER (num_limiters)); /* * The refresh_count() handler is not connected to the * attrib_combo because an actual change to the attrib * combo will cause a change to the value_combo by default. */ g_signal_connect_after (value_combo, "changed", G_CALLBACK (refresh_count), NULL); /* * We only need to connect one radio button in the set, since * if one is clicked, they both are clicked. If we ever have * more than two radio buttons, we may have to re-think that... */ g_signal_connect_after (plus_btn, "clicked", G_CALLBACK (refresh_count), NULL); num_limiters++; gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); /* * Filling the attribute column will result in a "change" signal * being emitted for the attribute combo. This will in turn * result in the value combo being filled. Thus, we don't * need to fill that one explicetly. */ #ifdef USE_NEW_GTK fill_attr_combo (GTK_COMBO_BOX (attrib_combo), atype); #else fill_attr_combo (GTK_OPTION_MENU (attrib_combo), atype); #endif } static void on_attrib_combo_changed (GtkWidget *combo, gpointer user_data) { LimitWidgets *widgets; gint limiter_num = GPOINTER_TO_INT (user_data); gint sel; gint prev_sel; /* * The attribute value in the attribute combo has changed. * Get the full current widget set, and if the user has selected * a different attribute than was previously selected, changed the * set of data displayed by the value combo. */ widgets = g_list_nth_data (limiters, limiter_num); g_assert (widgets != NULL); #ifdef USE_NEW_GTK sel = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); #else sel = gtk_option_menu_get_history (GTK_OPTION_MENU (combo)); #endif prev_sel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), PREV_SEL)); if (sel != prev_sel) { g_signal_handlers_block_by_func (widgets->value_combo, G_CALLBACK (on_value_combo_changed), GINT_TO_POINTER (limiter_num)); #ifdef USE_NEW_GTK fill_value_combo (GTK_COMBO_BOX (widgets->value_combo), sel); #else fill_value_combo (GTK_OPTION_MENU (widgets->value_combo), sel); #endif g_signal_handlers_unblock_by_func (widgets->value_combo, G_CALLBACK (on_value_combo_changed), GINT_TO_POINTER (limiter_num)); } g_object_set_data (G_OBJECT (combo), PREV_SEL, GINT_TO_POINTER (sel)); } static void refresh_count (GtkWidget *widget, gpointer user_data) { GnomeDateEdit *dted; GList *iter; LimitWidgets *limw; gint attr_sel; gint value_sel; GString *str; gboolean add_item; gboolean have_projects = FALSE; gboolean have_ptypes = FALSE; gboolean have_sevrs = FALSE; gboolean have_stats = FALSE; gint num_arch; PGconn *conn; PGresult *res; GtkWidget *top_dog; GtkLabel *cntlbl; GString *cntstr; top_dog = lookup_widget (widget, TOP_DOG); g_assert (top_dog != NULL); dted = GNOME_DATE_EDIT (lookup_widget (top_dog, ARCH_B4_DATE)); g_assert (dted != NULL); conn = g_object_get_data (G_OBJECT (top_dog), MY_DB_CONN); g_assert (conn != NULL); cntlbl = GTK_LABEL (lookup_widget (top_dog, COUNT_LBL)); g_assert (cntlbl != NULL); /* * Loop through the limiter widgets and determine which * widgets we should include and which we should exclude. * In cases where two limiters contradict one another, go * with the later one. */ g_list_foreach (projects, clear_include_flag, NULL); g_list_foreach (ptypes, clear_include_flag, NULL); g_list_foreach (sevrs, clear_include_flag, NULL); g_list_foreach (stats, clear_include_flag, NULL); for (iter = g_list_first (limiters); iter != NULL; iter = iter->next) { limw = iter->data; #ifdef USE_NEW_GTK attr_sel = gtk_combo_box_get_active (GTK_COMBO_BOX (limw->attrib_combo)); value_sel = gtk_combo_box_get_active (GTK_COMBO_BOX (limw->value_combo)); #else attr_sel = gtk_option_menu_get_history (GTK_OPTION_MENU (limw->attrib_combo)); value_sel = gtk_option_menu_get_history (GTK_OPTION_MENU (limw->value_combo)); #endif add_item = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (limw->plus_btn)); update_include_flag (attr_sel, value_sel, add_item); } /* * Make sure we are including at least one of each of the attributes. * If not, there is no point in actually doing the SQL. */ for (iter = g_list_first (projects)->next; iter != NULL; iter = iter->next) { have_projects = ((LimAttr *)iter->data)->include; if (have_projects) { break; } } for (iter = g_list_first (ptypes)->next; iter != NULL; iter = iter->next) { have_ptypes = ((LimAttr *)iter->data)->include; if (have_ptypes) { break; } } for (iter = g_list_first (sevrs)->next; iter != NULL; iter = iter->next) { have_sevrs = ((LimAttr *)iter->data)->include; if (have_sevrs) { break; } } for (iter = g_list_first (stats)->next; iter != NULL; iter = iter->next) { have_stats = ((LimAttr *)iter->data)->include; if (have_stats) { break; } } /* * Count the number of items that will be archived given the * current change. */ if (have_projects && have_ptypes && have_sevrs &&have_stats) { str = create_arch_sql (FALSE, gnome_date_edit_get_time (dted)); res = PQexec (conn, str->str); num_arch = atoi (PQgetvalue (res, 0, 0)); PQclear (res); g_string_free (str, TRUE); } else { num_arch = 0; } cntstr = g_string_new (""); g_string_printf (cntstr, "%d", num_arch); gtk_dialog_set_response_sensitive (GTK_DIALOG (top_dog), GTK_RESPONSE_OK, num_arch != 0); gtk_label_set_label (cntlbl, cntstr->str); g_string_free (cntstr, TRUE); } static void on_value_combo_changed (GtkWidget *combo, gpointer user_data) { LimitWidgets *widgets; gint limiter_num = GPOINTER_TO_INT (user_data); gint sel; gint attr_sel; /* * The user picked a different value. Store the value in object * data so we can restore the value if the user ever picks a * different attribute and then changes back to this attribute. */ widgets = g_list_nth_data (limiters, limiter_num); g_assert (widgets != NULL); #ifdef USE_NEW_GTK sel = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); attr_sel = gtk_combo_box_get_active (GTK_COMBO_BOX (widgets->attrib_combo)); #else sel = gtk_option_menu_get_history (GTK_OPTION_MENU (combo)); attr_sel = gtk_option_menu_get_history (GTK_OPTION_MENU (widgets->attrib_combo)); #endif g_object_set_data (G_OBJECT (combo), prev_sel[attr_sel], GINT_TO_POINTER (sel)); } static void on_add_clicked (GtkWidget *btn, gpointer user_data) { GtkWidget *rm_btn; g_assert (user_data != NULL); rm_btn = lookup_widget (GTK_WIDGET (user_data), RM_BUTTON); g_assert (rm_btn != NULL); gtk_widget_set_sensitive (rm_btn, TRUE); add_limiter (user_data, PROJECTS, FALSE); } static void on_rm_clicked (GtkWidget *btn, gpointer user_data) { LimitWidgets *lim; GList *last; g_assert (user_data != NULL); if (limiters != NULL) { last = g_list_last (limiters); lim = last->data; if (lim->allow_rm) { gtk_widget_destroy (lim->plus_btn); gtk_widget_destroy (lim->minus_btn); gtk_widget_destroy (lim->attrib_combo); gtk_widget_destroy (lim->value_combo); gtk_widget_destroy (lim->hbox); g_free (lim); limiters = g_list_delete_link (limiters, last); if (--num_limiters == NUM_ATTR_TYPES) { gtk_widget_set_sensitive (btn, FALSE); } } } } static void on_arch_dlg_show (GtkWidget *window, gpointer user_data) { } static void on_arch_dlg_destroy (GtkWidget *window, gpointer user_data) { destroy_attr_lists (); if (limiters != NULL) { destroy_limiters (); limiters = NULL; } } static void on_arch_dlg_clicked (GtkDialog *dlg, gint btn_cd, gpointer user_data) { GnomeDateEdit *dted; GString *str; PGresult *res; PGconn *conn; g_assert (dlg != NULL); dted = GNOME_DATE_EDIT (lookup_widget (GTK_WIDGET (dlg), ARCH_B4_DATE)); g_assert (dted != NULL); conn = g_object_get_data (G_OBJECT (dlg), MY_DB_CONN); g_assert (conn != NULL); switch (btn_cd) { case GTK_RESPONSE_OK: str = create_arch_sql (TRUE, gnome_date_edit_get_time (dted)); res = PQexec (conn, str->str); chk_sql_error (res, "Archive Problem Reports"); PQclear (res); g_string_free (str, TRUE); gtk_widget_destroy (GTK_WIDGET (dlg)); break; case GTK_RESPONSE_CANCEL: gtk_widget_destroy (GTK_WIDGET (dlg)); break; case GTK_RESPONSE_DELETE_EVENT: break; default: g_assert_not_reached (); } } static GtkWidget * create_archive_dlg (GtkWidget *parent, PGconn *conn) { GtkWidget *arch_dlg; GtkWidget *dialog_vbox; GtkWidget *vbox1; GtkWidget *vbox2; GtkWidget *hbox; GtkWidget *label; GtkWidget *dted; GtkWidget *lim_vbox; GtkWidget *btn; GtkTooltips *tooltips; GConfClient *client; g_assert (parent != NULL); g_assert (conn != NULL); client = g_object_get_data (G_OBJECT (parent), MY_GCONF_CLIENT); g_assert (client != NULL); tooltips = g_object_get_data (G_OBJECT (parent), TOOLTIPS); g_assert (tooltips != NULL); create_attr_lists (conn, client); /* * For some reason, I don't want the dialog to act like it has a * parent even though logically it does have one. When I figure * out why, I will have to find out why I say this, and then I * will need to document why here and in the find dlg. */ arch_dlg = gtk_dialog_new_with_buttons (_("Archive Problem Reports"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); GLADE_HOOKUP_OBJECT_NO_REF (arch_dlg, arch_dlg, "arch_dlg"); gtk_container_set_border_width (GTK_CONTAINER (arch_dlg), 5); gtk_window_set_resizable (GTK_WINDOW (arch_dlg), FALSE); g_signal_connect (arch_dlg, "show", G_CALLBACK (on_arch_dlg_show), NULL); g_signal_connect (arch_dlg, "destroy", G_CALLBACK (on_arch_dlg_destroy), NULL); g_signal_connect (arch_dlg, "response", G_CALLBACK (on_arch_dlg_clicked), NULL); g_object_set_data (G_OBJECT (arch_dlg), TOP_DOG, arch_dlg); g_object_set_data (G_OBJECT (arch_dlg), MY_PARENT, parent); g_object_set_data (G_OBJECT (arch_dlg), MY_DB_CONN, conn); dialog_vbox = GTK_DIALOG (arch_dlg)->vbox; gtk_widget_show (dialog_vbox); vbox1 = gtk_vbox_new (FALSE, DTL_BOX_SPACING); gtk_box_pack_start (GTK_BOX (dialog_vbox), vbox1, TRUE, TRUE, 0); gtk_widget_show (vbox1); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox1), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new (_("Include Problem Reports Created On Or Before:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); gtk_widget_show (label); dted = gnome_date_edit_new ((time_t) 0, FALSE, FALSE); GLADE_HOOKUP_OBJECT (arch_dlg, dted, ARCH_B4_DATE); gtk_box_pack_start (GTK_BOX (hbox), dted, TRUE, TRUE, 0); gtk_widget_show (dted); g_signal_connect (dted, "date-changed", G_CALLBACK (refresh_count), NULL); /* * vbox2 is used to group the label for the limiters, the hbox & * limiter vbox, and the add & remove buttons more tightly together... */ vbox2 = gtk_vbox_new (FALSE, DTL_BOX_SPACING / 2); gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0); gtk_widget_show (vbox2); label = gtk_label_new (_("Include or Exclude PRs Matching:")); gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show (label); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new (" "); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); lim_vbox = gtk_vbox_new (FALSE, 3); GLADE_HOOKUP_OBJECT (arch_dlg, lim_vbox, LIMITS_VBOX); gtk_box_pack_start (GTK_BOX (hbox), lim_vbox, TRUE, TRUE, 0); gtk_widget_show (lim_vbox); hbox = gtk_hbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); btn = gtk_button_new_with_mnemonic (_("Add")); gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0); GLADE_HOOKUP_OBJECT (arch_dlg, dted, ADD_BUTTON); gtk_widget_show (btn); g_signal_connect (btn, "clicked", G_CALLBACK (on_add_clicked), arch_dlg); btn = gtk_button_new_with_mnemonic (_("Remove")); gtk_box_pack_start (GTK_BOX (hbox), btn, FALSE, FALSE, 0); gtk_widget_show (btn); g_signal_connect (btn, "clicked", G_CALLBACK (on_rm_clicked), arch_dlg); GLADE_HOOKUP_OBJECT (arch_dlg, btn, RM_BUTTON); gtk_widget_set_sensitive (btn, FALSE); /* * End if the limiters section. Back to using vbox1. */ hbox = gtk_hbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (vbox1), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new (_("Problem Reports to be Archived:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); label = gtk_label_new (""); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); GLADE_HOOKUP_OBJECT (arch_dlg, label, COUNT_LBL); gtk_widget_show (label); add_limiter (arch_dlg, PROJECTS, TRUE); add_limiter (arch_dlg, PROBLEM_TYPES, TRUE); add_limiter (arch_dlg, SEVERITIES, TRUE); add_limiter (arch_dlg, STATUSES, TRUE); return arch_dlg; } void destroy_archive_dlg (void) { if (archive_dlg != NULL) { gtk_widget_destroy (archive_dlg); archive_dlg = NULL; } } void show_archive_dlg (GtkWidget *parent, PGconn *conn) { g_assert (parent != NULL); g_assert (conn != NULL); /* * If the dialog box exists, just make sure it is visable, otherwise * create it. */ if (archive_dlg) { gdk_window_show (archive_dlg->window); gdk_window_raise (archive_dlg->window); } else { archive_dlg = create_archive_dlg (parent, conn); /* * When the dialog is destroyed, call gtk_widget_destroyed(), * which will set the "user_data" (&find_dlg in this case) to * NULL. */ g_signal_connect (archive_dlg, "destroy", G_CALLBACK (gtk_widget_destroyed), &archive_dlg); gtk_widget_show (archive_dlg); } }