Crossfire Client, Branch  R11627
info.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk2_info_c =
00002     "$Id: info.c 11116 2008-12-30 06:57:53Z mwedel $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2005-2008 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire@metalforge.org
00023 */
00024 
00030 #ifdef HAVE_CONFIG_H
00031 #  include <config.h>
00032 #endif
00033 
00034 #include <gtk/gtk.h>
00035 #include <glade/glade.h>
00036 
00037 #include "client.h"
00038 
00039 #include "image.h"
00040 #include "gtk2proto.h"
00041 
00042 #include "main.h"
00043 
00051 #define FONT_NORMAL     0
00052 #define FONT_ARCANE     1
00053 #define FONT_STRANGE    2
00054 #define FONT_FIXED      3
00055 #define FONT_HAND       4
00056 #define NUM_FONTS       5
00057 
00061 static char *font_style_names[NUM_FONTS] = {
00062     "info_font_normal",
00063     "info_font_arcane",
00064     "info_font_strange",
00065     "info_font_fixed",
00066     "info_font_hand"
00067 };
00079 #define NUM_TEXT_VIEWS  2
00080 
00081 extern  const char * const usercolorname[NUM_COLORS];
00082 
00083 struct Info_Pane
00084 {
00085     GtkWidget       *textview;
00086     GtkWidget       *scrolled_window;
00087     GtkTextBuffer   *textbuffer;
00088     GtkTextMark     *textmark;
00089     GtkAdjustment   *adjustment;
00090     GtkTextTag      *color_tags[NUM_COLORS];
00091     GtkTextTag      *font_tags[NUM_FONTS];
00092     GtkTextTag      *bold_tag, *italic_tag, *underline_tag, *default_tag;
00093     GtkTextTag      **msg_type_tags[MSG_TYPE_LAST];
00094 } info_pane[NUM_TEXT_VIEWS];
00095 
00096 static void message_callback(int flag, int type, int subtype, char *message);
00097 
00098 extern  const char * const colorname[NUM_COLORS];
00099 
00100 /*
00101  * The idea behind the msg_type_names is to provide meaningful names that the
00102  * client can use to load/save these values, in particular, the gtk2 client
00103  * uses these to find styles on how to draw the different msg types.  We could
00104  * set this up as a two dimension array instead - that probably isn't as
00105  * efficient as the number of subtypes varies wildly.  The 0 subtypes are used
00106  * for general cases (describe the entire class of those message types).  Note
00107  * also that the names here are verbose - the actual code that uses these will
00108  * expand it further.  In practice, there should never be entries with both the
00109  * same type/subtype (each subtype should be unique) - if so, the results are
00110  * probably unpredictable on which one the code would use.
00111  */
00112 #include "msgtypes.h"
00113 
00114 static int max_subtype=0, has_style=0;
00115 
00133 void set_text_tag_from_style(GtkTextTag *tag, GtkStyle *style, GtkStyle *base_style)
00134 {
00135     g_object_set(tag, "foreground-set", FALSE, NULL);
00136     g_object_set(tag, "background-set", FALSE, NULL);
00137     g_object_set(tag, "font-desc", NULL, NULL);
00138 
00139     if (memcmp(
00140         &style->fg[GTK_STATE_NORMAL],
00141         &base_style->fg[GTK_STATE_NORMAL],
00142         sizeof(GdkColor)))
00143 
00144         g_object_set(tag, "foreground-gdk", &style->fg[GTK_STATE_NORMAL], NULL);
00145 
00146     if (memcmp(
00147         &style->bg[GTK_STATE_NORMAL],
00148         &base_style->bg[GTK_STATE_NORMAL],
00149         sizeof(GdkColor)))
00150 
00151         g_object_set(tag, "background-gdk", &style->bg[GTK_STATE_NORMAL], NULL);
00152 
00153     if (style->font_desc != base_style->font_desc)
00154         g_object_set(tag, "font-desc", style->font_desc, NULL);
00155 }
00156 
00165 void info_get_styles(void)
00166 {
00167     int i, j;
00168     static int has_init=0;
00169     GtkStyle    *tmp_style, *base_style[2];
00170     char    style_name[MAX_BUF];
00171 
00172     if (!has_init) {
00173         /*
00174          * We want to set up a 2 dimensional array of msg_type_tags to
00175          * correspond to all the types/subtypes, so looking up any value is
00176          * really easy.  We know the size of the types, but don't know the
00177          * number of subtypes - no single declared value.  So we just parse the
00178          * msg_type_names to find that, then know how big to make the other
00179          * dimension.  We could allocate different number of entries for each
00180          * type, but that makes processing a bit harder (no single value on the
00181          * number of subtypes), and this extra memory usage shouldn't really be
00182          * at all significant.
00183          */
00184         for (i = 0; i < sizeof(msg_type_names) / sizeof(Msg_Type_Names); i++) {
00185             if (msg_type_names[i].subtype > max_subtype)
00186                 max_subtype = msg_type_names[i].subtype;
00187         }
00188         for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00189             for (i = 0; i < MSG_TYPE_LAST; i++)
00190                 info_pane[j].msg_type_tags[i] =
00191                      calloc(max_subtype + 1, sizeof(GtkTextTag*));
00192 
00193             for (i = 0; i < NUM_FONTS; i++)
00194                 info_pane[j].font_tags[i] = NULL;
00195 
00196             for (i = 0; i < NUM_COLORS; i++)
00197                 info_pane[j].color_tags[i] = NULL;
00198             /*
00199              * These tag definitions never change - we don't get them from the
00200              * settings file (maybe we should), so we only need to allocate
00201              * them once.
00202              */
00203             info_pane[j].bold_tag =
00204                 gtk_text_buffer_create_tag(info_pane[j].textbuffer,
00205                     "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
00206 
00207             info_pane[j].italic_tag =
00208                  gtk_text_buffer_create_tag(info_pane[j].textbuffer,
00209                      "italic", "style", PANGO_STYLE_ITALIC, NULL);
00210 
00211             info_pane[j].underline_tag =
00212                 gtk_text_buffer_create_tag(info_pane[j].textbuffer,
00213                     "underline", "underline", PANGO_UNDERLINE_SINGLE, NULL);
00214             /*
00215              * This is really a convenience - we can pass multiple tags in when
00216              * drawing text, but once we pass in a NULL tag, that signifies no
00217              * more tags.  Rather than having to set up an array we pass in,
00218              * instead, we have this empty tag that we can pass is so that we
00219              * always have the same calling semantics, just differ what tags we
00220              * pass in.
00221              */
00222             if (!info_pane[j].default_tag)
00223                 info_pane[j].default_tag =
00224                     gtk_text_buffer_create_tag(info_pane[j].textbuffer,
00225                         "default", NULL);
00226         }
00227         has_init = 1;
00228     }
00229     for (i = 0; i < NUM_TEXT_VIEWS; i++) {
00230         base_style[i] =
00231             gtk_rc_get_style_by_paths(
00232                 gtk_settings_get_default(),
00233                 NULL, "info_default", G_TYPE_NONE);
00234     }
00235     if (!base_style[0]) {
00236         LOG(LOG_INFO, "info.c::info_get_styles",
00237             "Unable to find base style info_default"
00238             " - will not process most info tag styles!");
00239     }
00240 
00241     has_style = 0;
00242 
00243     /*
00244      * If we don't have a base style tag, we can't process these other tags, as
00245      * we need to be able to do a difference, and doing a difference from
00246      * nothing (meaning, taking everything in style) still doesn't work really
00247      * well.
00248      */
00249     if (base_style[0]) {
00250         /*
00251          * This processes the type/subtype styles.  We look up the names in the
00252          * array to find what name goes to what number.
00253          */
00254         for (i = 0; i < sizeof(msg_type_names) / sizeof(Msg_Type_Names); i++) {
00255             int type, subtype;
00256 
00257             snprintf(style_name, sizeof(style_name), "msg_%s", msg_type_names[i].style_name);
00258             type =  msg_type_names[i].type;
00259             subtype = msg_type_names[i].subtype;
00260 
00261             tmp_style =
00262                 gtk_rc_get_style_by_paths(
00263                     gtk_settings_get_default(), NULL, style_name, G_TYPE_NONE);
00264 
00265             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00266                 /*
00267                  * If we have a style for this, update the tag that goes along
00268                  * with this.  If we don't have a tag for this style, create
00269                  * it.
00270                  */
00271                 if (tmp_style) {
00272                     if (!info_pane[j].msg_type_tags[type][subtype]) {
00273                         info_pane[j].msg_type_tags[type][subtype] =
00274                             gtk_text_buffer_create_tag(
00275                                 info_pane[j].textbuffer, NULL, NULL);
00276                     }
00277                     set_text_tag_from_style(
00278                         info_pane[j].msg_type_tags[type][subtype],
00279                         tmp_style, base_style[j]);
00280                     has_style = 1;
00281                 } else {
00282                     /*
00283                      * No setting for this type/subtype, so remove tag if there
00284                      * is one.
00285                      */
00286                     if (info_pane[j].msg_type_tags[type][subtype]) {
00287                         gtk_text_tag_table_remove(
00288                             gtk_text_buffer_get_tag_table(
00289                                 info_pane[j].textbuffer),
00290                             info_pane[j].msg_type_tags[type][subtype]);
00291                         info_pane[j].msg_type_tags[type][subtype] = NULL;
00292                     }
00293                 }
00294             }
00295         }
00296 
00297         /*
00298          * Old message/color support.
00299          */
00300         for (i = 0; i < NUM_COLORS; i++) {
00301             snprintf(style_name, MAX_BUF, "info_%s", usercolorname[i]);
00302 
00303             tmp_style =
00304                 gtk_rc_get_style_by_paths(
00305                     gtk_settings_get_default(), NULL, style_name, G_TYPE_NONE);
00306 
00307             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00308                 if (tmp_style) {
00309                     if (!info_pane[j].color_tags[i]) {
00310                         info_pane[j].color_tags[i] =
00311                             gtk_text_buffer_create_tag(
00312                                 info_pane[j].textbuffer, NULL, NULL);
00313                     }
00314                     set_text_tag_from_style(
00315                         info_pane[j].color_tags[i],
00316                         tmp_style, base_style[j]);
00317                 } else {
00318                     if (info_pane[j].color_tags[i]) {
00319                         gtk_text_tag_table_remove(
00320                             gtk_text_buffer_get_tag_table(
00321                                 info_pane[j].textbuffer),
00322                             info_pane[j].color_tags[i]);
00323                         info_pane[j].color_tags[i] = NULL;
00324                     }
00325                 }
00326             }
00327         }
00328 
00329         /* Font type support */
00330         for (i = 0; i < NUM_FONTS; i++) {
00331             tmp_style =
00332                 gtk_rc_get_style_by_paths(
00333                     gtk_settings_get_default(),
00334                     NULL, font_style_names[i], G_TYPE_NONE);
00335 
00336             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00337                 if (tmp_style) {
00338                     if (!info_pane[j].font_tags[i]) {
00339                         info_pane[j].font_tags[i] =
00340                             gtk_text_buffer_create_tag(
00341                                 info_pane[j].textbuffer, NULL, NULL);
00342                     }
00343                     set_text_tag_from_style(
00344                         info_pane[j].font_tags[i], tmp_style, base_style[j]);
00345                 } else {
00346                     if (info_pane[j].font_tags[i]) {
00347                         gtk_text_tag_table_remove(
00348                             gtk_text_buffer_get_tag_table(
00349                                 info_pane[j].textbuffer),
00350                             info_pane[j].font_tags[i]);
00351                         info_pane[j].font_tags[i] = NULL;
00352                     }
00353                 }
00354             }
00355         }
00356     } else {
00357         /*
00358          * There is no base style - this should not normally be the case
00359          * with any real setting files, but certainly can be the case if the
00360          * user selected the 'None' setting.  So in this case, we just free all
00361          * the text tags.
00362          */
00363         has_style = 0;
00364         for (i = 0; i < sizeof(msg_type_names) / sizeof(Msg_Type_Names); i++) {
00365             int type, subtype;
00366 
00367             type = msg_type_names[i].type;
00368             subtype = msg_type_names[i].subtype;
00369 
00370             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00371                 if (info_pane[j].msg_type_tags[type][subtype]) {
00372                     gtk_text_tag_table_remove(
00373                         gtk_text_buffer_get_tag_table(
00374                             info_pane[j].textbuffer),
00375                         info_pane[j].msg_type_tags[type][subtype]);
00376                     info_pane[j].msg_type_tags[type][subtype] = NULL;
00377                 }
00378             }
00379         }
00380         for (i = 0; i < NUM_COLORS; i++) {
00381             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00382                 if (info_pane[j].color_tags[i]) {
00383                     gtk_text_tag_table_remove(
00384                         gtk_text_buffer_get_tag_table(
00385                             info_pane[j].textbuffer),
00386                         info_pane[j].color_tags[i]);
00387                     info_pane[j].color_tags[i] = NULL;
00388                 }
00389             }
00390         }
00391         /* Font type support */
00392         for (i = 0; i < NUM_FONTS; i++) {
00393             for (j = 0; j < NUM_TEXT_VIEWS; j++) {
00394                 if (info_pane[j].font_tags[i]) {
00395                     gtk_text_tag_table_remove(
00396                         gtk_text_buffer_get_tag_table(
00397                             info_pane[j].textbuffer),
00398                         info_pane[j].font_tags[i]);
00399                     info_pane[j].font_tags[i] = NULL;
00400                 }
00401             }
00402         }
00403     }
00404 }
00405 
00413 void info_init(GtkWidget *window_root)
00414 {
00415     int i;
00416     GtkTextIter end;
00417     char    widget_name[MAX_BUF];
00418     GladeXML *xml_tree;
00419 
00420     xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root));
00421     for (i = 0; i < NUM_TEXT_VIEWS; i++) {
00422         snprintf(widget_name, MAX_BUF, "textview_info%d", i+1);
00423         info_pane[i].textview = glade_xml_get_widget(xml_tree, widget_name);
00424 
00425         snprintf(widget_name, MAX_BUF, "scrolledwindow_textview%d", i+1);
00426 
00427         info_pane[i].scrolled_window =
00428             glade_xml_get_widget(xml_tree, widget_name);
00429 
00430         gtk_text_view_set_wrap_mode(
00431             GTK_TEXT_VIEW(info_pane[i].textview), GTK_WRAP_WORD);
00432 
00433         info_pane[i].textbuffer =
00434             gtk_text_view_get_buffer(GTK_TEXT_VIEW(info_pane[i].textview));
00435 
00436         info_pane[i].adjustment =
00437             gtk_scrolled_window_get_vadjustment(
00438                 GTK_SCROLLED_WINDOW(info_pane[i].scrolled_window));
00439 
00440         gtk_text_buffer_get_end_iter(info_pane[i].textbuffer, &end);
00441 
00442         info_pane[i].textmark =
00443             gtk_text_buffer_create_mark(
00444                 info_pane[i].textbuffer, NULL, &end, FALSE);
00445 
00446         gtk_widget_realize(info_pane[i].textview);
00447     }
00448 
00449     info_get_styles();
00450 
00451     /* Register callbacks for all message types */
00452     for (i = 0; i < MSG_TYPE_LAST; i++)
00453         setTextManager(i, message_callback);
00454 }
00455 
00481 static void add_to_textbuf(int pane, char *message,
00482                            int type, int subtype,
00483                            int bold, int italic,
00484                            int font, char *color, int underline)
00485 {
00486     GtkTextIter end;
00487     GdkRectangle rect;
00488     int scroll_to_end=0, color_num;
00489     GtkTextTag      *color_tag=NULL, *type_tag=NULL;
00490 
00491     /*
00492      * Lets see if the defined color matches any of our defined colors.  If we
00493      * get a match, set color_tag.  If color_tag is null, we either don't have
00494      * a match, we don't have a defined tag for the color, or we don't have a
00495      * color, use the default tag.  It would be nice to know if color is a sub
00496      * value set with [color] tag, or is part of the message itself - if we're
00497      * just being passed NDI_RED in the draw_ext_info from the server, we
00498      * really don't care about that - the type/subtype styles should really be
00499      * what determines what color to use.
00500      */
00501     if (color) {
00502         for (color_num = 0; color_num < NUM_COLORS; color_num++)
00503             if (!strcasecmp(usercolorname[color_num], color))
00504                 break;
00505         if (color_num < NUM_COLORS)
00506             color_tag = info_pane[pane].color_tags[color_num];
00507     }
00508     if (!color_tag)
00509         color_tag = info_pane[pane].default_tag;
00510 
00511     /*
00512      * Following block of code deals with the type/subtype.  First, we check
00513      * and make sure the passed in values are legal.  If so, first see if there
00514      * is a particular style for the type/subtype combo, if not, fall back to
00515      * one just for the type.
00516      */
00517     type_tag = info_pane[pane].default_tag;
00518 
00519     if (type >= MSG_TYPE_LAST
00520     || subtype >= max_subtype
00521     || type < 0 || subtype < 0 ) {
00522         LOG(LOG_ERROR, "info.c::add_to_textbuf",
00523             "type (%d) >= MSG_TYPE_LAST (%d) or "
00524             "subtype (%d) >= max_subtype (%d)\n",
00525             type, MSG_TYPE_LAST, subtype, max_subtype);
00526     } else {
00527         if (info_pane[pane].msg_type_tags[type][subtype])
00528             type_tag = info_pane[pane].msg_type_tags[type][subtype];
00529         else if (info_pane[pane].msg_type_tags[type][0])
00530             type_tag = info_pane[pane].msg_type_tags[type][0];
00531     }
00532 
00533     gtk_text_view_get_visible_rect(
00534         GTK_TEXT_VIEW(info_pane[pane].textview), &rect);
00535 
00536     if ((info_pane[pane].adjustment->value + rect.height)
00537         >= info_pane[pane].adjustment->upper)
00538             scroll_to_end = 1;
00539 
00540     gtk_text_buffer_get_end_iter(info_pane[pane].textbuffer, &end);
00541 
00542     gtk_text_buffer_insert_with_tags(
00543         info_pane[pane].textbuffer, &end, message, strlen(message),
00544         bold ? info_pane[pane].bold_tag : info_pane[pane].default_tag,
00545         italic ? info_pane[pane].italic_tag : info_pane[pane].default_tag,
00546         underline ? info_pane[pane].underline_tag : info_pane[pane].default_tag,
00547         info_pane[pane].font_tags[font] ?
00548             info_pane[pane].font_tags[font] : info_pane[pane].default_tag,
00549         color_tag, type_tag, NULL);
00550 
00551     if (scroll_to_end)
00552         gtk_text_view_scroll_mark_onscreen(
00553             GTK_TEXT_VIEW(info_pane[pane].textview), info_pane[pane].textmark);
00554 }
00555 
00573 static void message_callback(int orig_color, int type, int subtype, char *message) {
00574     char *marker, *current, *original;
00575     int bold=0, italic=0, font=0, underline=0;
00576     int pane=0;       
00578     char *color=NULL; 
00583     current = strdup(message);
00584     original = current;         /* Just so we know what to free */
00585 
00586     if (!has_style) {
00587         if (orig_color <0 || orig_color>NUM_COLORS) {
00588             LOG(LOG_ERROR, "info.c::message_callback",
00589                 "Passed invalid color from server: %d, max allowed is %d\n",
00590                 orig_color, NUM_COLORS);
00591             orig_color = 0;
00592         } else {
00593             /*
00594              * Not efficient - we have a number, but convert it to a string, at
00595              * which point add_to_textbuf() converts it back to a number :(
00596              */
00597             color = (char*)usercolorname[orig_color];
00598         }
00599     }
00600 
00601     while ((marker = strchr(current, '[')) != NULL) {
00602         *marker = 0;
00603 
00604         if (strlen(current) > 0)
00605             add_to_textbuf(pane,
00606                 current, type, subtype, bold, italic, font, color, underline);
00607 
00608         current = marker + 1;
00609 
00610         if ((marker = strchr(current, ']')) == NULL) {
00611             free(original);
00612             return;
00613         }
00614 
00615         *marker = 0;
00616         if (!strcmp(current, "b"))               bold = TRUE;
00617         else if (!strcmp(current,  "/b"))        bold = FALSE;
00618         else if (!strcmp(current,  "i"))         italic = TRUE;
00619         else if (!strcmp(current,  "/i"))        italic = FALSE;
00620         else if (!strcmp(current,  "ul"))        underline = TRUE;
00621         else if (!strcmp(current,  "/ul"))       underline = FALSE;
00622         else if (!strcmp(current,  "fixed"))     font = FONT_FIXED;
00623         else if (!strcmp(current,  "arcane"))    font = FONT_ARCANE;
00624         else if (!strcmp(current,  "hand"))      font = FONT_HAND;
00625         else if (!strcmp(current,  "strange"))   font = FONT_STRANGE;
00626         else if (!strcmp(current,  "print"))     font = FONT_NORMAL;
00627         else if (!strcmp(current,  "/color"))    color = NULL;
00628         else if (!strncmp(current, "color=", 6)) color = current + 6;
00629         else
00630             LOG(LOG_INFO, "info.c::message_callback",
00631                 "unrecognized tag: [%s]\n", current);
00632 
00633         current = marker + 1;
00634     }
00635 
00636     add_to_textbuf(
00637         pane, current, type, subtype, bold, italic, font, color, underline);
00638 
00639     add_to_textbuf(
00640         pane, "\n", type, subtype, bold, italic, font, color, underline);
00641 
00642     free(original);
00643 }
00644 
00662 void draw_info(const char *str, int color) {
00663     int ncolor = color;
00664     GtkTextIter end;
00665     GdkRectangle rect;
00666     int scroll_to_end=0;
00667 
00668     if (ncolor == NDI_WHITE) {
00669         ncolor = NDI_BLACK;
00670     }
00671 
00672     /*
00673      * This seems more complicated than it should be, but we need to see if the
00674      * window is scrolled at the end.  If it is, we want to keep scrolling it
00675      * down with new info.  If not, we don't want to change position -
00676      * otherwise, it makes it very difficult to look back at the old info (like
00677      * old messages missed during combat, looking at the shop listing while
00678      * people are chatting, etc) We need to find out the position before
00679      * putting in new text - otherwise, that operation will mess up our
00680      * position, and not give us right info.
00681      */
00682     gtk_text_view_get_visible_rect(
00683         GTK_TEXT_VIEW(info_pane[0].textview), &rect);
00684 
00685     if ((info_pane[0].adjustment->value + rect.height)
00686         >= info_pane[0].adjustment->upper)
00687             scroll_to_end = 1;
00688 
00689     gtk_text_buffer_get_end_iter(info_pane[0].textbuffer, &end);
00690 
00691     gtk_text_buffer_insert_with_tags(
00692         info_pane[0].textbuffer, &end, str, strlen(str),
00693         info_pane[0].color_tags[ncolor], NULL);
00694 
00695     gtk_text_buffer_insert(info_pane[0].textbuffer, &end, "\n" , 1);
00696 
00697     if (scroll_to_end)
00698         gtk_text_view_scroll_mark_onscreen(
00699             GTK_TEXT_VIEW(info_pane[0].textview), info_pane[0].textmark);
00700 
00701     /*
00702      * Non-black text is also copied to the critical message panel.  Why
00703      * does/should color alone determine the message is critical?
00704      */
00705     if (color != NDI_BLACK) {
00706 
00707         gtk_text_view_get_visible_rect(
00708             GTK_TEXT_VIEW(info_pane[1].textview), &rect);
00709 
00710         if ((info_pane[1].adjustment->value + rect.height)
00711             >= info_pane[1].adjustment->upper)
00712                 scroll_to_end = 1;
00713         else
00714             scroll_to_end = 0;
00715 
00716         gtk_text_buffer_get_end_iter(info_pane[1].textbuffer, &end);
00717 
00718         gtk_text_buffer_insert_with_tags(
00719             info_pane[1].textbuffer, &end, str, strlen(str),
00720             info_pane[1].color_tags[ncolor], NULL);
00721 
00722         gtk_text_buffer_insert(info_pane[1].textbuffer, &end, "\n" , 1);
00723 
00724         if (scroll_to_end)
00725             gtk_text_view_scroll_mark_onscreen(
00726                 GTK_TEXT_VIEW(info_pane[1].textview), info_pane[1].textmark);
00727     }
00728 }
00729 
00742 void draw_color_info(int colr, const char *buf) {
00743     draw_info(buf,colr);
00744 }
00745 
00750 void menu_clear(void) {
00751     int i;
00752 
00753     for (i=0; i < NUM_TEXT_VIEWS; i++) {
00754         gtk_text_buffer_set_text(info_pane[i].textbuffer, "", 0);
00755     }
00756 }
00757 
00767 void set_scroll(const char *s)
00768 {
00769 }
00770 
00780 void set_autorepeat(const char *s)
00781 {
00782 }
00783 
00795 int get_info_width(void)
00796 {
00797     return 40;
00798 }