OSDN Git Service

change User-Agent header version number.
[bbk/bchanl.git] / src / main.c
index faedfa2..b1d8517 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * main.c
  *
- * Copyright (c) 2009 project bchan
+ * Copyright (c) 2009-2012 project bchan
  *
  * This software is provided 'as-is', without any express or implied
  * warranty. In no event will the authors be held liable for any damages
 #include       "subjectretriever.h"
 #include       "subjectcache.h"
 #include       "subjectparser.h"
+#include       "subjectlist.h"
 #include       "subjectlayout.h"
 #include    "bbsmenuretriever.h"
 #include    "bbsmenucache.h"
 #include    "bbsmenuparser.h"
+#include       "bbsmenufilter.h"
 #include    "bbsmenulayout.h"
+#include    "extbbslist.h"
+#include    "util.h"
 
 #include    "bchanl_subject.h"
+#include    "bchanl_hmi.h"
+#include    "bchanl_menus.h"
+#include    "bchanl_panels.h"
+
+#include       <http/http_connector.h>
 
 #ifdef BCHANL_CONFIG_DEBUG
 # define DP(arg) printf arg
 #define BCHANL_DBX_TEXT_MSG_RETRBBSMENU 27
 #define BCHANL_DBX_TEXT_MSG_RETRSUBJECT 28
 #define BCHANL_DBX_TEXT_MSG_ERRRETR 29
+#define BCHANL_DBX_TB_SBJTOPT_FLT 30
+#define BCHANL_DBX_WS_SBJTOPT_ODR 31
+#define BCHANL_DBX_WS_SBJTOPT_ODRBY    32
+#define BCHANL_DBX_TEXT_CATE_EXTBBS 33
 
 #define BCHANL_MENU_WINDOW 3
 
+#define BCHANL_COMMONSTORAGE_EXTBBSLIST_RECTYPE 30
+#define BCHANL_COMMONSTORAGE_EXTBBSLIST_SUBTYPE 1
+
+LOCAL UB bchanl_httpheader_useragent[] = "Monazilla/1.00 (bchanl/0.200)";
+LOCAL W bchanl_httpheader_useragent_len = sizeof(bchanl_httpheader_useragent) - 1;
+
 typedef struct bchanl_hmistate_t_ bchanl_hmistate_t;
 struct bchanl_hmistate_t_ {
        PTRSTL ptr;
@@ -113,100 +132,174 @@ LOCAL VOID bchanl_hmistate_initialize(bchanl_hmistate_t *hmistate)
        }
 }
 
