/* vi:set ts=8 sts=0 sw=8: * $Id: msgbox.c,v 1.29 2000/04/20 04:13:31 kahn Exp kahn $ * * Copyright (C) 1998 Andy C. Kahn * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "main.h" #ifdef WANT_MSGBOX #include #include #include #include #include #include #include "prefs.h" #include "dialog.h" #include "misc.h" #include "msgbox.h" #include "gnpintl.h" /*** local types ***/ #define NUM_DEL_MSGS (prefs.msgbox_max_msg * (prefs.msgbox_per_del / 100.0)) typedef struct { GtkWidget *toplev; /* toplevel window */ GtkWidget *text; /* a text widget stores the messages */ gushort num; /* count of messages */ } msgbox_t; /*** local variables ***/ static msgbox_t msgbox = { NULL, NULL, 0 }; /*** local function prototypes ***/ static void msgbox_append(char *msg); static void msgbox_clear(void); static void msgbox_truncate(void); #ifdef GNP_DBG_MSGBOX static void msgbox_append_dbg(GtkWidget *w, gpointer data); static char *msgdbg_str = "added a message!"; #endif /*** global function definitions ***/ /* * PUBLIC: msgbox_close * * we don't really close/destroy the box; we hide it (since we still log * messages to the text box). */ void msgbox_close(void) { if (msgbox.toplev == NULL || !IS_USE_MSGBOX()) return; gtk_widget_hide(msgbox.toplev); } /* msgbox_hide */ /* * PUBLIC: msgbox_init * * basically taken from testgtk.c. should be called before any file is * opened. */ void msgbox_init(void) { #ifdef USE_LIBGLADE GladeXML *xml; #else GtkWidget *box1, *box2, *table; #endif GtkWidget *hbox, *tmp; if (!IS_USE_MSGBOX() || msgbox.toplev != NULL) return; msgbox.num = 0; #ifdef USE_LIBGLADE xml = my_glade_xml_get("Message Box"); msgbox.toplev = my_glade_widget_get(xml, "Message Box", 0); msgbox.text = my_glade_widget_get(xml, "msgbox_text", 0); hbox = my_glade_widget_get(xml, "msgbox_hbox", 0); tmp = my_glade_widget_get(xml, "msgbox_close", 0); gtk_signal_connect(GTK_OBJECT(tmp), "clicked", GTK_SIGNAL_FUNC(msgbox_close), NULL); (void)misc_button_new_w_label(_("Clear"), GNOME_STOCK_PIXMAP_CLEAR, msgbox_clear, NULL, hbox, PACK_END | PACK_FILL | SHOW_BUTTON | CANCEL_DEFAULT, 2); gtk_object_destroy(GTK_OBJECT(xml)); #else msgbox.toplev = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(msgbox.toplev); gtk_widget_set_name(msgbox.toplev, "messages"); gtk_widget_set_usize(msgbox.toplev, 500, 250); gtk_window_set_policy(GTK_WINDOW(msgbox.toplev), TRUE, TRUE, FALSE); gtk_window_set_title(GTK_WINDOW(msgbox.toplev), _("Messages")); gtk_container_border_width(GTK_CONTAINER(msgbox.toplev), 0); box1 = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(msgbox.toplev), box1); gtk_widget_show(box1); box2 = gtk_vbox_new(FALSE, 10); gtk_container_border_width(GTK_CONTAINER(box2), 10); gtk_box_pack_start(GTK_BOX(box1), box2, TRUE, TRUE, 0); gtk_widget_show(box2); table = gtk_table_new(2, 2, FALSE); gtk_table_set_row_spacing(GTK_TABLE(table), 0, 2); gtk_table_set_col_spacing(GTK_TABLE(table), 0, 2); gtk_box_pack_start(GTK_BOX(box2), table, TRUE, TRUE, 0); gtk_widget_show(table); msgbox.text = gtk_text_new(NULL, NULL); gtk_table_attach(GTK_TABLE(table), msgbox.text, 0, 1, 0, 1, #ifdef GTK_HAVE_FEATURES_1_1_0 (GtkAttachOptions)(GTK_EXPAND | GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_SHRINK | GTK_FILL), #else GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, #endif 0, 0); gtk_widget_show(msgbox.text); tmp = gtk_vscrollbar_new(GTK_TEXT(msgbox.text)->vadj); gtk_table_attach(GTK_TABLE(table), tmp, 1, 2, 0, 1, #ifdef GTK_HAVE_FEATURES_1_1_0 (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_SHRINK | GTK_FILL), #else GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, #endif 0, 0); gtk_widget_show(tmp); gtk_widget_realize(msgbox.text); gtk_text_set_editable(GTK_TEXT(msgbox.text), FALSE); gtk_text_set_word_wrap(GTK_TEXT(msgbox.text), FALSE); tmp = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(box1), tmp, FALSE, TRUE, 0); gtk_widget_show(tmp); box2 = gtk_vbox_new(FALSE, 10); gtk_container_border_width(GTK_CONTAINER(box2), 10); gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, TRUE, 0); gtk_widget_show(box2); hbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(box2), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); #ifdef GNP_DBG_MSGBOX (void)misc_button_new_w_label(_("Add message!"), NULL, msgbox_append_dbg, msgdbg_str, hbox, PACK_END | PACK_FILL | SHOW_BUTTON | CANCEL_DEFAULT, 0); #endif (void)misc_button_new_w_label(_("Close"), GNOME_STOCK_BUTTON_CLOSE, msgbox_close, NULL, hbox, PACK_END | PACK_FILL | CANCEL_DEFAULT | GRAB_DEFAULT | SHOW_BUTTON, 2); (void)misc_button_new_w_label(_("Clear"), GNOME_STOCK_PIXMAP_CLEAR, msgbox_clear, NULL, hbox, PACK_END | PACK_FILL | SHOW_BUTTON | CANCEL_DEFAULT, 2); #endif (void)gtk_signal_connect(GTK_OBJECT(msgbox.toplev), "delete_event", GTK_SIGNAL_FUNC(gtk_true), NULL); msgbox_printf("%s%s %s", WELCOME_MSG, APP_NAME, APP_VERSION, _(".")); } /* msgbox_init */ /* * PUBLIC: msgbox_printf * * print a formatted message to a the msgbox * * we need to use g_vsprintf(), which is in glib/gstring.c, but isn't found in * glib.h. and of course, we can't have gstring.h be public because it might * be indecent exposure. basically, we use g_vsprintf() to build a buffer of * the right/exact size needed to print the message. if we don't, then we'd * have to parse the va_list and check all the printf formatters. */ void msgbox_printf(const char *fmt, ...) { #ifdef GTK_HAVE_FEATURES_1_1_0 va_list args; #else va_list ap1, ap2; #endif char *buf; if (msgbox.toplev == NULL || !IS_USE_MSGBOX()) return; #ifdef GTK_HAVE_FEATURES_1_1_0 va_start(args, fmt); buf = g_strdup_vprintf(fmt, args); va_end(args); msgbox_append(buf); g_free(buf); #else va_start(ap1, fmt); va_start(ap2, fmt); buf = g_vsprintf(fmt, &ap1, &ap2); va_end(ap1); va_end(ap2); msgbox_append(buf); #endif } /* msgbox_printf */ /* * PUBLIC: msgbox_show * * shows the message box. invoked as a callback from the main menu. */ void msgbox_show(GtkWidget *w, gpointer data) { if (!IS_USE_MSGBOX()) { (void)do_dialog_ok( N_("No messages!"), N_(" Message box has been disabled ")); return; } if (misc_show_and_raise(msgbox.toplev)) return; msgbox_init(); } /* msgbox_show */ /*** local function definitions ***/ /* * PRIVATE: msgbox_append * * add another message the msgbox. all msgs contain the current time/data * before the actual string provided by the user. */ static void msgbox_append(char *msg) { time_t curtime; char *timestr, *buf; if (msgbox.toplev == NULL || !IS_USE_MSGBOX()) return; msgbox.num++; if (msgbox.num == prefs.msgbox_max_msg) msgbox_truncate(); gtk_text_freeze(GTK_TEXT(msgbox.text)); /* get string for current time and strip off \n added by ctime() */ time(&curtime); timestr = ctime(&curtime); if (timestr[strlen(timestr) - 1] == '\n') timestr[strlen(timestr) - 1] = '\0'; /* build the time/data as a string, append \n to msg if user didn't */ #ifdef GTK_HAVE_FEATURES_1_1_0 buf = g_strdup_printf("[%s] %s%c", timestr, msg, (msg[strlen(msg) - 1] == '\n') ? '\0' : '\n'); #else buf = g_new(char, strlen(timestr) + strlen(msg) + 5); g_snprintf(buf, strlen(timestr) + strlen(msg) + 5, "[%s] %s%c", timestr, msg, (msg[strlen(msg) - 1] == '\n') ? '\0' : '\n'); #endif gtk_text_insert(GTK_TEXT(msgbox.text), NULL, NULL, NULL, buf, strlen(buf)); g_free(buf); gtk_text_thaw(GTK_TEXT(msgbox.text)); } /* void msgbox_entry_append */ /* * PRIVATE: msgbox_clear * * clears and resets everything */ static void msgbox_clear(void) { int err; if (msgbox.toplev == NULL || !IS_USE_MSGBOX()) return; gtk_text_freeze(GTK_TEXT(msgbox.text)); gtk_text_set_point(GTK_TEXT(msgbox.text), 0); err = gtk_text_forward_delete( GTK_TEXT(msgbox.text), gtk_text_get_length(GTK_TEXT(msgbox.text))); gtk_text_thaw(GTK_TEXT(msgbox.text)); msgbox.num = 0; } /* msgbox_clear */ /* * PRIVATE: msgbox_truncate * * when the max number of msgs have been reached (prefs.msgbox_max_msg), delete * a specified percentage (msgbox_per_del) of them to free up space. */ static void msgbox_truncate(void) { char *buf; guint i, num, err, len; gboolean found = FALSE; if (msgbox.toplev == NULL || !IS_USE_MSGBOX() || NUM_DEL_MSGS == 0) return; len = gtk_text_get_length(GTK_TEXT(msgbox.text)); buf = gtk_editable_get_chars(GTK_EDITABLE(msgbox.text), 1, len); for (num = 0, i = 0; i < len; i++) { if (buf[i] == '\n') { num++; if (num == NUM_DEL_MSGS) { i++; /* skip over \n */ gtk_text_set_point(GTK_TEXT(msgbox.text), i); found = TRUE; break; } } } g_free(buf); g_assert(found == TRUE); gtk_text_freeze(GTK_TEXT(msgbox.text)); err = gtk_text_backward_delete(GTK_TEXT(msgbox.text), i); gtk_text_set_point(GTK_TEXT(msgbox.text), gtk_text_get_length(GTK_TEXT(msgbox.text))); gtk_text_thaw(GTK_TEXT(msgbox.text)); msgbox.num -= num; } /* msgbox_truncate */ #ifdef GNP_DBG_MSGBOX /* * PRIVATE: msgbox_append_dbg * * for debugging purposes only */ static void msgbox_append_dbg(GtkWidget *w, gpointer data) { msgbox_printf("%s", (char *)data); } /* msgbox_append_dbg */ #endif #else void msgbox_printf(const char *fmt, ...); void msgbox_printf(const char *fmt, ...) { } #endif /* #ifdef WANT_MSGBOX */ /* the end */