Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

infoview.cc

Go to the documentation of this file.
00001 /*
00002  *      HT Editor
00003  *      infoview.cc
00004  *
00005  *      Copyright (C) 1999-2002 Stefan Weyergraf (stefan@weyergraf.de)
00006  *
00007  *      This program is free software; you can redistribute it and/or modify
00008  *      it under the terms of the GNU General Public License version 2 as
00009  *      published by the Free Software Foundation.
00010  *
00011  *      This program is distributed in the hope that it will be useful,
00012  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *      GNU General Public License for more details.
00015  *
00016  *      You should have received a copy of the GNU General Public License
00017  *      along with this program; if not, write to the Free Software
00018  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 
00024 #include "htdoc.h"
00025 #include "htiobox.h"
00026 #include "htpal.h"
00027 #include "htstring.h"
00028 #include "htsys.h"
00029 #include "infoview.h"
00030 #include "tools.h"
00031 
00032 class info_pos: public Object {
00033 public:
00034         UINT line;
00035         UINT ofs;
00036         
00037         info_pos(UINT l, UINT o) {
00038                 line = l;
00039                 ofs = o;
00040         }
00041 };
00042 
00043 int compare_keys_info_pos_delinear(Object *data_a, Object *data_b)
00044 {
00045         info_pos *a = (info_pos*)data_a;
00046         info_pos *b = (info_pos*)data_b;
00047         dword da = delinearize(a->line);
00048         dword db = delinearize(b->line);
00049         if (da == db) {
00050                 return delinearize(a->ofs) - delinearize(b->ofs);
00051         }
00052         return da - db;
00053 }
00054 
00055 int compare_keys_info_pos(Object *data_a, Object *data_b)
00056 {
00057         info_pos *a = (info_pos*)data_a;
00058         info_pos *b = (info_pos*)data_b;
00059         if (a->line == b->line) {
00060                 return a->ofs - b->ofs;
00061         }
00062         return a->line - b->line;
00063 }
00064 
00065 class info_xref: public Object {
00066 public:
00067         char *target;
00068         UINT len;
00069         
00070         info_xref(char *t, UINT l) {
00071                 target = strdup(t);
00072                 len = l;
00073         }
00074         
00075         ~info_xref() {
00076                 free(target);
00077         }
00078 };
00079 
00080 // FIXME: this function is considered harmful
00081 char *memndup(const char *s, int n)
00082 {
00083         char *q = (char*)malloc(n+1);
00084         memcpy(q, s, n);
00085         q[n] = 0;
00086         return q;
00087 }
00088 
00089 /*
00090  *
00091  */
00092 
00093 bool parse_xref_body(ht_streamfile *f, ht_tree *t, char **n, UINT *o, UINT *line, bool note)
00094 {
00095         whitespaces(n);
00096         char *l = strchr(*n, ':');
00097         if (!l) return false;
00098         char *e = l;
00099         while ((e>*n) && ((unsigned char)*(e-1)<=32)) e--;
00100         char *name = NULL;
00101         char *target = NULL;
00102         char *end = l;
00103         bool extrabreak=false;
00104         l++;
00105         whitespaces(&l);
00106         if (*(end+1) == ':') {
00107                 name = memndup(*n, e-*n);
00108                 end+=2;
00109         } else if ((note && (l-1 > end)) || (!note && (*(end+1) == ' '))){
00110                 if (*(end+1) == '\n') extrabreak = true;
00111                 char *v = l;
00112                 if (*l == '(') {
00113                         v = strchr(l, ')');
00114                         if (!v) return false;
00115                 }
00116                 char *q = v;
00117                 while (*q && (*q != '.') && (*q != ',')) q++;
00118                 if (!*q) return false;
00119                 char *p = q;
00120 
00121                 while ((q>l) && ((unsigned char)*(q-1)<=32)) q--;
00122                 name = memndup(*n, e-*n);
00123                 target = memndup(l, q-l);
00124                 end = p+1;
00125         } else return false;
00126         f->write(name, strlen(name));
00127 
00128 //      fprintf(stderr, "xref: %s -> %s\n", name, ttt);
00129 
00130         char *thetarget = strdup(target ? target : name);
00131         char *ttt = thetarget;
00132         while (*ttt) {
00133                 if (*ttt == '\n') *ttt = ' ';
00134                 ttt++;
00135         }               
00136 
00137         char *p = name;
00138         info_xref *x = new info_xref(thetarget, *o);
00139         t->insert(new info_pos(*line, *o), x);
00140         while (*p) {
00141                 if (*p=='\n') {
00142                         x->len =  *o - x->len;
00143                         *o = 0;
00144                         (*line)++;
00145                         x = new info_xref(thetarget, *o);
00146                         t->insert(new info_pos(*line, *o), x);
00147                 } else {
00148                         (*o)++;
00149                 }
00150                 p++;
00151         }
00152         x->len =  *o - x->len;
00153         if (extrabreak) {
00154                 char cr = '\n';
00155                 f->write(&cr, 1);
00156                 *o = 0;
00157                 (*line)++;
00158         }
00159         free(thetarget);
00160         if (name) free(name);
00161         if (target) free(target);
00162 //      fprintf(stderr, "t2\n");
00163         *n = end;
00164         return true;
00165 }
00166 
00167 ht_tree *parse_info_node(ht_streamfile *fl, char *infotext)
00168 {
00169         char *n = infotext;
00170         bool linestart = true;
00171         FILEOFS f = 0;
00172         UINT o = 0;
00173         UINT l = 0;
00174         ht_stree *t = new ht_stree();
00175         t->init(compare_keys_info_pos_delinear);
00176 
00177         while (*n && (*n != 0x1f)) {
00178                 char *on = n;
00179                 UINT oo = o;
00180                 UINT ol = l;
00181                 FILEOFS of = f;
00182                 char *k = (*n == '*') ? n : strchr(n, '*');
00183                 if ((k == n) && (ht_strnicmp(n, "*note", 5) == 0)) {
00184                         n += 5;
00185                         if (!parse_xref_body(fl, t, &n, &o, &l, true)) {
00186                                 n = on;
00187                                 o = oo;
00188                                 l = ol;
00189                                 f = of;
00190                                 fl->seek(f);
00191                                 goto fallback;
00192                         }
00193                         f = fl->tell();
00194                 } else if (linestart && (k == n) && (n[1] == ' ')) {
00195                         n++;
00196                         if (!parse_xref_body(fl, t, &n, &o, &l, false)) {
00197                                 n = on;
00198                                 o = oo;
00199                                 l = ol;
00200                                 f = of;
00201                                 fl->seek(f);
00202                                 goto fallback;
00203                         }
00204                         f = fl->tell();
00205                 } else {
00206 fallback:
00207                         if (k && (k>n)) {
00208                                 char *cr = strchr(n, '\n');
00209                                 if (cr && (cr<k)) k = cr;
00210                                 if (k-n == 0) goto fallback2;
00211                                 fl->write(n, k-n);
00212                                 linestart = false;
00213                                 o+= k-n;
00214                                 f+= k-n;
00215                                 n+= k-n;
00216                         } else {
00217 fallback2:
00218                                 fl->write(n, 1);
00219                                 if (*n == '\n') {
00220                                         linestart = true;
00221                                         o=0;
00222                                         l++;
00223                                 } else {
00224                                         linestart = false;
00225                                         o++;
00226                                 }
00227                                 n++;
00228                                 f++;
00229                         }
00230                 }
00231         }
00232         t->set_compare_keys(compare_keys_info_pos);
00233         return t;
00234 }
00235 /*
00236 \x1f\nFile:.*\n                 lex_line = 0; lex_pofs = 0; file_block(&YY_LVALP->node, yytext); lex_ofs += yyleng; return INFO_FILE;
00237 \x1f\nTag\ Table:.*\n           lex_ofs += strlen(yytext); return INFO_TTBL;
00238 \x1f\nEnd\ Tag\ Table.*\n       lex_ofs += strlen(yytext); return INFO_ENDTTBL;
00239 \*[nN]ote(\ |\n).[^:]*::        xref(&YY_LVALP->xref, yytext, 6); return INFO_XREF;
00240 \*[nN]ote(\ |\n).[^:]*:\ ((\(.+\))|.)[^.,]*[.,] xref(&YY_LVALP->xref, yytext, 6); return INFO_XREF;
00241 ^\*\ .[^:]*::                   xref(&YY_LVALP->xref, yytext, 2); return INFO_XREF;
00242 ^\*\ .[^:]*:\ ((\(.+\))|.)[^.,]*[.,]    xref(&YY_LVALP->xref, yytext, 2); return INFO_XREF;
00243 .                               lex_ofs++; lex_pofs++; return yytext[0];
00244 \n                              lex_ofs++; lex_line++; lex_pofs=0; return yytext[0];
00245 */
00246 
00247 /*
00248  *      CLASS ht_info_lexer
00249  */
00250 
00251 void ht_info_lexer::init(ht_view *pf)
00252 {
00253         ht_syntax_lexer::init();
00254         xrefs = NULL;
00255         cx = 0;
00256         cy = 0;
00257         pal_from = pf;
00258 }
00259 
00260 lexer_state ht_info_lexer::getinitstate()
00261 {
00262         return 1;
00263 }
00264 
00265 vcp ht_info_lexer::getcolor_syntax(UINT pal_index)
00266 {
00267         return VCP(VC_WHITE, VC_BLACK);
00268 }
00269 
00270 lexer_token ht_info_lexer::geterrortoken()
00271 {
00272         return 42;
00273 }
00274 
00275 char *ht_info_lexer::getname()
00276 {
00277         return "infoview";
00278 }
00279 
00280 #define ILT_TEXT                1
00281 #define ILT_LINK                2
00282 #define ILT_LINK_SEL    3
00283 
00284 lexer_token ht_info_lexer::gettoken(void *b, UINT buflen, text_pos p, bool start_of_line, lexer_state *ret_state, UINT *ret_len)
00285 {
00286         if (buflen) {
00287                 if (xrefs) {
00288                         info_pos q(p.line, p.pofs);
00289                         info_xref *x = (info_xref*)xrefs->get(&q);
00290                         if (x) {
00291                                 *ret_len = MIN(x->len, buflen);
00292                                 return ((cy == p.line) && (cx >= p.pofs) &&
00293                                 (cx < p.pofs + x->len)) ? ILT_LINK_SEL : ILT_LINK;
00294                         }
00295                 }                       
00296                 *ret_len = 1;
00297                 return ILT_TEXT;
00298         } else {
00299                 *ret_len = 0;
00300                 return 0;
00301         }
00302 }
00303 
00304 vcp ht_info_lexer::gettoken_color(lexer_token t)
00305 {
00306         switch (t) {
00307                 case ILT_TEXT:
00308                         return pal_from->getcolor(palidx_generic_text_focused);
00309                 case ILT_LINK:
00310                         return pal_from->getcolor(palidx_generic_text_shortcut);
00311                 case ILT_LINK_SEL:
00312                         return pal_from->getcolor(palidx_generic_text_shortcut_selected);
00313         }
00314         return VCP(VC_WHITE, VC_RED);
00315 }
00316 
00317 void ht_info_lexer::set_cursor(UINT x, UINT y)
00318 {
00319         cx = x;
00320         cy = y;
00321 }
00322 
00323 void ht_info_lexer::set_xrefs(ht_tree *x)
00324 {
00325         xrefs = x;
00326 }
00327 
00328 /*
00329  *      CLASS ht_info_textfile
00330  */
00331  
00332 class info_history_entry: public Object {
00333 public:
00334         char *cwd;
00335         char *file;
00336         char *node;
00337         UINT cursorx;
00338         UINT cursory;
00339         UINT xofs;
00340         UINT top_line;
00341 
00342         info_history_entry(char *c, char *f, char *n, UINT x, UINT y, UINT xo, UINT yo)
00343         {
00344                 cwd = ht_strdup(c);
00345                 file = ht_strdup(f);
00346                 node = ht_strdup(n);
00347                 cursorx = x;
00348                 cursory = y;
00349                 xofs = xo;
00350                 top_line = yo;
00351         }
00352 
00353         ~info_history_entry()
00354         {
00355                 if (cwd) free(cwd);
00356                 if (file) free(file);
00357                 if (node) free(node);
00358         }
00359 };
00360  
00361 void ht_info_textfile::init(ht_streamfile *s, bool own_s, ht_syntax_lexer *l)
00362 {
00363         start = 0;
00364         end = 0;
00365         ht_ltextfile::init(s, own_s, l);
00366 }
00367 
00368 void ht_info_textfile::done()
00369 {
00370         ht_ltextfile::done();
00371 }
00372 
00373 ht_ltextfile_line *ht_info_textfile::fetch_line(UINT line)
00374 {
00375         if (line < linecount())
00376                 return ht_ltextfile::fetch_line(start+line);
00377         return NULL;
00378 }
00379 
00380 UINT ht_info_textfile::linecount()
00381 {
00382         return end-start;
00383 }
00384 
00385 void ht_info_textfile::set_node(UINT ofs, UINT len)
00386 {
00387         UINT s, e, t;
00388         start = 0;
00389         end = ht_ltextfile::linecount();
00390         convert_ofs2line(ofs, &s, &t);
00391         convert_ofs2line(ofs+len, &e, &t);
00392         start = s;
00393         end = e;
00394 }
00395 
00396 /*
00397  *      CLASS ht_info_viewer
00398  */
00399 
00400 void ht_info_viewer::init(bounds *b)
00401 {
00402         ht_mem_file *f = new ht_mem_file();
00403         f->init();
00404 
00405         ht_info_textfile *s = new ht_info_textfile();
00406         s->init(f, true, NULL);
00407 
00408         ht_text_viewer::init(b, true, s, NULL);
00409         cwd = NULL;
00410         file = NULL;
00411         node = NULL;
00412         xrefs = NULL;
00413         history = new ht_clist();
00414         ((ht_clist*)history)->init();
00415 }
00416 
00417 void ht_info_viewer::done()
00418 {
00419         if (history) {
00420                 history->destroy();
00421                 delete history;
00422         }
00423         if (xrefs) {
00424                 xrefs->destroy();
00425                 delete xrefs;
00426         }
00427         if (cwd) free(cwd);
00428         if (node) free(node);
00429         if (file) free(file);
00430         ht_text_viewer::done();
00431 }
00432 
00433 char *ht_info_viewer::defaultpalette()
00434 {
00435         return palkey_generic_help_default;
00436 }
00437 
00438 void ht_info_viewer::draw()
00439 {
00440         ((ht_info_lexer*)lexer)->set_cursor(physical_cursorx(), cursory+top_line);
00441         ht_text_viewer::draw();
00442 }
00443 
00444 int ht_info_viewer::find_node(char *infotext, char *node)
00445 {
00446         char *tags[] = {"File", "Node", "Prev", "Next", "Up"};
00447 #define NUM_NODE_TAGS (sizeof (tags) / sizeof (tags[0]))
00448         char *s = infotext;
00449         char *firstnode = NULL;
00450         while ((s=strchr(s, 0x1f))) {
00451                 s++;
00452                 while ((*s>0) && (*s<32)) s++;
00453                 char *cr = strchr(s, '\n');
00454                 if (cr) {
00455                         while (*s && (s<cr)) {
00456                                 whitespaces(&s);
00457                                 char *os = s;
00458                                 for (UINT i=0; i<NUM_NODE_TAGS; i++) {
00459                                         UINT l = strlen(tags[i]);
00460                                         if ((strncmp(s, tags[i], l) == 0) && (s[l] == ':')) {
00461                                                 s += l+1;
00462                                                 whitespaces(&s);
00463                                                 char *e = strchr(s, ',');
00464                                                 if (!e || (e>cr)) e = cr;
00465                                                 if (!firstnode && (strcmp(tags[i], "Node") == 0)) {
00466                                                         firstnode = cr+1;
00467                                                 }
00468                                                 if ((strcmp(tags[i], "Node") == 0) &&
00469                                                 ((size_t)(e-s) == strlen(node)) &&
00470                                                 (strncmp(s, node, e-s)==0)) {
00471                                                         return cr+1-infotext;
00472                                                 }
00473                                                 s = e+1;
00474                                         }
00475                                 }
00476                                 if (os == s) break;
00477                         }
00478                 }
00479         }
00480 //     if (firstnode) return firstnode-infotext;
00481         return -1;
00482 }
00483 
00484 ht_tree *ht_info_viewer::get_xrefs()
00485 {
00486         return xrefs;
00487 }
00488 
00489 bool ht_info_viewer::gotonode(char *f, char *n)
00490 {
00491         return igotonode(f, n, true);
00492 }
00493 
00494 UINT ht_info_viewer::readfile(char *fn, char **text)
00495 {
00496         FILE *f = fopen(fn, "r");
00497         if (!f) return 0;
00498         
00499         fseek(f, 0, SEEK_END);
00500         int size = ftell(f);
00501         fseek(f, 0, SEEK_SET);
00502 
00503         char *x = (char*)malloc(size+1);
00504         UINT len = fread(x, 1, size, f);
00505         x[len] = 0;
00506 
00507         fclose(f);
00508 
00509         *text = x;
00510         return len;
00511 }
00512 
00513 bool ht_info_viewer::igotonode(char *f, char *n, bool add2hist)
00514 {
00515         char *infotext = NULL;
00516         char nfile[HT_NAME_MAX];
00517         nfile[0] = 0;
00518         char ncwd[HT_NAME_MAX];
00519         ncwd[0] = 0;
00520         bool newnode = !node || (node && (strcmp(node, n) != 0));
00521         int fl = strlen(f)-strlen(MAGIC_HT_HELP);
00522         if ((fl>=0) && (strcmp(f+fl, MAGIC_HT_HELP) == 0)) {
00523                 infotext = strdup(htinfo);
00524                 strcpy(ncwd, "");
00525                 strcpy(nfile, MAGIC_HT_HELP);
00526         } else {
00527                 char ff[HT_NAME_MAX], cff[HT_NAME_MAX];
00528                 cff[0] = 0;
00529                 if (file && sys_common_canonicalize(cff, file, cwd, sys_is_path_delim) != 0) return false;
00530                 if (sys_common_canonicalize(ff, f, cwd, sys_is_path_delim) != 0) return false;
00531                 if ((strcmp(ff, cff) != 0) || newnode) {
00532                         if (!readfile(ff, &infotext)) return false;
00533                         char c[HT_NAME_MAX];
00534                         if (sys_dirname(c, ff) == 0) strcpy(ncwd, c);
00535                         if (sys_basename(c, ff) == 0) strcpy(nfile, c);
00536                 }
00537         }
00538         if (infotext) {
00539                 int o = find_node(infotext, n);
00540                 if (o == -1) {
00541                         free(infotext);
00542                         return false;
00543                 }
00544 
00545                 ht_mem_file *m = new ht_mem_file();
00546                 m->init();
00547                 
00548                 ht_tree *x = parse_info_node(m, infotext+o);
00549                 if (x == NULL) {
00550                         m->done();
00551                         delete m;
00552                         free(infotext);
00553                         return false;
00554                 }
00555                 free(infotext);
00556                 /* add to history or not*/
00557                 if (add2hist && cwd && file && node) {
00558 //                      fprintf(stderr, "histhist: c:%s, f:%s, n:%s\n", cwd, file, node);
00559                         history->insert(new info_history_entry(
00560                                 cwd, file, node, cursorx, cursory, 
00561                                 xofs, top_line));
00562                 }                       
00563                 /* now modify text_viewer's state */
00564                 if (xrefs) {
00565                         xrefs->destroy();
00566                         delete xrefs;
00567                 }
00568                 xrefs = x;
00569 
00570                 ht_info_textfile *infofile=new ht_info_textfile();
00571                 infofile->init(m, true, NULL);
00572 
00573                 ht_info_lexer *infolexer = new ht_info_lexer();
00574                 infolexer->init(this);
00575 
00576                 infolexer->set_xrefs(xrefs);
00577                 infofile->set_node(0, m->get_size());
00578                 set_textfile(infofile, true);
00579                 set_lexer(infolexer, true);
00580 
00581                 cursorx = 0;
00582                 cursory = 0;
00583                 xofs = 0;
00584                 top_line = 0;
00585 
00586                 if (file) free(file);
00587                 file = strdup(nfile);
00588 
00589                 if (cwd) free(cwd);
00590                 cwd = strdup(ncwd);
00591 
00592                 if (node) free(node);
00593                 node = strdup(n);
00594 
00595 //              fprintf(stderr, "setset: c:%s, f:%s, n:%s\n", cwd, file, node);
00596 
00597                 select_clear();
00598                 return true;
00599         }
00600         return false;
00601 }
00602 
00603 void ht_info_viewer::get_pindicator_str(char *buf)
00604 {
00605         buf += sprintf(buf, " %d:%d ", top_line+cursory+1, xofs+cursorx+1);
00606         sprintf(buf, "(%s) %s ", file, node);
00607 }
00608 
00609 void ht_info_viewer::handlemsg(htmsg *msg)
00610 {
00611         if (msg->msg == msg_keypressed) {
00612                 switch (msg->data1.integer) {
00613                         case K_Space:
00614                         case K_Return: 
00615                                 if (get_xrefs()) {
00616                                         info_pos p(top_line + cursory, xofs + physical_cursorx());
00617                                         info_xref *x = (info_xref*)get_xrefs()->get(&p);
00618                                         if (!x) {
00619                                                 UINT cx = physical_cursorx();
00620                                                 info_pos *q = (info_pos*)get_xrefs()->enum_prev((Object**)&x, &p);
00621                                                 if ((q) && ((q->line != top_line+cursory) ||
00622                                                 (cx < q->ofs) || (cx >= q->ofs + x->len))) {
00623                                                         x = NULL;
00624                                                 }
00625                                         }
00626                                         if (x) {
00627                                                 char *p = NULL, *q = NULL;
00628                                                 char *a = x->target;
00629                                                 if (*a == '(') {
00630                                                         char *b = strchr(a, ')');
00631                                                         if (b) {
00632                                                                 p = memndup(a+1, b-a-1);
00633                                                                 q = ht_strdup(b+1);
00634                                                         }
00635                                                 }
00636                                                 if (!p) p = ht_strdup(file);
00637                                                 if (!q) q = ht_strdup(x->target);
00638                                                 if (!igotonode(p, q, true))
00639                                                         errorbox("help topic '(%s)%s' not found", p, q);
00640                                                 if (p) free(p);
00641                                                 if (q) free(q);
00642                                         }
00643                                         clearmsg(msg);
00644                                         dirtyview();
00645                                         return;
00646                                 }
00647                                 break;
00648                         case K_Alt_Backspace:
00649                         case K_Backspace: {
00650                                 int c;
00651                                 if ((c = history->count())) {
00652                                         info_history_entry *e = (info_history_entry*)history->get(c-1);
00653 //                                      fprintf(stderr, "backspace: %s, %s\n", e->file, e->node);
00654                                         if (e->node) {
00655                                                 if (igotonode(e->file, e->node, false)) {
00656                                                         cursorx = e->cursorx;
00657                                                         cursory = e->cursory;
00658                                                         xofs = e->xofs;
00659                                                         top_line = e->top_line;
00660                                                 } else {
00661                                                         errorbox("help topic '(%s)%s' not found", e->file, e->node);
00662                                                 }                                               
00663                                                 history->del(c-1);
00664                                                 clearmsg(msg);
00665                                                 dirtyview();
00666                                                 return;
00667                                         }                                               
00668                                 }
00669                                 break;                                  
00670                         }
00671                         case K_Tab: {
00672                                 if (get_xrefs()) {
00673                                         info_pos p(top_line + cursory, xofs + physical_cursorx());
00674                                         info_xref *r;
00675                                         info_pos *q = (info_pos*)get_xrefs()->enum_next((Object**)&r, &p);
00676                                         if (q) {
00677                                                 goto_line(q->line);
00678                                                 cursor_pput(q->ofs);
00679                                         }
00680                                 }                                       
00681                                 clearmsg(msg);
00682                                 dirtyview();
00683                                 return;
00684                         }
00685                         case K_BackTab: {
00686                                 if (get_xrefs()) {
00687                                         info_pos p(top_line + cursory, xofs + physical_cursorx());
00688                                         info_xref *r;
00689                                         info_pos *q = (info_pos*)get_xrefs()->enum_prev((Object**)&r, &p);
00690                                         if (q) {
00691                                                 goto_line(q->line);
00692                                                 cursor_pput(q->ofs);
00693                                         }
00694                                 }                                       
00695                                 clearmsg(msg);
00696                                 dirtyview();
00697                                 return;
00698                         }
00699                 }
00700         }
00701         ht_text_viewer::handlemsg(msg);
00702 }

Generated on Fri May 7 21:15:40 2004 by doxygen 1.3.5