-typedef struct bchanl_t_ bchanl_t;
+typedef struct bchanl_bbsmenu_t_ bchanl_bbsmenu_t;
+struct bchanl_bbsmenu_t_ {
+       W gid;
 
-LOCAL VOID bchanl_killme(bchanl_t *bchanl);
+       bbsmnretriever_t *retriever;
+       bbsmncache_t *cache;
+       bbsmnparser_t *parser;
+       bbsmnfilter_t *filter;
+       bbsmnlayout_t *layout;
+       bbsmndraw_t *draw;
 
-typedef struct bchanl_subjectwindow_t_ bchanl_subjectwindow_t;
-struct bchanl_subjectwindow_t_ {
-       bchanl_t *owner;
+       bchanl_subjecthash_t *subjecthash;
+       extbbslist_t *extbbslist;
+       extbbslist_editcontext_t *editctx;
+       TC *category_extbbs;
+};
 
-       W wid;
-       W gid;
-       bchanl_subject_t *sbjt;
-       sbjtdraw_t *draw;
-       commonwindow_t *window;
+#define BCHANL_NETWORK_FLAG_WAITHTTPEVENT 0x00000001
+
+struct bchanl_t_ {
+       W taskid;
+       W flgid; /* for reduce TMOUT message sending. */
+
+       bchanl_mainmenu_t mainmenu;
+       VID vid;
+       W exectype;
+
+       bchanl_hmistate_t hmistate;
+
+       http_connector_t *connector;
+
+       sbjtretriever_t *retriever;
+
+       bchanl_subjecthash_t *subjecthash;
+       bchanl_bbsmenu_t bbsmenu;
+       bchanl_subject_t *currentsubject;
+       bchanl_subject_t *nextsubject;
+       struct {
+               Bool resnum;
+               Bool since;
+               Bool vigor;
+       } subjectdisplay;
+
+       bchanlhmi_t *hmi;
+       subjectwindow_t *subjectwindow;
+       bbsmenuwindow_t *bbsmenuwindow;
+       subjectoptionwindow_t *subjectoptionwindow;
+       registerexternalwindow_t *registerexternalwindow;
+       externalbbswindow_t *externalbbswindow;
 };
+typedef struct bchanl_t_ bchanl_t;
 
-LOCAL VOID bchanl_subjectwindow_scroll(VP arg, W dh, W dv)
+LOCAL VOID bchanl_swapresnumberdisplay(bchanl_t *bchanl)
 {
-       bchanl_subjectwindow_t *bchanl = (bchanl_subjectwindow_t*)arg;
-       if (bchanl->draw == NULL) {
-               return;
+       if (bchanl->subjectdisplay.resnum != False) {
+               bchanl->subjectdisplay.resnum = False;
+       } else {
+               bchanl->subjectdisplay.resnum = True;
+       }
+}
+
+LOCAL VOID bchanl_swapsincedisplay(bchanl_t *bchanl)
+{
+       if (bchanl->subjectdisplay.since != False) {
+               bchanl->subjectdisplay.since = False;
+       } else {
+               bchanl->subjectdisplay.since = True;
+       }
+}
+
+LOCAL VOID bchanl_swapvigordisplay(bchanl_t *bchanl)
+{
+       if (bchanl->subjectdisplay.vigor != False) {
+               bchanl->subjectdisplay.vigor = False;
+       } else {
+               bchanl->subjectdisplay.vigor = True;
+       }
+}
+
+LOCAL VOID bchanl_killme(bchanl_t *bchanl);
+
+LOCAL VOID bchanl_subjectwindow_draw(bchanl_t *bchanl)
+{
+       sbjtdraw_t *draw;
+       RECT r;
+       if (bchanl->currentsubject == NULL) {
+               do {
+                       if (subjectwindow_startredisp(bchanl->subjectwindow, &r) == 0) {
+                               break;
+                       }
+                       subjectwindow_eraseworkarea(bchanl->subjectwindow, &r);
+               } while (subjectwindow_endredisp(bchanl->subjectwindow) > 0);
+       } else {
+               draw = bchanl_subject_getdraw(bchanl->currentsubject);
+               do {
+                       if (subjectwindow_startredisp(bchanl->subjectwindow, &r) == 0) {
+                               break;
+                       }
+                       subjectwindow_eraseworkarea(bchanl->subjectwindow, &r);
+                       sbjtdraw_draw(draw, &r);
+               } while (subjectwindow_endredisp(bchanl->subjectwindow) > 0);
        }
-       sbjtdraw_scrollviewrect(bchanl->draw, dh, dv);
-       wscr_wnd(bchanl->wid, NULL, -dh, -dv, W_MOVE|W_RDSET);
 }
 
-LOCAL VOID bchanl_subjectwindow_draw(VP arg, RECT *r)
+LOCAL VOID bchanl_subjectwindow_scroll(bchanl_t *bchanl, W dh, W dv)
 {
-       bchanl_subjectwindow_t *bchanl = (bchanl_subjectwindow_t*)arg;
-       if (bchanl->draw == NULL) {
+       sbjtdraw_t *draw;
+       if (bchanl->currentsubject == NULL) {
                return;
        }
-       sbjtdraw_draw(bchanl->draw, r);
+       draw = bchanl_subject_getdraw(bchanl->currentsubject);
+       sbjtdraw_scrollviewrect(draw, dh, dv);
+       subjectwindow_scrollworkarea(bchanl->subjectwindow, -dh, -dv);
+       bchanl_subjectwindow_draw(bchanl);
 }
 
-LOCAL VOID bchanl_subjectwindow_resize(VP arg)
+LOCAL VOID bchanl_subjectwindow_resize(bchanl_t *bchanl, SIZE newsize)
 {
-       bchanl_subjectwindow_t *bchanl = (bchanl_subjectwindow_t*)arg;
        W l,t,r,b;
-       RECT work;
+       sbjtdraw_t *draw;
 
-       if (bchanl->draw == NULL) {
+       if (bchanl->currentsubject == NULL) {
                return;
        }
+       draw = bchanl_subject_getdraw(bchanl->currentsubject);
 
-       sbjtdraw_getviewrect(bchanl->draw, &l, &t, &r, &b);
-       wget_wrk(bchanl->wid, &work);
+       sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
 
-       r = l + work.c.right - work.c.left;
-       b = t + work.c.bottom - work.c.top;
+       r = l + newsize.h;
+       b = t + newsize.v;
 
-       sbjtdraw_setviewrect(bchanl->draw, l, t, r, b);
-       commonwindow_setworkrect(bchanl->window, l, t, r, b);
+       sbjtdraw_setviewrect(draw, l, t, r, b);
+       subjectwindow_setworkrect(bchanl->subjectwindow, l, t, r, b);
 
-       wreq_dsp(bchanl->wid);
+       bchanl_subjectwindow_draw(bchanl);
 }
 
-LOCAL VOID bchanl_subjectwindow_close(VP arg)
+LOCAL VOID bchanl_subjectwindow_close(bchanl_t *bchanl)
 {
-       bchanl_subjectwindow_t *bchanl = (bchanl_subjectwindow_t*)arg;
-       bchanl_killme(bchanl->owner);
+       bchanl_killme(bchanl);
 }
 
-LOCAL VOID bchanl_subjectwindow_click(VP arg, PNT pos)
+LOCAL VOID bchanl_subjectwindow_press(bchanl_t *bchanl, PNT evpos)
 {
-}
-
-LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
-{
-       bchanl_subjectwindow_t *bchanl = (bchanl_subjectwindow_t*)arg;
-       sbjtparser_thread_t *thread;
+       sbjtlist_tuple_t *tuple;
+       sbjtdraw_t *draw;
        WID wid_butup;
-       W event_type, size, err, fsn_len;
+       W event_type, size, err, fsn_len, dx, dy;
        void *fsn;
        GID gid;
-       PNT pos, p1;
+       PNT pos, p1, pos_butup;
        TR_VOBJREC vrec;
        TRAYREC tr_rec;
        WEVENT paste_ev;
        SEL_RGN sel;
-       RECT r0;
+       RECT r0, vframe;
 
-       if (bchanl->draw == NULL) {
-               return;
-       }
-       if (bchanl->sbjt == NULL) {
+       if (bchanl->currentsubject == NULL) {
                return;
        }
+       draw = bchanl_subject_getdraw(bchanl->currentsubject);
 
-       err = sbjtdraw_findthread(bchanl->draw, wev->s.pos, &thread);
+       err = sbjtdraw_findthread(draw, evpos, &tuple, &vframe);
        if (err == 0) {
                return;
        }
 
-       gid = wsta_drg(bchanl->wid, 0);
+       gid = subjectwindow_startdrag(bchanl->subjectwindow);
        if (gid < 0) {
                DP_ER("wsta_drg error:", gid);
                return;
@@ -215,19 +308,21 @@ LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
        gget_fra(gid, &r0);
        gset_vis(gid, r0);
 
-       p1 = wev->s.pos;
+       dx = vframe.c.left - evpos.x;
+       dy = vframe.c.top - evpos.y;
+
+       p1 = evpos;
        sel.sts = 0;
-       sel.rgn.r.c.left = p1.x - 5;
-       sel.rgn.r.c.top = p1.y - 5;
-       sel.rgn.r.c.right = sel.rgn.r.c.left + 100;
-       sel.rgn.r.c.bottom = sel.rgn.r.c.top + 20;
+       sel.rgn.r.c.left = vframe.c.left;
+       sel.rgn.r.c.top = vframe.c.top;
+       sel.rgn.r.c.right = vframe.c.right;
+       sel.rgn.r.c.bottom = vframe.c.bottom;
        adsp_sel(gid, &sel, 1);
 
        gset_ptr(PS_GRIP, NULL, -1, -1);
        for (;;) {
-               event_type = wget_drg(&pos, wev);
+               event_type = subjectwindow_getdrag(bchanl->subjectwindow, &pos, &wid_butup, &pos_butup);
                if (event_type == EV_BUTUP) {
-                       wid_butup = wev->s.wid;
                        break;
                }
                if (event_type != EV_NULL) {
@@ -246,9 +341,10 @@ LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
        }
        gset_ptr(PS_SELECT, NULL, -1, -1);
        adsp_sel(gid, &sel, 0);
-       wend_drg();
+       subjectwindow_enddrag(bchanl->subjectwindow);
 
-       if (wid_butup == bchanl->wid) {
+       /* BUTUP on self window or no window or system message panel */
+       if ((wid_butup == subjectwindow_getWID(bchanl->subjectwindow))||(wid_butup == 0)||(wid_butup == -1)) {
                return;
        }
 
@@ -263,11 +359,15 @@ LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
                return;
        }
        fsn_len = dget_siz((B*)fsn);
-       err = bchanl_subject_createviewervobj(bchanl->sbjt, thread, fsn, fsn_len, &vrec.vseg, (LINK*)&vrec.vlnk);
+       err = bchanl_subject_createviewervobj(bchanl->currentsubject, tuple, fsn, fsn_len, &vrec.vseg, (LINK*)&vrec.vlnk);
        if (err < 0) {
                DP_ER("bchanl_subject_createviewervobj error", err);
                return;
        }
+       if (err == BCHANL_SUBJECT_CREATEVIEWERVOBJ_CANCELED) {
+               DP(("canceled\n"));
+               return;
+       }
 
        tr_rec.id = TR_VOBJ;
        tr_rec.len = sizeof(TR_VOBJREC);
@@ -282,7 +382,8 @@ LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
        }
 
        paste_ev.r.type = EV_REQUEST;
-       paste_ev.r.r.p.rightbot = wev->s.pos;
+       paste_ev.r.r.p.rightbot.x = pos_butup.x + dx;
+       paste_ev.r.r.p.rightbot.y = pos_butup.y + dy;
        paste_ev.r.cmd = W_PASTE;
        paste_ev.r.wid = wid_butup;
        err = wsnd_evt(&paste_ev);
@@ -306,110 +407,208 @@ LOCAL VOID bchanl_subjectwindow_press(VP arg, WEVENT *wev)
        wswi_wnd(wid_butup, NULL);
 }
 
-LOCAL W bchanl_subjectwindow_initialize(bchanl_t *owner, bchanl_subjectwindow_t *window, RECT *r, TC *tit)
+LOCAL VOID bchanl_subjectwindow_butdn(bchanl_t *bchanl, W dck, PNT evpos)
 {
-       WID wid;
-       commonwindow_t *cwindow;
-
-       wid = wopn_wnd(WA_SIZE|WA_HHDL|WA_VHDL|WA_BBAR|WA_RBAR, 0, r, NULL, 1, tit, NULL, NULL);
-       if (wid < 0) {
-               return wid;
-       }
-       cwindow = commonwindow_new(wid, window);
-       if (window == NULL) {
-               return -1; /* TODO */
-       }
-       commonwindow_setscrollcallback(cwindow, bchanl_subjectwindow_scroll);
-       commonwindow_setdrawcallback(cwindow, bchanl_subjectwindow_draw);
-       commonwindow_setresizecallback(cwindow, bchanl_subjectwindow_resize);
-       commonwindow_setclosecallback(cwindow, bchanl_subjectwindow_close);
-       commonwindow_setclickcallback(cwindow, bchanl_subjectwindow_click);
-       commonwindow_setdclickcallback(cwindow, bchanl_subjectwindow_click);
-       commonwindow_setpresscallback(cwindow, bchanl_subjectwindow_press);
-       commonwindow_setqpresscallback(cwindow, bchanl_subjectwindow_press);
-
-       window->owner = owner;
-       window->wid = wid;
-       window->gid = wget_gid(wid);
-       window->window = cwindow;
-       window->draw = NULL;
+       switch (dck) {
+       case    W_CLICK:
+       case    W_DCLICK:
+       case    W_QPRESS:
+       default:
+               return;
+       case    W_PRESS:
+               bchanl_subjectwindow_press(bchanl, evpos);
+       }
+}
 
-       return 0;
+LOCAL VOID bchanl_setcurrentsubject(bchanl_t *bchanl, bchanl_subject_t *sbjt)
+{
+       bchanl->currentsubject = sbjt;
+       bchanl_subject_setresnumberdisplay(sbjt, bchanl->subjectdisplay.resnum);
+       bchanl_subject_setsincedisplay(sbjt, bchanl->subjectdisplay.since);
+       bchanl_subject_setvigordisplay(sbjt, bchanl->subjectdisplay.vigor);
+       subjectwindow_requestredisp(bchanl->subjectwindow);
 }
 
-LOCAL VOID bchanl_subjectwindow_finalize(bchanl_subjectwindow_t *window)
+LOCAL VOID bchanl_setnextsubject(bchanl_t *bchanl, bchanl_subject_t *sbjt)
 {
-       commonwindow_delete(window->window);
-       wcls_wnd(window->wid, CLR);
+       bchanl->nextsubject = sbjt;
 }
 
-LOCAL VOID bchanl_subjectwindow_setsubject(bchanl_subjectwindow_t *window, bchanl_subject_t *sbjt)
+LOCAL VOID bchanl_bbsmenuwindow_draw(bchanl_t *bchanl)
 {
-       window->sbjt = sbjt;
-       window->draw = bchanl_subject_getdraw(sbjt);
-       wreq_dsp(window->wid);
+       RECT r;
+       do {
+               if (bbsmenuwindow_startredisp(bchanl->bbsmenuwindow, &r) == 0) {
+                       break;
+               }
+               bbsmenuwindow_eraseworkarea(bchanl->bbsmenuwindow, &r);
+               bbsmndraw_draw(bchanl->bbsmenu.draw, &r);
+       } while (bbsmenuwindow_endredisp(bchanl->bbsmenuwindow) > 0);
 }
 
-LOCAL VOID bchanl_subjectwindow_setdraw(bchanl_subjectwindow_t *window, sbjtdraw_t *draw)
+LOCAL VOID bchanl_bbsmenuwindow_scroll(bchanl_t *bchanl, W dh, W dv)
 {
-       window->sbjt = NULL;
-       window->draw = draw;
-       wreq_dsp(window->wid);
+       bbsmndraw_scrollviewrect(bchanl->bbsmenu.draw, dh, dv);
+       bbsmenuwindow_scrollworkarea(bchanl->bbsmenuwindow, -dh, -dv);
+       bchanl_bbsmenuwindow_draw(bchanl);
 }
 
-typedef struct bchanl_bbsmenuwindow_t_ bchanl_bbsmenuwindow_t;
-struct bchanl_bbsmenuwindow_t_ {
-       bchanl_t *owner;
+LOCAL VOID bchanl_bbsmenuwindow_resize(bchanl_t *bchanl, SIZE newsize)
+{
+       W l,t,r,b;
 
-       W wid;
-       W gid;
-       bbsmndraw_t *draw;
-       bchanl_subjecthash_t *subjecthash;
-       commonwindow_t *window;
-};
+       bbsmndraw_getviewrect(bchanl->bbsmenu.draw, &l, &t, &r, &b);
 
-LOCAL VOID bchanl_bbsmenuwindow_scroll(VP arg, W dh, W dv)
+       r = l + newsize.h;
+       b = t + newsize.v;
+
+       bbsmndraw_setviewrect(bchanl->bbsmenu.draw, l, t, r, b);
+       bbsmenuwindow_setworkrect(bchanl->bbsmenuwindow, l, t, r, b);
+
+       bchanl_bbsmenuwindow_draw(bchanl);
+}
+
+LOCAL VOID bchanl_bbsmenuwindow_close(bchanl_t *bchanl)
 {
-       bchanl_bbsmenuwindow_t *bchanl = (bchanl_bbsmenuwindow_t*)arg;
-       bbsmndraw_scrollviewrect(bchanl->draw, dh, dv);
-       wscr_wnd(bchanl->wid, NULL, -dh, -dv, W_MOVE|W_RDSET);
+       bchanl_killme(bchanl);
 }
 
-LOCAL VOID bchanl_bbsmenuwindow_draw(VP arg, RECT *r)
+LOCAL VOID bchanl_updatesubjectorder(bchanl_t *bchanl, SUBJECTOPTIONWINDOW_ORDERVALUE_T order, SUBJECTOPTIONWINDOW_ORDERBYVALUE_T orderby, TC *filterword, W filterword_len)
 {
-       bchanl_bbsmenuwindow_t *bchanl = (bchanl_bbsmenuwindow_t*)arg;
-       bbsmndraw_draw(bchanl->draw, r);
+       Bool descending;
+       W sbjt_orderby;
+
+       if (order == SUBJECTOPTIONWINDOW_ORDERVALUE_DESCENDING) {
+               descending = True;
+       } else {
+               descending = False;
+       }
+       switch (orderby) {
+       case SUBJECTOPTIONWINDOW_ORDERBYVALUE_NUMBER:
+       default:
+               sbjt_orderby = BCHANL_SUBJECT_SORTBY_NUMBER;
+               break;
+       case SUBJECTOPTIONWINDOW_ORDERBYVALUE_RES:
+               sbjt_orderby = BCHANL_SUBJECT_SORTBY_RES;
+               break;
+       case SUBJECTOPTIONWINDOW_ORDERBYVALUE_SINCE:
+               sbjt_orderby = BCHANL_SUBJECT_SORTBY_SINCE;
+               break;
+       case SUBJECTOPTIONWINDOW_ORDERBYVALUE_VIGOR:
+               sbjt_orderby = BCHANL_SUBJECT_SORTBY_VIGOR;
+               break;
+       }
+
+       bchanl_subject_reorder(bchanl->currentsubject, filterword, filterword_len, sbjt_orderby, descending);
+
+       subjectwindow_requestredisp(bchanl->subjectwindow);
 }
 
-LOCAL VOID bchanl_bbsmenuwindow_resize(VP arg)
+LOCAL VOID bchanl_changesubjectorder(bchanl_t *bchanl, W neworder)
 {
-       bchanl_bbsmenuwindow_t *bchanl = (bchanl_bbsmenuwindow_t*)arg;
-       W l,t,r,b;
-       RECT work;
+       SUBJECTOPTIONWINDOW_ORDERBYVALUE_T orderby;
+       W len;
+       TC buf[512];
 
-       bbsmndraw_getviewrect(bchanl->draw, &l, &t, &r, &b);
-       wget_wrk(bchanl->wid, &work);
+       if (bchanl->currentsubject == NULL) {
+               return;
+       }
+
+       subjectoptionwindow_getorderbyvalue(bchanl->subjectoptionwindow, &orderby);
+       len = subjectoptionwindow_getfiltertext(bchanl->subjectoptionwindow, buf, 512);
+
+       bchanl_updatesubjectorder(bchanl, neworder, orderby, buf, len);
+}
+
+LOCAL VOID bchanl_changesubjectorderby(bchanl_t *bchanl, W neworderby)
+{
+       SUBJECTOPTIONWINDOW_ORDERBYVALUE_T order;
+       W len;
+       TC buf[512];
+
+       if (bchanl->currentsubject == NULL) {
+               return;
+       }
+
+       subjectoptionwindow_getordervalue(bchanl->subjectoptionwindow, &order);
+       len = subjectoptionwindow_getfiltertext(bchanl->subjectoptionwindow, buf, 512);
+
+       bchanl_updatesubjectorder(bchanl, order, neworderby, buf, len);
+}
+
+LOCAL VOID bchanl_changesubjectfilterword(bchanl_t *bchanl, TC *newstr, W newstr_len)
+{
+       sbjtlayout_t *layout;
+       sbjtdraw_t *draw;
+       SUBJECTOPTIONWINDOW_ORDERVALUE_T order;
+       SUBJECTOPTIONWINDOW_ORDERBYVALUE_T orderby;
+       RECT w_work;
+       W l, t, r, b;
+
+       if (bchanl->currentsubject == NULL) {
+               return;
+       }
+
+       subjectoptionwindow_getordervalue(bchanl->subjectoptionwindow, &order);
+       subjectoptionwindow_getorderbyvalue(bchanl->subjectoptionwindow, &orderby);
 
-       r = l + work.c.right - work.c.left;
-       b = t + work.c.bottom - work.c.top;
+       bchanl_updatesubjectorder(bchanl, order, orderby, newstr, newstr_len);
 
-       bbsmndraw_setviewrect(bchanl->draw, l, t, r, b);
-       commonwindow_setworkrect(bchanl->window, l, t, r, b);
+       subjectwindow_getworkrect(bchanl->subjectwindow, &w_work);
+       draw = bchanl_subject_getdraw(bchanl->currentsubject);
+       sbjtdraw_setviewrect(draw, 0, 0, w_work.c.right, w_work.c.bottom);
+       subjectwindow_setworkrect(bchanl->subjectwindow, 0, 0, w_work.c.right, w_work.c.bottom);
 
-       wreq_dsp(bchanl->wid);
+       layout = bchanl_subject_getlayout(bchanl->currentsubject);
+       sbjtlayout_getdrawrect(layout, &l, &t, &r, &b);
+       subjectwindow_setdrawrect(bchanl->subjectwindow, l, t, r, b);
 }
 
-LOCAL VOID bchanl_bbsmenuwindow_close(VP arg)
+LOCAL VOID bchanl_changedisplayattribute(bchanl_t *bchanl)
 {
-       bchanl_bbsmenuwindow_t *bchanl = (bchanl_bbsmenuwindow_t*)arg;
-       bchanl_killme(bchanl->owner);
+       sbjtlayout_t *layout;
+       SUBJECTOPTIONWINDOW_ORDERBYVALUE_T order;
+       SUBJECTOPTIONWINDOW_ORDERBYVALUE_T orderby;
+       W len;
+       TC buf[512];
+       W l, t, r, b;
+
+       if (bchanl->currentsubject == NULL) {
+               return;
+       }
+
+       subjectoptionwindow_getordervalue(bchanl->subjectoptionwindow, &order);
+       subjectoptionwindow_getorderbyvalue(bchanl->subjectoptionwindow, &orderby);
+       len = subjectoptionwindow_getfiltertext(bchanl->subjectoptionwindow, buf, 512);
+
+       bchanl_updatesubjectorder(bchanl, order, orderby, buf, len);
+
+       layout = bchanl_subject_getlayout(bchanl->currentsubject);
+       sbjtlayout_getdrawrect(layout, &l, &t, &r, &b);
+       subjectwindow_setdrawrect(bchanl->subjectwindow, l, t, r, b);
 }
 
-LOCAL VOID bchanl_sendsubjectrequest(bchanl_t *bchanl, bchanl_subject_t *subject);
+LOCAL VOID bchanl_sendsubjectrequest(bchanl_t *bchanl, bchanl_subject_t *subject)
+{
+       sbjtcache_t *cache;
+       W err;
+
+       bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_BUSY);
+       pdsp_msg(bchanl->hmistate.msg_retr_subject);
+
+       cache = bchanl_subject_getcache(subject);
+       err = sbjtretriever_sendrequest(bchanl->retriever, cache);
+       if (err < 0) {
+               pdsp_msg(bchanl->hmistate.msg_error_retr);
+               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
+               return;
+       }
+       bchanl_setnextsubject(bchanl, subject);
+       set_flg(bchanl->flgid, BCHANL_NETWORK_FLAG_WAITHTTPEVENT);
+}
 
-LOCAL VOID bchanl_bbsmenuwindow_click(VP arg, PNT pos)
+LOCAL VOID bchanl_bbsmenuwindow_click(bchanl_t *bchanl, PNT pos)
 {
-       bchanl_bbsmenuwindow_t *bchanl = (bchanl_bbsmenuwindow_t*)arg;
        bbsmnparser_item_t *item;
        bchanl_subject_t *subject;
        W fnd;
@@ -418,7 +617,7 @@ LOCAL VOID bchanl_bbsmenuwindow_click(VP arg, PNT pos)
        TC *title;
        W title_len;
 
-       fnd = bbsmndraw_findboard(bchanl->draw, pos, &item);
+       fnd = bbsmndraw_findboard(bchanl->bbsmenu.draw, pos, &item);
        if (fnd == 0) {
                DP(("not found\n"));
                return;
@@ -434,77 +633,39 @@ LOCAL VOID bchanl_bbsmenuwindow_click(VP arg, PNT pos)
        }
        bchanl_subject_gettitle(subject, &title, &title_len);
 
-       bchanl_sendsubjectrequest(bchanl->owner, subject);
+       bchanl_sendsubjectrequest(bchanl, subject);
 }
 
-LOCAL VOID bchanl_bbsmenuwindow_press(VP arg, WEVENT *wev)
+LOCAL VOID bchanl_bbsmenuwindow_butdn(bchanl_t *bchanl, W dck, PNT evpos)
 {
+       switch (dck) {
+       case    W_DCLICK:
+       case    W_PRESS:
+       case    W_QPRESS:
+       default:
+               return;
+       case    W_CLICK:
+               bchanl_bbsmenuwindow_click(bchanl, evpos);
+       }
 }
 
-LOCAL W bchanl_bbsmenuwindow_initialize(bchanl_t *owner, bchanl_bbsmenuwindow_t *window, RECT *r, TC *tit, bchanl_subjecthash_t *subjecthash)
-{
-       WID wid;
-       commonwindow_t *cwindow;
-
-       wid = wopn_wnd(WA_SIZE|WA_HHDL|WA_VHDL|WA_BBAR|WA_RBAR, 0, r, NULL, 1, tit, NULL, NULL);
-       if (wid < 0) {
-               return wid;
-       }
-       cwindow = commonwindow_new(wid, window);
-       if (cwindow == NULL) {
-               return -1; /* TODO */
-       }
-       commonwindow_setscrollcallback(cwindow, bchanl_bbsmenuwindow_scroll);
-       commonwindow_setdrawcallback(cwindow, bchanl_bbsmenuwindow_draw);
-       commonwindow_setresizecallback(cwindow, bchanl_bbsmenuwindow_resize);
-       commonwindow_setclosecallback(cwindow, bchanl_bbsmenuwindow_close);
-       commonwindow_setclickcallback(cwindow, bchanl_bbsmenuwindow_click);
-       commonwindow_setdclickcallback(cwindow, bchanl_bbsmenuwindow_click);
-       commonwindow_setpresscallback(cwindow, bchanl_bbsmenuwindow_press);
-       commonwindow_setqpresscallback(cwindow, bchanl_bbsmenuwindow_press);
-
-       window->owner = owner;
-       window->wid = wid;
-       window->gid = wget_gid(wid);
-       window->window = cwindow;
-       window->draw = NULL;
-       window->subjecthash = subjecthash;
-
-       return 0;
-}
-
-LOCAL VOID bchanl_bbsmenuwindow_finalize(bchanl_bbsmenuwindow_t *window)
-{
-       commonwindow_delete(window->window);
-       wcls_wnd(window->wid, CLR);
-}
-
-typedef struct bchanl_bbsmenu_t_ bchanl_bbsmenu_t;
-struct bchanl_bbsmenu_t_ {
-       W gid;
-
-       bbsmnretriever_t *retriever;
-       bbsmncache_t *cache;
-       bbsmnparser_t *parser;
-       bbsmnlayout_t *layout;
-       bbsmndraw_t *draw;
-
-       bchanl_subjecthash_t *subjecthash;
-};
-
-LOCAL W bchanl_bbsmenu_initialize(bchanl_bbsmenu_t *bchanl, GID gid, bchanl_subjecthash_t *subjecthash)
+LOCAL W bchanl_bbsmenu_initialize(bchanl_bbsmenu_t *bchanl, GID gid, bchanl_subjecthash_t *subjecthash, LINK *storage, http_connector_t *connector)
 {
        bbsmnretriever_t *retriever;
        bbsmncache_t *cache;
        bbsmnparser_t *parser;
+       bbsmnfilter_t *filter;
        bbsmnlayout_t *layout;
        bbsmndraw_t *draw;
+       extbbslist_t *extbbslist;
+       TC *category_extbbs;
+       W err;
 
        cache = bbsmncache_new();
        if (cache == NULL) {
                goto error_cache;
        }
-       retriever = bbsmnretriever_new();
+       retriever = bbsmnretriever_new(connector, bchanl_httpheader_useragent, bchanl_httpheader_useragent_len);
        if (retriever == NULL) {
                goto error_retriever;
        }
@@ -512,6 +673,10 @@ LOCAL W bchanl_bbsmenu_initialize(bchanl_bbsmenu_t *bchanl, GID gid, bchanl_subj
        if (parser == NULL) {
                goto error_parser;
        }
+       filter = bbsmnfilter_new();
+       if (filter == NULL) {
+               goto error_filter;
+       }
        layout = bbsmnlayout_new(gid);
        if (layout == NULL) {
                goto error_layout;
@@ -520,20 +685,41 @@ LOCAL W bchanl_bbsmenu_initialize(bchanl_bbsmenu_t *bchanl, GID gid, bchanl_subj
        if (draw == NULL) {
                goto error_draw;
        }
+       extbbslist = extbbslist_new(storage, BCHANL_COMMONSTORAGE_EXTBBSLIST_RECTYPE, BCHANL_COMMONSTORAGE_EXTBBSLIST_SUBTYPE);
+       if (extbbslist == NULL) {
+               DP_ER("extbbslist_new", 0);
+               goto error_extbbslist;
+       }
+       err = extbbslist_readfile(extbbslist);
+       if (err < 0) {
+               DP_ER("extbbslist_readfile", 0);
+               goto error_extbbslist_readfile;
+       }
+       dget_dtp(TEXT_DATA, BCHANL_DBX_TEXT_CATE_EXTBBS, (void**)&category_extbbs);
 
        bchanl->gid = gid;
        bchanl->retriever = retriever;
        bchanl->cache = cache;
        bchanl->parser = parser;
+       bchanl->filter = filter;
        bchanl->layout = layout;
        bchanl->draw = draw;
        bchanl->subjecthash = subjecthash;
+       bchanl->extbbslist = extbbslist;
+       bchanl->editctx = NULL;
+       bchanl->category_extbbs = category_extbbs;
 
        return 0;
 
+error_extbbslist_readfile:
+       extbbslist_delete(extbbslist);
+error_extbbslist:
+       bbsmndraw_delete(draw);
 error_draw:
        bbsmnlayout_delete(layout);
 error_layout:
+       bbsmnfilter_delete(filter);
+error_filter:
        bbsmnparser_delete(parser);
 error_parser:
        bbsmnretriever_delete(retriever);
@@ -554,133 +740,466 @@ LOCAL W bchanl_bbsmenu_appenditemtohash(bchanl_bbsmenu_t *bchanl, bbsmnparser_it
        return err;
 }
 
-LOCAL VOID bchanl_bbsmenu_relayout(bchanl_bbsmenu_t *bchanl, bchanl_bbsmenuwindow_t *bchanl_window)
+LOCAL VOID bchanl_bbsmenu_registerexternalbbs(bchanl_bbsmenu_t *bchanl, TC *title, W title_len, TC *url, W url_len)
+{
+       extbbslist_editcontext_append(bchanl->editctx, title, title_len, url, url_len);
+}
+
+LOCAL VOID bchanl_bbsmenu_relayoutcache(bchanl_bbsmenu_t *bchanl)
 {
-       W err, l, t, r, b, tmp = 0;
-       Bool check;
+       W err, ret;
        bbsmnparser_t *parser = bchanl->parser;
        bbsmnparser_item_t *item;
+       bbsmnfilter_t *filter = bchanl->filter;
        bbsmnlayout_t *layout = bchanl->layout;
 
-       bbsmnlayout_clear(layout);
-       bbsmnparser_clear(parser);
-
        for (;;) {
                err = bbsmnparser_getnextitem(parser, &item);
                if (err != 1) {
                        break;
                }
-               if (item == NULL) {
-                       break;
-               }
-               check = bbsmnparser_item_checkboradurl(item);
-               if (check == True) {
-                       if (tmp == 0) { /* temporary filter. */
-                               bbsmnparser_item_delete(item);
-                               tmp = 1;
-                               continue;
-                       }
-                       if (item->category == NULL) {
-                               err = bchanl_bbsmenu_appenditemtohash(bchanl, item);
+               bbsmnfilter_inputitem(filter, item);
+               for (;;) {
+                       ret = bbsmnfilter_outputitem(filter, &item);
+                       if (item != NULL) {
+                               if (item->category == NULL) {
+                                       err = bchanl_bbsmenu_appenditemtohash(bchanl, item);
+                                       if (err < 0) {
+                                               return;
+                                       }
+                               }
+                               err = bbsmnlayout_appenditem(layout, item);
                                if (err < 0) {
                                        return;
                                }
                        }
-                       err = bbsmnlayout_appenditem(layout, item);
-                       if (err < 0) {
-                               return;
+                       if (ret != BBSMNFILTER_OUTPUTITEM_CONTINUE) {
+                               break;
                        }
-               } else {
-                       bbsmnparser_item_delete(item);
                }
+               if (ret == BBSMNFILTER_OUTPUTITEM_END) {
+                       printf("D\n");
+                       break;
+               }
+               if (ret != BBSMNFILTER_OUTPUTITEM_WAITNEXT) {
+                       /* TODO: error */
+                       break;
+               }
+/*
+               if (item == NULL) {
+                       printf("F\n");
+                       break;
+               }
+*/
        }
-
-       bbsmnlayout_getdrawrect(bchanl->layout, &l, &t, &r, &b);
-       commonwindow_setdrawrect(bchanl_window->window, l, t, r, b);
-
-       wreq_dsp(bchanl_window->wid);
 }
 
-struct bchanl_t_ {
-       W taskid;
-       W mbfid;
-
-       MENUITEM *mnitem;
-       MNID mnid;
-       VID vid;
-       W exectype;
-
-       bchanl_hmistate_t hmistate;
-
-       sbjtretriever_t *retriever;
-
-       bchanl_subjecthash_t *subjecthash;
-       bchanl_subjectwindow_t subjectwindow;
-       bchanl_bbsmenuwindow_t bbsmenuwindow;
-       bchanl_bbsmenu_t bbsmenu;
-
-       struct {
-               sbjtcache_t *cache;
-               sbjtparser_t *parser;
-               sbjtlayout_t *layout;
-               sbjtdraw_t *draw;
-       } testdata;
-};
-
-#define BCHANL_MESSAGE_RETRIEVER_RELAYOUT 1
-#define BCHANL_MESSAGE_RETRIEVER_ERROR -1
-
-LOCAL VOID bchanl_retriever_task(W arg)
+LOCAL VOID bchanl_bbsmenu_relayoutexternal(bchanl_bbsmenu_t *bchanl)
 {
-       bchanl_t *bchanl;
-       bbsmnretriever_t *retr;
-       bbsmncache_t *cache;
-       W msg,err;
+       W err, ret, category_len, title_len, url_len;
+       Bool cont;
+       TC *category, *title;
+       UB *url;
+       extbbslist_readcontext_t *ctx;
+       bbsmnparser_t *parser = bchanl->parser;
+       bbsmnparser_item_t *item;
+       bbsmnlayout_t *layout = bchanl->layout;
+       extbbslist_t *list = bchanl->extbbslist;
 
-       bchanl = (bchanl_t*)arg;
-       retr = bchanl->bbsmenu.retriever;
-       cache = bchanl->bbsmenu.cache;
+       ret = extbbslist_number(bchanl->extbbslist);
+       if (ret <= 0) {
+               return;
+       }
+
+       category = bchanl->category_extbbs;
+       category_len = tc_strlen(category);
+       item = bbsmnparser_newcategoryitem(parser, category, category_len);
+       if (item == NULL) {
+               return;
+       }
+       err = bbsmnlayout_appenditem(layout, item);
+       if (err < 0) {
+               return;
+       }
 
+       ctx = extbbslist_startread(list);
+       if (ctx == NULL) {
+               return;
+       }
        for (;;) {
-               DP(("before rcv_mbf %d\n", bchanl->mbfid));
-               err = rcv_mbf(bchanl->mbfid, (VP)&msg, T_FOREVER);
-               DP_ER("rcv_mbf error:",err);
-               if (err != 4) {
-                       continue;
+               cont = extbbslist_readcontext_getnext(ctx, &title, &title_len, &url, &url_len);
+               if (cont == False) {
+                       break;
                }
 
-               err = bbsmnretriever_sendrequest(retr, cache);
-
-               switch (err) {
-               case BBSMNRETRIEVER_REQUEST_ALLRELOAD:
-                       req_tmg(0, BCHANL_MESSAGE_RETRIEVER_RELAYOUT);
+               item = bbsmnparser_newboarditem(parser, title, title_len, url, url_len);
+               if (item == NULL) {
                        break;
-               default:
-                       req_tmg(0, BCHANL_MESSAGE_RETRIEVER_ERROR);
-                       DP_ER("bbsmnretreiver_request error",err);
+               }
+               err = bchanl_bbsmenu_appenditemtohash(bchanl, item);
+               if (err < 0) {
+                       break;
+               }
+               err = bbsmnlayout_appenditem(layout, item);
+               if (err < 0) {
                        break;
                }
        }
-
-       ext_tsk();
+       extbbslist_endread(list, ctx);
 }
 
-LOCAL W bchanl_prepare_network(bchanl_t *bchanl)
+LOCAL VOID bchanl_bbsmenu_relayout(bchanl_bbsmenu_t *bchanl, bbsmenuwindow_t *window)
 {
-       if (bchanl->retriever == NULL) {
-               return 0;
-       }
+       W l, t, r, b;
 
-       bchanl->mbfid = cre_mbf(sizeof(W), sizeof(W), DELEXIT);
-       if (bchanl->mbfid < 0) {
-               DP_ER("error cre_mbf:", bchanl->mbfid);
-               return -1;
-       }
-       bchanl->taskid = cre_tsk(bchanl_retriever_task, -1, (W)bchanl);
-       if (bchanl->taskid < 0) {
-               del_mbf(bchanl->mbfid);
-               bchanl->mbfid = -1;
-               DP_ER("error cre_tsk:", bchanl->taskid);
+       bbsmnlayout_clear(bchanl->layout);
+       bbsmnfilter_clear(bchanl->filter);
+       bbsmnparser_clear(bchanl->parser);
+
+       bchanl_bbsmenu_relayoutcache(bchanl);
+       bchanl_bbsmenu_relayoutexternal(bchanl);
+
+       bbsmnlayout_getdrawrect(bchanl->layout, &l, &t, &r, &b);
+       bbsmenuwindow_setdrawrect(window, l, t, r, b);
+
+       bbsmenuwindow_requestredisp(window);
+}
+
+LOCAL Bool bchanl_registerexternalbbs(bchanl_t *bchanl)
+{
+       TC title[128];
+       TC url[256];
+       W title_len, url_len, l, t, r, b;
+       TCURL_CHECK_VALID_BBSURL ret;
+       GID gid;
+
+       title_len = registerexternalwindow_getboradnametext(bchanl->registerexternalwindow, title, 128);
+       if (title_len < 0) {
+               DP_ER("registerexternalwindow_getboradnametext error", title_len);
+               return True;
+       }
+       title[title_len] = TNULL;
+       url_len = registerexternalwindow_geturltext(bchanl->registerexternalwindow, url, 255);
+       if (url_len < 0) {
+               DP_ER("registerexternalwindow_geturltext error", url_len);
+               return True;
+       }
+       url[url_len] = TNULL;
+
+       ret = tcurl_check_valid_bbsurl(url, url_len);
+       switch (ret) {
+       case TCURL_CHECK_VALID_BBSURL_NO_LAST_SLSH:
+               url[url_len] = TK_SLSH;
+               url_len++;
+               /* intentional */
+       case TCURL_CHECK_VALID_BBSURL_OK:
+               break;
+       case TCURL_CHECK_VALID_BBSURL_INVALID_SCHEME:
+               bchan_panels_urlerror_scheme();
+               return False;
+       case TCURL_CHECK_VALID_BBSURL_INVALID_HOST:
+               bchan_panels_urlerror_host();
+               return False;
+       case TCURL_CHECK_VALID_BBSURL_INVALID_PATH:
+               bchan_panels_urlerror_path();
+               return False;
+       }
+
+       bchanl_bbsmenu_registerexternalbbs(&bchanl->bbsmenu, title, title_len, url, url_len);
+
+       registerexternalwindow_setboradnametext(bchanl->registerexternalwindow, NULL, 0);
+       registerexternalwindow_seturltext(bchanl->registerexternalwindow, NULL, 0);
+
+       gid = externalbbswindow_getGID(bchanl->externalbbswindow);
+       extbbslist_editcontext_getdrawrect(bchanl->bbsmenu.editctx, gid, &l, &t, &r, &b);
+       externalbbswindow_setdrawrect(bchanl->externalbbswindow, l, t, r, b);
+       externalbbswindow_requestredisp(bchanl->externalbbswindow);
+
+       return True;
+}
+
+LOCAL VOID bchanl_externalbbswindow_draw(bchanl_t *bchanl)
+{
+       RECT r;
+
+       do {
+               if (externalbbswindow_startredisp(bchanl->externalbbswindow, &r) == 0) {
+                       break;
+               }
+               externalbbswindow_eraseworkarea(bchanl->externalbbswindow, &r);
+               extbbslist_editcontext_draw(bchanl->bbsmenu.editctx, externalbbswindow_getGID(bchanl->externalbbswindow), &r);
+       } while (externalbbswindow_endredisp(bchanl->externalbbswindow) > 0);
+}
+
+LOCAL VOID bchanl_externalbbswindow_resize(bchanl_t *bchanl, SIZE newsize)
+{
+       W l,t,r,b;
+
+       extbbslist_editcontext_getviewrect(bchanl->bbsmenu.editctx, &l, &t, &r, &b);
+
+       r = l + newsize.h;
+       b = t + newsize.v;
+
+       extbbslist_editcontext_setviewrect(bchanl->bbsmenu.editctx, l, t, r, b);
+       externalbbswindow_setworkrect(bchanl->externalbbswindow, l, t, r, b);
+}
+
+LOCAL VOID bchanl_externalbbswindow_close(bchanl_t *bchanl)
+{
+       Bool changed, save = False;
+       BCHAN_PANELS_SAVECONFIRM_RESULT confirm;
+
+       changed = extbbslist_editcontext_ischanged(bchanl->bbsmenu.editctx);
+       if (changed != False) {
+               confirm = bchan_panels_saveconfirm();
+               switch (confirm) {
+               case BCHAN_PANELS_SAVECONFIRM_RESULT_CANCEL:
+                       return;
+               case BCHAN_PANELS_SAVECONFIRM_RESULT_OK_NOSAVE:
+                       save = False;
+                       break;
+               case BCHAN_PANELS_SAVECONFIRM_RESULT_OK_SAVE:
+                       save = True;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       extbbslist_endedit(bchanl->bbsmenu.extbbslist, bchanl->bbsmenu.editctx, save);
+       bchanl->bbsmenu.editctx = NULL;
+       externalbbswindow_close(bchanl->externalbbswindow);
+       if (save != False) {
+               bchanl_bbsmenu_relayout(&bchanl->bbsmenu, bchanl->bbsmenuwindow);
+       }
+}
+
+LOCAL VOID bchanl_externalbbswindow_butdn(bchanl_t *bchanl, W type, PNT pos)
+{
+       Bool found;
+       W sel;
+
+       if (type == W_CLICK) {
+               found = extbbslist_editcontext_finditem(bchanl->bbsmenu.editctx, pos, &sel);
+               if (found != False) {
+                       extbbslist_editcontext_setselect(bchanl->bbsmenu.editctx, sel);
+               }
+               externalbbswindow_requestredisp(bchanl->externalbbswindow);
+       }
+}
+
+LOCAL W bchanl_externalbbswindow_paste_readtray(bchanl_t *bchanl)
+{
+       W err, name_len, url_len;
+       TC *name, *url;
+
+       err = tray_getextbbsinfo(NULL, &name_len, NULL, &url_len);
+       if (err < 0) {
+               return 1;
+       }
+
+       name = malloc(sizeof(TC)*(name_len+1));
+       if (name == NULL) {
+               return 1;
+       }
+       url = malloc(sizeof(TC)*url_len+1);
+       if (url == NULL) {
+               free(name);
+               return 1;
+       }
+
+       err = tray_getextbbsinfo(name, &name_len, url, &url_len);
+       if (err < 0) {
+               free(url);
+               free(name);
+               return 1;
+       }
+       name[name_len] = TNULL;
+       url[url_len] = TNULL;
+
+       registerexternalwindow_setboradnametext(bchanl->registerexternalwindow, name, name_len);
+       registerexternalwindow_seturltext(bchanl->registerexternalwindow, url, url_len);
+       registerexternalwindow_open(bchanl->registerexternalwindow);
+
+       free(url);
+       free(name);
+
+       return 0;
+}
+
+LOCAL VOID bchanl_externalbbswindow_paste(bchanl_t *bchanl)
+{
+       W nak;
+       PNT p = {0x8000, 0x8000};
+       nak = bchanl_externalbbswindow_paste_readtray(bchanl);
+       externalbbswindow_responsepasterequest(bchanl->externalbbswindow, nak, &p);
+}
+
+LOCAL VOID bchanl_externalbbswindow_scroll(bchanl_t *bchanl, W dh, W dv)
+{
+       extbbslist_editcontext_scrollviewrect(bchanl->bbsmenu.editctx, dh, dv);
+       externalbbswindow_scrollworkarea(bchanl->externalbbswindow, -dh, -dv);
+}
+
+#define BCHANL_MESSAGE_RETRIEVER_RELAYOUT 1
+#define BCHANL_MESSAGE_RETRIEVER_ERROR -1
+#define BCHANL_MESSAGE_HTTP_EVENT 2
+
+LOCAL Bool bchanl_bbsmenu_httpevent(bchanl_bbsmenu_t *bchanl, http_connector_event *hevent)
+{
+       Bool ok;
+       W err;
+
+       ok = bbsmnretriever_iswaitingendpoint(bchanl->retriever, hevent->endpoint);
+       if (ok == False) {
+               return False;
+       }
+       err = bbsmnretriever_recievehttpevent(bchanl->retriever, bchanl->cache, hevent);
+
+       switch (err) {
+       case BBSMNRETRIEVER_REQUEST_ALLRELOAD:
+               req_tmg(0, BCHANL_MESSAGE_RETRIEVER_RELAYOUT);
+               break;
+       case BBSMNRETRIEVER_REQUEST_WAITNEXT:
+               break;
+       default:
+               req_tmg(0, BCHANL_MESSAGE_RETRIEVER_ERROR);
+               DP_ER("bbsmnretriever_recievehttpevent", err);
+               break;
+       }
+
+       return True;
+}
+
+LOCAL Bool bchanl_subject_httpevent(bchanl_t *bchanl, http_connector_event *hevent)
+{
+       Bool ok;
+       W err;
+       sbjtcache_t *cache;
+       sbjtlayout_t *layout;
+       sbjtdraw_t *draw;
+       TC *title;
+       RECT w_work;
+       W l, t, r, b, title_len;
+
+       if (bchanl->nextsubject == NULL) {
+               return False;
+       }       
+
+       ok = sbjtretriever_iswaitingendpoint(bchanl->retriever, hevent->endpoint);
+       if (ok == False) {
+               return False;
+       }
+       cache = bchanl_subject_getcache(bchanl->nextsubject);
+       err = sbjtretriever_recievehttpevent(bchanl->retriever, cache, hevent);
+
+       switch (err) {
+       case SBJTRETRIEVER_REQUEST_ALLRELOAD:
+               /* should asynchronous layout? */
+
+               subjectoptionwindow_setfiltertext(bchanl->subjectoptionwindow, NULL, 0);
+               err = subjectoptionwindow_setordervalue(bchanl->subjectoptionwindow, SUBJECTOPTIONWINDOW_ORDERVALUE_ASCENDING);
+               subjectoptionwindow_setorderbyvalue(bchanl->subjectoptionwindow, SUBJECTOPTIONWINDOW_ORDERBYVALUE_NUMBER);
+
+               bchanl_setcurrentsubject(bchanl, bchanl->nextsubject);
+               bchanl_setnextsubject(bchanl, NULL);
+               bchanl_subject_relayout(bchanl->currentsubject);
+
+               subjectwindow_getworkrect(bchanl->subjectwindow, &w_work);
+               draw = bchanl_subject_getdraw(bchanl->currentsubject);
+               sbjtdraw_setviewrect(draw, 0, 0, w_work.c.right, w_work.c.bottom);
+               subjectwindow_setworkrect(bchanl->subjectwindow, 0, 0, w_work.c.right, w_work.c.bottom);
+
+               layout = bchanl_subject_getlayout(bchanl->currentsubject);
+               sbjtlayout_getdrawrect(layout, &l, &t, &r, &b);
+               subjectwindow_setdrawrect(bchanl->subjectwindow, l, t, r, b);
+
+               bchanl_subject_gettitle(bchanl->currentsubject, &title, &title_len);
+               subjectwindow_settitle(bchanl->subjectwindow, title);
+
+               pdsp_msg(NULL);
+               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
+
+               break;
+       case SBJTRETRIEVER_REQUEST_WAITNEXT:
+               break;
+       default:
+               req_tmg(0, BCHANL_MESSAGE_RETRIEVER_ERROR);
+               DP_ER("bbsmnretriever_recievehttpevent", err);
+               break;
+       }
+
+       return True;
+}
+
+LOCAL VOID bchanl_http_task(W arg)
+{
+       bchanl_t *bchanl;
+       http_connector_t *connector;
+       bbsmnretriever_t *retr;
+       bbsmncache_t *cache;
+       W err;
+
+       bchanl = (bchanl_t*)arg;
+       connector = bchanl->connector;
+       retr = bchanl->bbsmenu.retriever;
+       cache = bchanl->bbsmenu.cache;
+
+       for (;;) {
+               err = http_connector_waitconnection(connector, T_FOREVER);
+               if (err < 0) {
+                       DP_ER("http_connector_waitconnection", err);
+                       req_tmg(0, BCHANL_MESSAGE_RETRIEVER_ERROR);
+                       break;
+               }
+
+               err = wai_flg(bchanl->flgid, BCHANL_NETWORK_FLAG_WAITHTTPEVENT, WF_AND, T_FOREVER);
+               if (err < 0) {
+                       DP_ER("wai_flg", err);
+               }
+               req_tmg(0, BCHANL_MESSAGE_HTTP_EVENT);
+       }
+
+       ext_tsk();
+}
+
+LOCAL VOID bchanl_handle_httpevent(bchanl_t *bchanl)
+{
+       W err;
+       http_connector_event hevent;
+       Bool rcv;
+
+       set_flg(bchanl->flgid, BCHANL_NETWORK_FLAG_WAITHTTPEVENT);
+
+       err = http_connector_getevent(bchanl->connector, &hevent);
+       if (err < 0) {
+               return;
+       }
+
+       rcv = bchanl_bbsmenu_httpevent(&bchanl->bbsmenu, &hevent);
+       if (rcv != False) {
+               return;
+       }
+
+       rcv = bchanl_subject_httpevent(bchanl, &hevent);
+}
+
+LOCAL W bchanl_prepare_network(bchanl_t *bchanl)
+{
+       if (bchanl->retriever == NULL) {
+               return 0;
+       }
+
+       bchanl->taskid = cre_tsk(bchanl_http_task, -1, (W)bchanl);
+       if (bchanl->taskid < 0) {
+               DP_ER("error cre_tsk:", bchanl->taskid);
+               return -1;
+       }
+       bchanl->flgid = cre_flg(0, DELEXIT);
+       if (bchanl->flgid < 0) {
+               ter_tsk(bchanl->taskid);
+               bchanl->taskid = -1;
+               DP_ER("error cre_flg:", bchanl->flgid);
                return -1;
        }
 
@@ -689,11 +1208,11 @@ LOCAL W bchanl_prepare_network(bchanl_t *bchanl)
 
 LOCAL W bchanl_networkrequest_bbsmenu(bchanl_t *bchanl)
 {
-       W msg = 1, err;
+       W err;
        static UW lastrequest = 0;
        UW etime;
 
-       if (bchanl->mbfid < 0) {
+       if (bchanl->flgid < 0) {
                return 0;
        }
 
@@ -707,119 +1226,166 @@ LOCAL W bchanl_networkrequest_bbsmenu(bchanl_t *bchanl)
        }
        lastrequest = etime;
 
-       err = snd_mbf(bchanl->mbfid, &msg, sizeof(W), T_FOREVER);
+       bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_BUSY);
+       pdsp_msg(bchanl->hmistate.msg_retr_bbsmenu);
+
+       err = bbsmnretriever_sendrequest(bchanl->bbsmenu.retriever, bchanl->bbsmenu.cache);
        if (err < 0) {
-               DP_ER("snd_mbf error:", err);
+               DP_ER("bbsmnretriever_sendrequest error:", err);
+               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
                return err;
        }
-
-       bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_BUSY);
-       pdsp_msg(bchanl->hmistate.msg_retr_bbsmenu);
+       set_flg(bchanl->flgid, BCHANL_NETWORK_FLAG_WAITHTTPEVENT);
 
        return 0;
 }
 
-static WEVENT  wev0;
-
-LOCAL W bchanl_initialize(bchanl_t *bchanl, VID vid, W exectype)
+LOCAL W bchanl_initialize(bchanl_t *bchanl, VID vid, W exectype, LINK *storage)
 {
        static  RECT    r0 = {{400, 100, 700+7, 200+30}};
        static  RECT    r1 = {{100, 100, 300+7, 300+30}};
+       static  RECT    r2 = {{400, 300, 800+7, 400+30}};
+       static  PAT     white = {{0, 16, 16, 0x10ffffff, 0, FILL100}};
+       static  PAT     bgpat0;
+       static  PAT *bgpat;
        TC *title0 = NULL, *title1 = NULL;
-       W len, err;
+       W err;
+       WID wid;
        GID gid;
-       MENUITEM *mnitem_dbx, *mnitem;
-       MNID mnid;
        RECT w_work;
+       PNT p0 = {450, 0};
+       http_connector_t *connector;
        sbjtretriever_t *retriever;
+       bchanlhmi_t *hmi;
        bchanl_subjecthash_t *subjecthash;
+       subjectwindow_t *subjectwindow;
+       bbsmenuwindow_t *bbsmenuwindow;
+       subjectoptionwindow_t *subjectoptionwindow;
+       registerexternalwindow_t *registerexternalwindow;
+       externalbbswindow_t *externalbbswindow;
+
+       err = wget_inf(WI_PANELBACK, &bgpat0, sizeof(bgpat0));
+       if (err != sizeof(bgpat0)) {
+               bgpat = &white;
+       } else {
+               bgpat = &bgpat0;
+       }
+
+       connector = http_connector_new();
+       if (connector == NULL) {
+               DP_ER("http_connector_new error", 0);
+               goto error_http_connector;
+       }
 
-       retriever = sbjtretriever_new();
+       retriever = sbjtretriever_new(connector, bchanl_httpheader_useragent, bchanl_httpheader_useragent_len);
        if (retriever == NULL) {
                DP_ER("sbjtretriever_new error", 0);
                goto error_retriever;
        }
+       hmi = bchanlhmi_new();
+       if (hmi == NULL) {
+               DP_ER("bchanlhmi_new error", 0);
+               goto error_bchanlhmi;
+       }
        dget_dtp(TEXT_DATA, BCHANL_DBX_TEXT_WINDOWTITLE_SUBJECT, (void**)&title0);
-       err = bchanl_subjectwindow_initialize(bchanl, &(bchanl->subjectwindow), &r0, title0);
-       if (err < 0) {
-               DP_ER("bchanl_subjectwindow_initialize error", err);
+       subjectwindow = bchanlhmi_newsubjectwindow(hmi, &r0, 0, title0, NULL);
+       if (subjectwindow == NULL) {
+               DP_ER("bchanlhmi_newsubjectwindow error", 0);
                goto error_subjectwindow;
        }
-       gid = wget_gid(bchanl->subjectwindow.wid);
+       gid = subjectwindow_getGID(subjectwindow);
        subjecthash = bchanl_subjecthash_new(gid, 100);
        if (subjecthash == NULL) {
                DP_ER("bchanl_subjecthash_new error", 0);
                goto error_subjecthash;
        }
+       subjectoptionwindow = bchanlhmi_newsubjectoptionwindow(hmi, &p0, subjectwindow, NULL, bgpat, BCHANL_DBX_TB_SBJTOPT_FLT, BCHANL_DBX_WS_SBJTOPT_ODR, BCHANL_DBX_WS_SBJTOPT_ODRBY);
+       if (subjectoptionwindow == NULL) {
+               DP_ER("bchanlhmi_newsubjectoptionwindow", 0);
+               goto error_subjectoptionwindow;
+       }
        dget_dtp(TEXT_DATA, BCHANL_DBX_TEXT_WINDOWTITLE_BBSMENU, (void**)&title1);
-       err = bchanl_bbsmenuwindow_initialize(bchanl, &(bchanl->bbsmenuwindow), &r1, title1, subjecthash);
-       if (err < 0) {
-               DP_ER("bchanl_bbsmenuwindow_initialize error", err);
+       bbsmenuwindow = bchanlhmi_newbbsmenuwindow(hmi, &r1, 0, title1, NULL);
+       if (bbsmenuwindow == NULL) {
+               DP_ER("bchanlhmi_newbbsmenuwindow error", 0);
                goto error_bbsmenuwindow;
        }
-       err = bchanl_bbsmenu_initialize(&(bchanl->bbsmenu), bchanl->bbsmenuwindow.gid, subjecthash);
+       gid = bbsmenuwindow_getGID(bbsmenuwindow);
+       registerexternalwindow = bchanlhmi_newregisterexternalwindow(hmi, &p0, 0, NULL, bgpat);
+       if (registerexternalwindow == NULL) {
+               DP_ER("bchanlhmi_newregisterexternalwindow error", 0);
+               goto error_registerexternalwindow;
+       }
+       externalbbswindow = bchanlhmi_newexternalbbswindow(hmi, &r2, 0, NULL, NULL);
+       if (externalbbswindow == NULL) {
+               DP_ER("bchanlhmi_newexternalbbswindow", 0);
+               goto error_externalbbswindow;
+       }
+       err = bchanl_bbsmenu_initialize(&(bchanl->bbsmenu), gid, subjecthash, storage, connector);
        if (err < 0) {
                DP_ER("bchanl_bbsmenu_initialize error", err);
                goto error_bbsmenu;
        }
-       err = dget_dtp(8, BCHANL_DBX_MENU_TEST, (void**)&mnitem_dbx);
+       err = bchanl_mainmenu_initialize(&(bchanl->mainmenu), BCHANL_DBX_MENU_TEST);
        if (err < 0) {
-               DP_ER("dget_dtp error %d", err);
-               goto error_dget_dtp;
-       }
-       len = dget_siz((B*)mnitem_dbx);
-       mnitem = malloc(len);
-       if (mnitem == NULL) {
-               DP_ER("malloc error", 0);
-               goto error_mnitem;
-       }
-       memcpy(mnitem, mnitem_dbx, len);
-       mnid = mcre_men(BCHANL_MENU_WINDOW+2, mnitem, NULL);
-       if (mnid < 0) {
-               DP_ER("mcre_men error", mnid);
-               goto error_mcre_men;
+               DP_ER("bchanl_mainmenu_initialize %d", err);
+               goto error_mainmenu;
        }
 
        bchanl_hmistate_initialize(&bchanl->hmistate);
 
        if (exectype == EXECREQ) {
-               osta_prc(vid, bchanl->bbsmenuwindow.wid);
+               wid = bbsmenuwindow_getWID(bbsmenuwindow);
+               osta_prc(vid, wid);
        }
 
-       wget_wrk(bchanl->bbsmenuwindow.wid, &w_work);
+       bbsmenuwindow_getworkrect(bbsmenuwindow, &w_work);
        bbsmndraw_setviewrect(bchanl->bbsmenu.draw, 0, 0, w_work.c.right, w_work.c.bottom);
-       commonwindow_setworkrect(bchanl->bbsmenuwindow.window, 0, 0, w_work.c.right, w_work.c.bottom);
-
-       bchanl->bbsmenuwindow.draw = bchanl->bbsmenu.draw;
+       bbsmenuwindow_setworkrect(bbsmenuwindow, 0, 0, w_work.c.right, w_work.c.bottom);
 
+       bchanl->connector = connector;
        bchanl->retriever = retriever;
        bchanl->subjecthash = subjecthash;
 
-       bchanl->testdata.cache = NULL;
-       bchanl->testdata.parser = NULL;
-       bchanl->testdata.layout = NULL;
-       bchanl->testdata.draw = NULL;
+       bchanl->currentsubject = NULL;
+       bchanl->nextsubject = NULL;
+       bchanl->subjectdisplay.resnum = True;
+       bchanl->subjectdisplay.since = False;
+       bchanl->subjectdisplay.vigor = False;
 
-       bchanl->mnitem = mnitem;
-       bchanl->mnid = mnid;
        bchanl->vid = vid;
        bchanl->exectype = exectype;
 
+       bchanl->hmi = hmi;
+       bchanl->subjectwindow = subjectwindow;
+       bchanl->bbsmenuwindow = bbsmenuwindow;
+       bchanl->subjectoptionwindow = subjectoptionwindow;
+       bchanl->registerexternalwindow = registerexternalwindow;
+       bchanl->externalbbswindow = externalbbswindow;
+
        return 0;
 
-error_mcre_men:
-       free(mnitem);
-error_mnitem:
-error_dget_dtp:
+error_mainmenu:
+       //bchanl_bbsmenu_finalize(&(bchanl->bbsmenu));
 error_bbsmenu:
-       bchanl_bbsmenuwindow_finalize(&(bchanl->bbsmenuwindow));
+       bchanlhmi_deleteexternalbbswindow(hmi, externalbbswindow);
+error_externalbbswindow:
+       bchanlhmi_deleteregisterexternalwindow(hmi, registerexternalwindow);
+error_registerexternalwindow:
+       bchanlhmi_deletebbsmenuwindow(hmi, bbsmenuwindow);
 error_bbsmenuwindow:
+       bchanlhmi_deletesubjectoptionwindow(hmi, subjectoptionwindow);
+error_subjectoptionwindow:
        bchanl_subjecthash_delete(subjecthash);
 error_subjecthash:
-       bchanl_subjectwindow_finalize(&(bchanl->subjectwindow));
+       bchanlhmi_deletesubjectwindow(hmi, subjectwindow);
 error_subjectwindow:
+       bchanlhmi_delete(hmi);
+error_bchanlhmi:
        sbjtretriever_delete(retriever);
 error_retriever:
+       http_connector_delete(connector);
+error_http_connector:
        return -1; /* TODO */
 }
 
@@ -828,74 +1394,25 @@ LOCAL VOID bchanl_killme(bchanl_t *bchanl)
        gset_ptr(PS_BUSY, NULL, -1, -1);
        pdsp_msg(NULL);
 
-       if (bchanl->testdata.draw != NULL) {
-               sbjtdraw_delete(bchanl->testdata.draw);
-       }
-       if (bchanl->testdata.layout != NULL) {
-               sbjtlayout_delete(bchanl->testdata.layout);
-       }
-       if (bchanl->testdata.parser != NULL) {
-               sbjtparser_delete(bchanl->testdata.parser);
-       }
-       if (bchanl->testdata.cache != NULL) {
-               sbjtcache_delete(bchanl->testdata.cache);
-       }
-
+       extbbslist_writefile(bchanl->bbsmenu.extbbslist);
        if (bchanl->exectype == EXECREQ) {
                oend_prc(bchanl->vid, NULL, 0);
        }
-       mdel_men(bchanl->mnid);
-       free(bchanl->mnitem);
-       bchanl_bbsmenuwindow_finalize(&(bchanl->bbsmenuwindow));
+       bchanl_mainmenu_finalize(&bchanl->mainmenu);
+       bchanlhmi_deleteexternalbbswindow(bchanl->hmi, bchanl->externalbbswindow);
+       bchanlhmi_deleteregisterexternalwindow(bchanl->hmi, bchanl->registerexternalwindow);
+       bchanlhmi_deletebbsmenuwindow(bchanl->hmi, bchanl->bbsmenuwindow);
+       bchanlhmi_deletesubjectoptionwindow(bchanl->hmi, bchanl->subjectoptionwindow);
        bchanl_subjecthash_delete(bchanl->subjecthash);
-       bchanl_subjectwindow_finalize(&(bchanl->subjectwindow));
+       bchanlhmi_deletesubjectwindow(bchanl->hmi, bchanl->subjectwindow);
+       bchanlhmi_delete(bchanl->hmi);
        sbjtretriever_delete(bchanl->retriever);
+       http_connector_delete(bchanl->connector);
 
        ext_prc(0);
 }
 
-LOCAL VOID bchanl_sendsubjectrequest(bchanl_t *bchanl, bchanl_subject_t *subject)
-{
-       sbjtcache_t *cache;
-       sbjtlayout_t *layout;
-       sbjtdraw_t *draw;
-       TC *title;
-       RECT w_work;
-       W l, t, r, b, title_len, err;
-
-       bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_BUSY);
-       pdsp_msg(bchanl->hmistate.msg_retr_subject);
-
-       cache = bchanl_subject_getcache(subject);
-       err = sbjtretriever_sendrequest(bchanl->retriever, cache);
-       if (err < 0) {
-               pdsp_msg(bchanl->hmistate.msg_error_retr);
-               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
-               return;
-       }
-
-       pdsp_msg(NULL);
-       bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
-
-       bchanl_subject_relayout(subject);
-
-       bchanl_subjectwindow_setsubject(&bchanl->subjectwindow, subject);
-
-       wget_wrk(bchanl->subjectwindow.wid, &w_work);
-       draw = bchanl_subject_getdraw(subject);
-       sbjtdraw_setviewrect(draw, 0, 0, w_work.c.right, w_work.c.bottom);
-       commonwindow_setworkrect(bchanl->subjectwindow.window, 0, 0, w_work.c.right, w_work.c.bottom);
-
-       layout = bchanl_subject_getlayout(subject);
-       sbjtlayout_getdrawrect(layout, &l, &t, &r, &b);
-       commonwindow_setdrawrect(bchanl->subjectwindow.window, l, t, r, b);
-
-       bchanl_subject_gettitle(subject, &title, &title_len);
-       wset_tit(bchanl->subjectwindow.wid, -1, title, 0);
-}
-
-
-LOCAL VOID bchanl_readbbsmenutestdata(bchanl_bbsmenu_t *bchanl, bchanl_bbsmenuwindow_t *bchanl_window)
+LOCAL VOID bchanl_readbbsmenutestdata(bchanl_bbsmenu_t *bchanl, bbsmenuwindow_t *bchanl_window)
 {
        TC fname[] = {TK_b, TK_b, TK_s, TK_m, TK_e, TK_n, TK_u, TK_PROD, TK_h, TK_t, TK_m, TK_l, TNULL};
        LINK lnk;
@@ -903,6 +1420,7 @@ LOCAL VOID bchanl_readbbsmenutestdata(bchanl_bbsmenu_t *bchanl, bchanl_bbsmenuwi
        UB *bin;
        RECT w_work;
        bbsmncache_t *cache = bchanl->cache;
+       bbsmndraw_t *draw = bchanl->draw;
 
        err = get_lnk(fname, &lnk, F_NORM);
        if (err < 0) {
@@ -936,282 +1454,719 @@ LOCAL VOID bchanl_readbbsmenutestdata(bchanl_bbsmenu_t *bchanl, bchanl_bbsmenuwi
 
        req_tmg(0, BCHANL_MESSAGE_RETRIEVER_RELAYOUT);
 
-       wget_wrk(bchanl_window->wid, &w_work);
-       bbsmndraw_setviewrect(bchanl_window->draw, 0, 0, w_work.c.right, w_work.c.bottom);
-       commonwindow_setworkrect(bchanl_window->window, 0, 0, w_work.c.right, w_work.c.bottom);
-}
-
-LOCAL W bchanl_readsubjecttestdata(bchanl_t *bchanl, TC *fname)
-{
-       W fd, len, err, l, t, r, b;
-       LINK lnk;
-       UB *bin;
-       RECT w_work;
-       COLOR color;
-       FSSPEC fspec;
-       sbjtcache_t *cache = bchanl->testdata.cache;
-       sbjtparser_t *parser = bchanl->testdata.parser;
-       sbjtparser_thread_t *item;
-       sbjtlayout_t *layout = bchanl->testdata.layout;
-       sbjtdraw_t *draw = bchanl->testdata.draw;
-
-       cache = sbjtcache_new();
-       if (cache == NULL) {
-               return -1;
-       }
-       bchanl->testdata.cache = cache;
-       parser = sbjtparser_new(cache);
-       if (parser == NULL) {
-               return -1;
-       }
-       bchanl->testdata.parser = parser;
-       layout = sbjtlayout_new(bchanl->subjectwindow.gid);
-       if (layout == NULL) {
-               return -1;
-       }
-       bchanl->testdata.layout = layout;
-       draw = sbjtdraw_new(layout);
-       if (draw == NULL) {
-               return -1;
-       }
-       bchanl->testdata.draw = draw;
-
-       err = wget_inf(WI_FSVOBJ, &fspec, sizeof(FSSPEC));
-       if (err >= 0) {
-               sbjtlayout_setfsspec(layout, &fspec);
-       }
-       err = wget_inf(WI_VOBJBGCOL, &color, sizeof(COLOR));
-       if (err >= 0) {
-               sbjtlayout_setvobjbgcol(layout, color);
-       }
-
-       err = get_lnk(fname, &lnk, F_NORM);
-       if (err < 0) {
-               DP_ER("error get_lnk", err);
-               return err;
-       }
-       fd = opn_fil(&lnk, F_READ, NULL);
-       if (fd < 0) {
-               return fd;
-       }
-       err = rea_rec(fd, 0, NULL, 0, &len, NULL);
-       if (err < 0) {
-               cls_fil(fd);
-               return err;
-       }
-       bin = malloc(len);
-       if (bin == NULL) {
-               cls_fil(fd);
-               return -1;
-       }
-       err = rea_rec(fd, 0, bin, len, 0, NULL);
-       if (err < 0) {
-               free(bin);
-               cls_fil(fd);
-               return err;
-       }
-       cls_fil(fd);
-
-       sbjtcache_appenddata(cache, bin, len);
-       free(bin);
-
-       for (;;) {
-               err = sbjtparser_getnextthread(parser, &item);
-               if (err != 1) {
-                       break;
-               }
-               if (item == NULL) {
-                       break;
-               }
-               err = sbjtlayout_appendthread(layout, item);
-               if (err < 0) {
-                       return err;
-               }
-       }
-
-       bchanl_subjectwindow_setdraw(&bchanl->subjectwindow, draw);
-
-       wget_wrk(bchanl->subjectwindow.wid, &w_work);
-       sbjtdraw_setviewrect(draw, 0, 0, w_work.c.right, w_work.c.bottom);
-       commonwindow_setworkrect(bchanl->subjectwindow.window, 0, 0, w_work.c.right, w_work.c.bottom);
-
-       sbjtlayout_getdrawrect(layout, &l, &t, &r, &b);
-       commonwindow_setdrawrect(bchanl->subjectwindow.window, l, t, r, b);
-
-       return 0;
+       bbsmenuwindow_getworkrect(bchanl_window, &w_work);
+       bbsmndraw_setviewrect(draw, 0, 0, w_work.c.right, w_work.c.bottom);
+       bbsmenuwindow_setworkrect(bchanl_window, 0, 0, w_work.c.right, w_work.c.bottom);
 }
 
-LOCAL VOID bchanl_subjectwindow_keydwn(bchanl_subjectwindow_t *window, UH keycode, TC ch)
+LOCAL VOID bchanl_subjectwindow_keydwn(bchanl_t *bchanl, UH keycode, TC ch, UW stat)
 {
        W l,t,r,b,l1,t1,r1,b1,scr;
        sbjtlayout_t *layout;
+       sbjtdraw_t *draw;
 
-       if (window->draw == NULL) {
+       if (bchanl->currentsubject == NULL) {
                return;
        }
+       draw = bchanl_subject_getdraw(bchanl->currentsubject);
 
        switch (ch) {
        case KC_CC_U:
-               sbjtdraw_getviewrect(window->draw, &l, &t, &r, &b);
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
                if (t < 16) {
                        scr = -t;
                } else {
                        scr = -16;
                }
-               commonwindow_scrollbyvalue(window->window, 0, scr);
+               subjectwindow_scrollbyvalue(bchanl->subjectwindow, 0, scr);
+               bchanl_subjectwindow_scroll(bchanl, 0, scr);
                break;
        case KC_CC_D:
-               sbjtdraw_getviewrect(window->draw, &l, &t, &r, &b);
-               layout = bchanl_subject_getlayout(window->sbjt);
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               layout = bchanl_subject_getlayout(bchanl->currentsubject);
                sbjtlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
                if (b + 16 > b1) {
                        scr = b1 - b;
                } else {
                        scr = 16;
                }
-               commonwindow_scrollbyvalue(window->window, 0, scr);
+               if (scr > 0) {
+                       subjectwindow_scrollbyvalue(bchanl->subjectwindow, 0, scr);
+                       bchanl_subjectwindow_scroll(bchanl, 0, scr);
+               }
                break;
        case KC_CC_R:
-               sbjtdraw_getviewrect(window->draw, &l, &t, &r, &b);
-               layout = bchanl_subject_getlayout(window->sbjt);
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               layout = bchanl_subject_getlayout(bchanl->currentsubject);
                sbjtlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
                if (r + 16 > r1) {
                        scr = r1 - r;
                } else {
                        scr = 16;
                }
-               commonwindow_scrollbyvalue(window->window, scr, 0);
+               if (scr > 0) {
+                       subjectwindow_scrollbyvalue(bchanl->subjectwindow, scr, 0);
+                       bchanl_subjectwindow_scroll(bchanl, scr, 0);
+               }
                break;
        case KC_CC_L:
-               sbjtdraw_getviewrect(window->draw, &l, &t, &r, &b);
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
                if (l < 16) {
                        scr = -l;
                } else {
                        scr = -16;
                }
-               commonwindow_scrollbyvalue(window->window, scr, 0);
+               subjectwindow_scrollbyvalue(bchanl->subjectwindow, scr, 0);
+               bchanl_subjectwindow_scroll(bchanl, scr, 0);
                break;
        case KC_PG_U:
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               if (t < (b - t)) {
+                       scr = -t;
+               } else {
+                       scr = - (b - t);
+               }
+               subjectwindow_scrollbyvalue(bchanl->subjectwindow, 0, scr);
+               bchanl_subjectwindow_scroll(bchanl, 0, scr);
+               break;
        case KC_PG_D:
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               layout = bchanl_subject_getlayout(bchanl->currentsubject);
+               sbjtlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
+               if (b + (b - t) > b1) {
+                       scr = b1 - b;
+               } else {
+                       scr = (b - t);
+               }
+               if (scr > 0) {
+                       subjectwindow_scrollbyvalue(bchanl->subjectwindow, 0, scr);
+                       bchanl_subjectwindow_scroll(bchanl, 0, scr);
+               }
+               break;
        case KC_PG_R:
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               layout = bchanl_subject_getlayout(bchanl->currentsubject);
+               sbjtlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
+               if (r + (r - l) > r1) {
+                       scr = r1 - r;
+               } else {
+                       scr = (r - l);
+               }
+               if (scr > 0) {
+                       subjectwindow_scrollbyvalue(bchanl->subjectwindow, scr, 0);
+                       bchanl_subjectwindow_scroll(bchanl, scr, 0);
+               }
+               break;
        case KC_PG_L:
-               /* area scroll */
+               sbjtdraw_getviewrect(draw, &l, &t, &r, &b);
+               if (l < (r - l)) {
+                       scr = -l;
+               } else {
+                       scr = - (r - l);
+               }
+               subjectwindow_scrollbyvalue(bchanl->subjectwindow, scr, 0);
+               bchanl_subjectwindow_scroll(bchanl, scr, 0);
+               break;
+       case TK_E: /* temporary */
+               if (stat & ES_CMD) {
+                       bchanl_killme(bchanl);
+               }
                break;
        }
 }
 
-LOCAL VOID bchanl_bbsmenuwindow_keydwn(bchanl_bbsmenuwindow_t *window, UH keycode, TC ch)
+LOCAL VOID bchanl_bbsmenuwindow_keydwn(bchanl_t *bchanl, UH keycode, TC ch, UW stat)
 {
        W l,t,r,b,l1,t1,r1,b1,scr;
+       bbsmndraw_t *draw = bchanl->bbsmenu.draw;
+       bbsmnlayout_t *layout = bchanl->bbsmenu.layout;
 
        switch (ch) {
        case KC_CC_U:
-               bbsmndraw_getviewrect(window->draw, &l, &t, &r, &b);
+               bbsmndraw_getviewrect(draw, &l, &t, &r, &b);
                if (t < 16) {
                        scr = -t;
                } else {
                        scr = -16;
                }
-               commonwindow_scrollbyvalue(window->window, 0, scr);
+               bbsmenuwindow_scrollbyvalue(bchanl->bbsmenuwindow, 0, scr);
+               bchanl_bbsmenuwindow_scroll(bchanl, 0, scr);
                break;
        case KC_CC_D:
-               bbsmndraw_getviewrect(window->draw, &l, &t, &r, &b);
-               bbsmnlayout_getdrawrect(window->owner->bbsmenu.layout, &l1, &t1, &r1, &b1);
+               bbsmndraw_getviewrect(draw, &l, &t, &r, &b);
+               bbsmnlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
                if (b + 16 > b1) {
                        scr = b1 - b;
                } else {
                        scr = 16;
                }
-               commonwindow_scrollbyvalue(window->window, 0, scr);
+               if (scr > 0) {
+                       bbsmenuwindow_scrollbyvalue(bchanl->bbsmenuwindow, 0, scr);
+                       bchanl_bbsmenuwindow_scroll(bchanl, 0, scr);
+               }
                break;
        case KC_CC_R:
        case KC_CC_L:
+               break;
        case KC_PG_U:
+               bbsmndraw_getviewrect(draw, &l, &t, &r, &b);
+               if (t < (b - t)) {
+                       scr = -t;
+               } else {
+                       scr = - (b - t);
+               }
+               bbsmenuwindow_scrollbyvalue(bchanl->bbsmenuwindow, 0, scr);
+               bchanl_bbsmenuwindow_scroll(bchanl, 0, scr);
+               break;
        case KC_PG_D:
+               bbsmndraw_getviewrect(draw, &l, &t, &r, &b);
+               bbsmnlayout_getdrawrect(layout, &l1, &t1, &r1, &b1);
+               if (b + (b - t) > b1) {
+                       scr = b1 - b;
+               } else {
+                       scr = (b - t);
+               }
+               if (scr > 0) {
+                       bbsmenuwindow_scrollbyvalue(bchanl->bbsmenuwindow, 0, scr);
+                       bchanl_bbsmenuwindow_scroll(bchanl, 0, scr);
+               }
+               break;
        case KC_PG_R:
        case KC_PG_L:
-               /* area scroll */
                break;
        case KC_PF5:
-               bchanl_networkrequest_bbsmenu(window->owner);
+               bchanl_networkrequest_bbsmenu(bchanl);
                break;
+       case TK_E: /* temporary */
+               if (stat & ES_CMD) {
+                       bchanl_killme(bchanl);
+               }
+               break;
+       }
+}
+
+
+LOCAL VOID bchanl_keydwn(bchanl_t *bchanl, UH keytop, TC ch, UW stat)
+{
+       Bool act;
+
+       act = subjectwindow_isactive(bchanl->subjectwindow);
+       if (act == True) {
+               bchanl_subjectwindow_keydwn(bchanl, keytop, ch, stat);
+               return;
+       }
+       act = bbsmenuwindow_isactive(bchanl->bbsmenuwindow);
+       if (act == True) {
+               bchanl_bbsmenuwindow_keydwn(bchanl, keytop, ch, stat);
+               return;
        }
 }
 
-LOCAL VOID bchanl_setupmenu(bchanl_t *bchanl)
+enum BCHANL_TEXTBOX_MENU_TYPE_ {
+       BCHANL_TEXTBOX_MENU_TYPE_NONE,
+       BCHANL_TEXTBOX_MENU_TYPE_FILTER,
+       BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE,
+       BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL,
+};
+typedef enum BCHANL_TEXTBOX_MENU_TYPE_ BCHANL_TEXTBOX_MENU_TYPE;
+
+LOCAL VOID bchanl_setupmenu(bchanl_t *bchanl, BCHANL_TEXTBOX_MENU_TYPE type)
 {
-       wget_dmn(&(bchanl->mnitem[BCHANL_MENU_WINDOW].ptr));
-       mset_itm(bchanl->mnid, BCHANL_MENU_WINDOW, bchanl->mnitem+BCHANL_MENU_WINDOW);
-       oget_men(0, NULL, &(bchanl->mnitem[BCHANL_MENU_WINDOW+1].ptr), NULL, NULL);
-       mset_itm(bchanl->mnid, BCHANL_MENU_WINDOW+1, bchanl->mnitem+BCHANL_MENU_WINDOW+1);
+       Bool isactive, isopen, isopen_extbbs, selected = False, fromtray, totray, trayempty;
+       W index, num;
+
+       isactive = subjectwindow_isactive(bchanl->subjectwindow);
+       isopen = subjectoptionwindow_isopen(bchanl->subjectoptionwindow);
+       isopen_extbbs = externalbbswindow_isopen(bchanl->externalbbswindow);
+       if (isopen_extbbs != False) {
+               index = extbbslist_editcontext_getselect(bchanl->bbsmenu.editctx);
+               if (index >= 0) {
+                       selected = True;
+               }
+       }
+       switch (type) {
+       case BCHANL_TEXTBOX_MENU_TYPE_NONE:
+       default:
+               fromtray = totray = False;
+               break;
+       case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+               trayempty = tray_isempty();
+               if (trayempty == False) {
+                       fromtray = True;
+               } else {
+                       fromtray = False;
+               }
+               num = subjectoptionwindow_cutfiltertext(bchanl->subjectoptionwindow, NULL, 0, False);
+               if (num > 0) {
+                       totray = True;
+               } else {
+                       totray = False;
+               }
+               break;
+       case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+               trayempty = tray_isempty();
+               if (trayempty == False) {
+                       fromtray = True;
+               } else {
+                       fromtray = False;
+               }
+               num = registerexternalwindow_cutboradnametext(bchanl->registerexternalwindow, NULL, 0, False);
+               if (num > 0) {
+                       totray = True;
+               } else {
+                       totray = False;
+               }
+               break;
+       case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+               trayempty = tray_isempty();
+               if (trayempty == False) {
+                       fromtray = True;
+               } else {
+                       fromtray = False;
+               }
+               num = registerexternalwindow_cuturltext(bchanl->registerexternalwindow, NULL, 0, False);
+               if (num > 0) {
+                       totray = True;
+               } else {
+                       totray = False;
+               }
+               break;
+       }
+
+       bchanl_mainmenu_setup(&bchanl->mainmenu, isactive, isopen, isopen_extbbs, selected, fromtray, totray, bchanl->subjectdisplay.resnum, bchanl->subjectdisplay.since, bchanl->subjectdisplay.vigor);
 }
 
-LOCAL VOID bchanl_selectmenu(bchanl_t *bchanl, W i)
+LOCAL VOID bchanl_selectmenu(bchanl_t *bchanl, W sel, BCHANL_TEXTBOX_MENU_TYPE type)
 {
-       switch(i >> 8) {
-       case 0: /* [½ªÎ»] */
+       Bool isopen;
+       RECT work;
+#define BCHANL_SELECTMENU_STRBUF_LENGTH 256
+       TC str[BCHANL_SELECTMENU_STRBUF_LENGTH];
+       W index, len = 0, l, t, r, b;
+       GID gid;
+
+       switch(sel) {
+       case BCHANL_MAINMENU_SELECT_CLOSE: /* [½ªÎ»] */
                bchanl_killme(bchanl);
                break;
-       case 1: /* [ɽ¼¨] */
-               switch(i & 0xff) {
-               case 1: /* [ºÆɽ¼¨] */
-                       wreq_dsp(bchanl->subjectwindow.wid);
-                       wreq_dsp(bchanl->bbsmenuwindow.wid);
+       case BCHANL_MAINMENU_SELECT_REDISPLAY: /* [ºÆɽ¼¨] */
+               subjectwindow_requestredisp(bchanl->subjectwindow);
+               bbsmenuwindow_requestredisp(bchanl->bbsmenuwindow);
+               break;
+       case BCHANL_MAINMENU_SELECT_BBSMENUFETCH: /* [ÈÄ°ìÍ÷ºÆ¼èÆÀ] */
+               bchanl_networkrequest_bbsmenu(bchanl);
+               break;
+       case BCHANL_MAINMENU_SELECT_SUBJECTOPTION: /* [¥¹¥ì°ìÍ÷ÀßÄê] */
+               isopen = subjectoptionwindow_isopen(bchanl->subjectoptionwindow);
+               if (isopen == False) {
+                       subjectoptionwindow_open(bchanl->subjectoptionwindow);
+               } else {
+                       subjectoptionwindow_close(bchanl->subjectoptionwindow);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EXTBBS_MANAGER: /* [³°ÉôÈĤÎÄɲÃ] */
+               isopen = externalbbswindow_isopen(bchanl->externalbbswindow);
+               if (isopen == False) {
+
+                       bchanl->bbsmenu.editctx = extbbslist_startedit(bchanl->bbsmenu.extbbslist);
+                       if (bchanl->bbsmenu.editctx == NULL) {
+                               break;
+                       }
+                       externalbbswindow_open(bchanl->externalbbswindow);
+                       externalbbswindow_getworkrect(bchanl->externalbbswindow, &work);
+                       extbbslist_editcontext_setviewrect(bchanl->bbsmenu.editctx, 0, 0, work.c.right - work.c.left, work.c.bottom - work.c.top);
+                       externalbbswindow_setworkrect(bchanl->externalbbswindow, 0, 0, work.c.right - work.c.left, work.c.bottom - work.c.top);
+                       gid = externalbbswindow_getGID(bchanl->externalbbswindow);
+                       extbbslist_editcontext_getdrawrect(bchanl->bbsmenu.editctx, gid, &l, &t, &r, &b);
+                       externalbbswindow_setdrawrect(bchanl->externalbbswindow, l, t, r, b);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EXTBBS_REGISTER:
+               isopen = registerexternalwindow_isopen(bchanl->registerexternalwindow);
+               if (isopen == False) {
+                       registerexternalwindow_open(bchanl->registerexternalwindow);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EXTBBS_UP:
+               isopen = externalbbswindow_isopen(bchanl->externalbbswindow);
+               if (isopen != False) {
+                       index = extbbslist_editcontext_getselect(bchanl->bbsmenu.editctx);
+                       if (index < 0) {
+                               break;
+                       }
+                       extbbslist_editcontext_swapitem(bchanl->bbsmenu.editctx, index-1, index);
+                       externalbbswindow_requestredisp(bchanl->externalbbswindow);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EXTBBS_DOWN:
+               isopen = externalbbswindow_isopen(bchanl->externalbbswindow);
+               if (isopen != False) {
+                       index = extbbslist_editcontext_getselect(bchanl->bbsmenu.editctx);
+                       if (index < 0) {
+                               break;
+                       }
+                       extbbslist_editcontext_swapitem(bchanl->bbsmenu.editctx, index, index+1);
+                       externalbbswindow_requestredisp(bchanl->externalbbswindow);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EXTBBS_DELETE:
+               isopen = externalbbswindow_isopen(bchanl->externalbbswindow);
+               if (isopen != False) {
+                       index = extbbslist_editcontext_getselect(bchanl->bbsmenu.editctx);
+                       if (index < 0) {
+                               break;
+                       }
+                       extbbslist_editcontext_deleteitem(bchanl->bbsmenu.editctx, index);
+                       gid = externalbbswindow_getGID(bchanl->externalbbswindow);
+                       extbbslist_editcontext_getdrawrect(bchanl->bbsmenu.editctx, gid, &l, &t, &r, &b);
+                       externalbbswindow_setdrawrect(bchanl->externalbbswindow, l, t, r, b);
+                       externalbbswindow_requestredisp(bchanl->externalbbswindow);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EDIT_COPY_TO_TRAY:
+               switch (type) {
+               case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+                       len = subjectoptionwindow_cutfiltertext(bchanl->subjectoptionwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, False);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+                       len = registerexternalwindow_cutboradnametext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, False);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+                       len = registerexternalwindow_cuturltext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, False);
+                       break;
+               default:
+                       break;
+               }
+               if (len > 0) {
+                       tray_pushstring(str, len);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EDIT_COPY_FROM_TRAY:
+               len = tray_popstring(str, BCHANL_SELECTMENU_STRBUF_LENGTH);
+               switch (type) {
+               case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+                       subjectoptionwindow_insertfiltertext(bchanl->subjectoptionwindow, str, len);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+                       registerexternalwindow_insertboradnametext(bchanl->registerexternalwindow, str, len);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+                       registerexternalwindow_inserturltext(bchanl->registerexternalwindow, str, len);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EDIT_MOVE_TO_TRAY:
+               switch (type) {
+               case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+                       len = subjectoptionwindow_cutfiltertext(bchanl->subjectoptionwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+                       len = registerexternalwindow_cutboradnametext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+                       len = registerexternalwindow_cuturltext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               default:
+                       break;
+               }
+               if (len > 0) {
+                       tray_pushstring(str, len);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_EDIT_MOVE_FROM_TRAY:
+               len = tray_popstring(str, BCHANL_SELECTMENU_STRBUF_LENGTH);
+               switch (type) {
+               case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+                       subjectoptionwindow_insertfiltertext(bchanl->subjectoptionwindow, str, len);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+                       registerexternalwindow_insertboradnametext(bchanl->registerexternalwindow, str, len);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+                       registerexternalwindow_inserturltext(bchanl->registerexternalwindow, str, len);
+                       break;
+               default:
                        break;
                }
+               tray_deletedata();
                break;
-       case 2: /* [Áàºî] */
-               switch(i & 0xff) {
-               case 1: /* [ÈÄ°ìÍ÷ºÆ¼èÆÀ] */
-                       bchanl_networkrequest_bbsmenu(bchanl);
+       case BCHANL_MAINMENU_SELECT_EDIT_DELETE:
+               switch (type) {
+               case BCHANL_TEXTBOX_MENU_TYPE_FILTER:
+                       subjectoptionwindow_cutfiltertext(bchanl->subjectoptionwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE:
+                       registerexternalwindow_cutboradnametext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               case BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL:
+                       registerexternalwindow_cuturltext(bchanl->registerexternalwindow, str, BCHANL_SELECTMENU_STRBUF_LENGTH, True);
+                       break;
+               default:
                        break;
                }
                break;
-       case BCHANL_MENU_WINDOW: /* [¥¦¥£¥ó¥É¥¦] */
-               wexe_dmn(i);
+       case BCHANL_MAINMENU_SELECT_DISPLAY_RESNUMBER:
+               bchanl_swapresnumberdisplay(bchanl);
+               if (bchanl->currentsubject != NULL) {
+                       bchanl_subject_setresnumberdisplay(bchanl->currentsubject, bchanl->subjectdisplay.resnum);
+                       bchanl_changedisplayattribute(bchanl);
+               }
+               break;
+       case BCHANL_MAINMENU_SELECT_DISPLAY_SINCE:
+               bchanl_swapsincedisplay(bchanl);
+               if (bchanl->currentsubject != NULL) {
+                       bchanl_subject_setsincedisplay(bchanl->currentsubject, bchanl->subjectdisplay.since);
+                       bchanl_changedisplayattribute(bchanl);
+               }
                break;
-       case BCHANL_MENU_WINDOW+1: /* [¾®Êª] */
-           oexe_apg(0, i);
+       case BCHANL_MAINMENU_SELECT_DISPLAY_VIGOR:
+               bchanl_swapvigordisplay(bchanl);
+               if (bchanl->currentsubject != NULL) {
+                       bchanl_subject_setvigordisplay(bchanl->currentsubject, bchanl->subjectdisplay.vigor);
+                       bchanl_changedisplayattribute(bchanl);
+               }
                break;
        }
        return;
 }
 
-LOCAL VOID bchanl_popupmenu(bchanl_t *bchanl, PNT pos)
+LOCAL VOID bchanl_popupmenu(bchanl_t *bchanl, PNT pos, BCHANL_TEXTBOX_MENU_TYPE type)
 {
-       W       i;
-
-       bchanl_setupmenu(bchanl);
+       W sel;
+       bchanl_setupmenu(bchanl, type);
        gset_ptr(PS_SELECT, NULL, -1, -1);
-       i = msel_men(bchanl->mnid, pos);
-       if (i > 0) {
-               bchanl_selectmenu(bchanl, i);
+       sel = bchanl_mainmenu_popup(&bchanl->mainmenu, pos);
+       if (sel > 0) {
+               bchanl_selectmenu(bchanl, sel, type);
        }
 }
 
-LOCAL VOID receive_message(bchanl_t *bchanl)
+LOCAL W bchanl_keyselect(bchanl_t *bchanl, TC keycode, BCHANL_TEXTBOX_MENU_TYPE type)
 {
-       MESSAGE msg;
-       W code, err;
-
-    err = rcv_msg(MM_ALL, &msg, sizeof(MESSAGE), WAIT|NOCLR);
-       if (err >= 0) {
-               if (msg.msg_type == MS_TMOUT) { /* should be use other type? */
-                       code = msg.msg_body.TMOUT.code;
-                       switch (code) {
-                       case BCHANL_MESSAGE_RETRIEVER_RELAYOUT:
-                               bchanl_bbsmenu_relayout(&bchanl->bbsmenu, &bchanl->bbsmenuwindow);
-                               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
-                               pdsp_msg(NULL);
-                               break;
-                       case BCHANL_MESSAGE_RETRIEVER_ERROR:
-                               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
-                               pdsp_msg(NULL);
+       W sel;
+       bchanl_setupmenu(bchanl, type);
+       sel = bchanl_mainmenu_keyselect(&bchanl->mainmenu, keycode);
+       if (sel > 0) {
+               bchanl_selectmenu(bchanl, sel, type);
+       }
+       return 0;
+}
+
+LOCAL VOID bchanl_handletimeout(bchanl_t *bchanl, W code)
+{
+       switch (code) {
+       case BCHANL_MESSAGE_RETRIEVER_RELAYOUT:
+               bchanl_bbsmenu_relayout(&bchanl->bbsmenu, bchanl->bbsmenuwindow);
+               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
+               pdsp_msg(NULL);
+               break;
+       case BCHANL_MESSAGE_RETRIEVER_ERROR:
+               bchanl_hmistate_updateptrstyle(&bchanl->hmistate, PS_SELECT);
+               pdsp_msg(NULL);
+               break;
+       case BCHANL_MESSAGE_HTTP_EVENT:
+               bchanl_handle_httpevent(bchanl);
+               break;
+       }
+}
+
+LOCAL VOID bchanl_eventdispatch(bchanl_t *bchanl)
+{
+       bchanlhmievent_t *evt;
+       W sel, err;
+       Bool close;
+
+       err = bchanlhmi_getevent(bchanl->hmi, &evt);
+       if (err < 0) {
+               return;
+       }
+
+       switch (evt->type) {
+       case BCHANLHMIEVENT_TYPE_COMMON_MOUSEMOVE:
+               break;
+       case BCHANLHMIEVENT_TYPE_COMMON_KEYDOWN:
+               if (evt->data.common_keydown.stat & ES_CMD) {   /*Ì¿Îᥭ¡¼*/
+                       bchanl_setupmenu(bchanl, BCHANL_TEXTBOX_MENU_TYPE_NONE);
+                       sel = bchanl_keyselect(bchanl, evt->data.common_keydown.keycode, BCHANL_TEXTBOX_MENU_TYPE_NONE);
+                       if (sel > 0) {
+                               bchanl_selectmenu(bchanl, sel, BCHANL_TEXTBOX_MENU_TYPE_NONE);
                                break;
                        }
                }
+               bchanl_keydwn(bchanl, evt->data.common_keydown.keytop, evt->data.common_keydown.keycode, evt->data.common_keydown.stat);
+               break;
+       case BCHANLHMIEVENT_TYPE_COMMON_MENU:
+               bchanl_popupmenu(bchanl, evt->data.common_menu.pos, BCHANL_TEXTBOX_MENU_TYPE_NONE);
+               break;
+       case BCHANLHMIEVENT_TYPE_COMMON_TIMEOUT:
+               bchanl_handletimeout(bchanl, evt->data.common_timeout.code);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_DRAW:
+               bchanl_subjectwindow_draw(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_RESIZE:
+               bchanl_subjectwindow_resize(bchanl, evt->data.subjectwindow_resize.work_sz);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_CLOSE:
+               bchanl_subjectwindow_close(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_BUTDN:
+               bchanl_subjectwindow_butdn(bchanl, evt->data.subjectwindow_butdn.type, evt->data.subjectwindow_butdn.pos);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_PASTE:
+               subjectwindow_responsepasterequest(bchanl->subjectwindow, /* NACK */ 1, NULL);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_MOUSEMOVE:
+               gset_ptr(bchanl->hmistate.ptr, NULL, -1, -1);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTWINDOW_SCROLL:
+               bchanl_subjectwindow_scroll(bchanl, evt->data.subjectwindow_scroll.dh, evt->data.subjectwindow_scroll.dv);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_DRAW:
+               bchanl_bbsmenuwindow_draw(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_RESIZE:
+               bchanl_bbsmenuwindow_resize(bchanl, evt->data.bbsmenuwindow_resize.work_sz);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_CLOSE:
+               bchanl_bbsmenuwindow_close(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_BUTDN:
+               bchanl_bbsmenuwindow_butdn(bchanl, evt->data.bbsmenuwindow_butdn.type, evt->data.bbsmenuwindow_butdn.pos);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_MOUSEMOVE:
+               gset_ptr(bchanl->hmistate.ptr, NULL, -1, -1);
+               break;
+       case BCHANLHMIEVENT_TYPE_BBSMENUWINDOW_SCROLL:
+               bchanl_bbsmenuwindow_scroll(bchanl, evt->data.bbsmenuwindow_scroll.dh, evt->data.bbsmenuwindow_scroll.dv);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_FILTER_DETERMINE:
+               bchanl_changesubjectfilterword(bchanl, evt->data.subjectoptionwindow_filter_determine.value, evt->data.subjectoptionwindow_filter_determine.len);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_FILTER_COPY:
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_FILTER_MOVE:
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_FILTER_MENU:
+               bchanl_popupmenu(bchanl, evt->data.subjectoptionwindow_filter_menu.pos, BCHANL_TEXTBOX_MENU_TYPE_FILTER);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_FILTER_KEYMENU:
+               bchanl_keyselect(bchanl, evt->data.subjectoptionwindow_filter_keymenu.keycode, BCHANL_TEXTBOX_MENU_TYPE_FILTER);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_ORDER_CHANGE:
+               bchanl_changesubjectorder(bchanl, evt->data.subjectoptionwindow_order_change.value);
+               break;
+       case BCHANLHMIEVENT_TYPE_SUBJECTOPTIONWINDOW_PARTS_ORDERBY_CHANGE:
+               bchanl_changesubjectorderby(bchanl, evt->data.subjectoptionwindow_order_change.value);
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_BORADNAME_DETERMINE:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_BORADNAME_COPY:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_BORADNAME_MOVE:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_BORADNAME_MENU:
+               bchanl_popupmenu(bchanl, evt->data.registerexternalwindow_boradname_menu.pos, BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE);
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_BORADNAME_KEYMENU:
+               bchanl_keyselect(bchanl, evt->data.registerexternalwindow_boradname_keymenu.keycode, BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_TITLE);
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_URL_DETERMINE:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_URL_COPY:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_URL_MOVE:
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_URL_MENU:
+               bchanl_popupmenu(bchanl, evt->data.registerexternalwindow_url_menu.pos, BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL);
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_URL_KEYMENU:
+               bchanl_keyselect(bchanl, evt->data.registerexternalwindow_url_keymenu.keycode, BCHANL_TEXTBOX_MENU_TYPE_EXTBBS_URL);
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_DETERMINE_PUSH:
+               close = bchanl_registerexternalbbs(bchanl);
+               if (close != False) {
+                       registerexternalwindow_close(bchanl->registerexternalwindow);
+               }
+               break;
+       case BCHANLHMIEVENT_TYPE_REGISTEREXTERNALWINDOW_PARTS_CANCEL_PUSH:
+               registerexternalwindow_close(bchanl->registerexternalwindow);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_DRAW:
+               bchanl_externalbbswindow_draw(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_RESIZE:
+               bchanl_externalbbswindow_resize(bchanl, evt->data.externalbbswindow_resize.work_sz);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_CLOSE:
+               bchanl_externalbbswindow_close(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_BUTDN:
+               bchanl_externalbbswindow_butdn(bchanl, evt->data.externalbbswindow_butdn.type, evt->data.externalbbswindow_butdn.pos);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_PASTE:
+               bchanl_externalbbswindow_paste(bchanl);
+               break;
+       case BCHANLHMIEVENT_TYPE_EXTERNALBBSWINDOW_SCROLL:
+               bchanl_externalbbswindow_scroll(bchanl, evt->data.externalbbswindow_scroll.dh, evt->data.externalbbswindow_scroll.dv);
+               break;
+       case BCHANLHMIEVENT_TYPE_NONE:
+       }
+}
+
+LOCAL TC filename_dbg_databox[] = (TC[]){TK_b, TK_c, TK_h, TK_a, TK_n, TK_l, TK_PROD, TK_d, TK_b, TK_x, TNULL};
+LOCAL TC filename_dbg_storage[] = (TC[]){TK_c, TK_o, TK_m, TK_m, TK_o, TK_n, TK_s, TK_t, TK_o, TK_r, TK_a, TK_g, TK_e, TK_2, TNULL};
+LOCAL TC filename_storage[] = (TC[]){TK_c, TK_o, TK_m, TK_m, TK_o, TK_n, TK_s, TK_t, TK_o, TK_r, TK_a, TK_g, TK_e, TNULL};
+
+LOCAL W main_CLI_args(VID *vid, LINK *storage)
+{
+       W err;
+       LINK dbx;
+
+       *vid = -1;
+       err = get_lnk(filename_dbg_databox, &dbx, F_NORM);
+       if (err < 0) {
+               DP_ER("get_lnk:test databox error", err);
+               return err;
+       }
+       err = dopn_dat(&dbx);
+       if (err < 0) {
+               DP_ER("dopn_dat error", err);
+               return err;
+       }
+       err = get_lnk(filename_dbg_storage, storage, F_NORM);
+       if (err < 0) {
+               DP_ER("get_lnk;commonstorage error", err);
+               return err;
+       }
+
+       return 0;
+}
+
+LOCAL W main_EXECREC_args(M_EXECREQ *msg, VID *vid, LINK *storage)
+{
+       W err;
+       LINK lnk;
+
+       err = dopn_dat(&msg->self);
+       if (err < 0) {
+               DP_ER("dopn_dat", err);
+               return err;
+       }
+
+       lnk = msg->self;
+       err = get_lnk(filename_storage, &lnk, F_BASED);
+       if (err < 0) {
+               DP_ER("get_lnk;commonstorage error", err);
+               return err;
        }
-       clr_msg(MM_ALL, MM_ALL);
+       *storage = lnk;
+
+       *vid = msg->vid;
+
+       return 0;
 }
 
 typedef struct _arg {
@@ -1268,13 +2223,11 @@ LOCAL    CLI_arg   MESSAGEtoargv(const MESSAGE *src)
 
 EXPORT W       MAIN(MESSAGE *msg)
 {
-       W       i, err;
-       WID wid, wid_bbsmenu, act;
+       W err;
        VID vid = -1;
+       LINK storage;
        CLI_arg arg;
-       LINK dbx;
        bchanl_t bchanl;
-       commonwindow_t *window, *window_bbsmenu;
 
        err = dopn_dat(NULL);
        if (err < 0) {
@@ -1285,14 +2238,8 @@ EXPORT   W       MAIN(MESSAGE *msg)
        switch (msg->msg_type) {
        case 0: /* CLI */
                arg = MESSAGEtoargv(msg);
-               err = get_lnk((TC[]){TK_b, TK_c, TK_h, TK_a, TK_n, TK_l, TK_PROD, TK_d, TK_b, TK_x,TNULL}, &dbx, F_NORM);
+               err = main_CLI_args(&vid, &storage);
                if (err < 0) {
-                       DP_ER("get_lnk:databox error", err);
-                       ext_prc(0);
-               }
-               err = dopn_dat(&dbx);
-               if (err < 0) {
-                       DP_ER("dopn_dat error", err);
                        ext_prc(0);
                }
                break;
@@ -1308,19 +2255,17 @@ EXPORT  W       MAIN(MESSAGE *msg)
                if ((((M_EXECREQ*)msg)->mode & 2) == 0) {
                        ext_prc(0);
                }
-               err = dopn_dat(&((M_EXECREQ*)msg)->self);
+               err = main_EXECREC_args((M_EXECREQ*)msg, &vid, &storage);
                if (err < 0) {
-                       DP_ER("dopn_dat error", err);
                        ext_prc(0);
                }
-               vid = ((M_EXECREQ*)msg)->vid;
                break;
        default:
                ext_prc(0);
                break;
        }
 
-       err = bchanl_initialize(&bchanl, vid, msg->msg_type);
+       err = bchanl_initialize(&bchanl, vid, msg->msg_type, &storage);
        if (err < 0) {
                DP_ER("bchanl_initialize error", err);
                ext_prc(0);
@@ -1331,100 +2276,18 @@ EXPORT W       MAIN(MESSAGE *msg)
                bchanl_killme(&bchanl);
                return err;
        }
-       window = bchanl.subjectwindow.window;
-       wid = bchanl.subjectwindow.wid;
-       window_bbsmenu = bchanl.bbsmenuwindow.window;
-       wid_bbsmenu = bchanl.bbsmenuwindow.wid;
 
        if (msg->msg_type == 0) {
-               bchanl_readbbsmenutestdata(&(bchanl.bbsmenu), &(bchanl.bbsmenuwindow));
-               if (arg.ac > 1) {
-                       err = bchanl_readsubjecttestdata(&bchanl, arg.argv[1]);
-                       if (err < 0) {
-                               DP_ER("bchanl_readsubjecttestdata error\n", err);
-                               bchanl_killme(&bchanl);
-                               return err;
-                       }
-               }
+               bchanl_readbbsmenutestdata(&(bchanl.bbsmenu), bchanl.bbsmenuwindow);
        } else if (msg->msg_type == EXECREQ) {
                bchanl_networkrequest_bbsmenu(&bchanl);
        }
 
-       wreq_dsp(bchanl.subjectwindow.wid);
-       wreq_dsp(bchanl.bbsmenuwindow.wid);
+       subjectwindow_requestredisp(bchanl.subjectwindow);
+       bbsmenuwindow_requestredisp(bchanl.bbsmenuwindow);
 
        for (;;) {
-               wget_evt(&wev0, WAIT);
-               switch (wev0.s.type) {
-                       case    EV_NULL:
-                               if ((wev0.s.wid != wid)&&(wev0.g.wid != wid_bbsmenu)) {
-                                       gset_ptr(bchanl.hmistate.ptr, NULL, -1, -1);
-                                       break;          /*¥¦¥£¥ó¥É¥¦³°*/
-                               }
-                               if (wev0.s.cmd != W_WORK)
-                                       break;          /*ºî¶ÈÎΰ賰*/
-                               if (wev0.s.stat & ES_CMD)
-                                       break;  /*Ì¿Îᥭ¡¼¤¬²¡¤µ¤ì¤Æ¤¤¤ë*/
-                               gset_ptr(bchanl.hmistate.ptr, NULL, -1, -1);
-                               break;
-                       case    EV_REQUEST:
-                               if (wev0.g.wid == wid) {
-                                       commonwindow_weventrequest(window, &wev0);
-                               } else if (wev0.g.wid == wid_bbsmenu) {
-                                       commonwindow_weventrequest(window_bbsmenu, &wev0);
-                               }
-                               break;
-                       case    EV_RSWITCH:
-                               if (wev0.s.wid == wid) {
-                                       commonwindow_weventreswitch(window, &wev0);
-                               } else if (wev0.g.wid == wid_bbsmenu) {
-                                       commonwindow_weventreswitch(window_bbsmenu, &wev0);
-                               }
-                               break;
-                       case    EV_SWITCH:
-                               if (wev0.s.wid == wid) {
-                                       commonwindow_weventswitch(window, &wev0);
-                               } else if (wev0.g.wid == wid_bbsmenu) {
-                                       commonwindow_weventswitch(window_bbsmenu, &wev0);
-                               }
-                               break;
-                       case    EV_BUTDWN:
-                               if (wev0.g.wid == wid) {
-                                       commonwindow_weventbutdn(window, &wev0);
-                               } else if (wev0.g.wid == wid_bbsmenu) {
-                                       commonwindow_weventbutdn(window_bbsmenu, &wev0);
-                               }
-                               break;
-                       case    EV_KEYDWN:
-                               if (wev0.s.stat & ES_CMD) {
-                                       bchanl_setupmenu(&bchanl);
-                                       i = mfnd_key(bchanl.mnid, wev0.e.data.key.code);
-                                       if (i >= 0) {
-                                               bchanl_selectmenu(&bchanl, i);
-                                               break;
-                                       }
-                               }
-                       case    EV_AUTKEY:
-                               act = wget_act(NULL);
-                               if (act == wid) {
-                                       bchanl_subjectwindow_keydwn(&bchanl.subjectwindow, wev0.e.data.key.keytop, wev0.e.data.key.code);
-                               } else if (act == wid_bbsmenu) {
-                                       bchanl_bbsmenuwindow_keydwn(&bchanl.bbsmenuwindow, wev0.e.data.key.keytop, wev0.e.data.key.code);
-                               }
-                               break;
-                       case    EV_INACT:
-                               pdsp_msg(NULL);
-                               break;
-                       case    EV_DEVICE:
-                               oprc_dev(&wev0.e, NULL, 0);
-                               break;
-                       case    EV_MSG:
-                               receive_message(&bchanl);
-                               break;
-                       case    EV_MENU:
-                               bchanl_popupmenu(&bchanl, wev0.s.pos);
-                               break;
-               }
+               bchanl_eventdispatch(&bchanl);
        }
 
        return 0;