diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-04-15 01:16:13 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-04-15 01:16:13 +0800 |
commit | a942aaef1cdde595f482a853e6a1291c04b9d2c7 (patch) | |
tree | 73c7992b234f85bd7440a33ccf2a6c610cdff42c /mbbsd/visio.c | |
parent | a2f6c27927c37f89b888f9e4c1afe96049bdf406 (diff) | |
download | pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar.gz pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar.bz2 pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar.lz pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar.xz pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.tar.zst pttbbs-a942aaef1cdde595f482a853e6a1291c04b9d2c7.zip |
- (internal) code refine - improving visio system
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4164 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/visio.c')
-rw-r--r-- | mbbsd/visio.c | 346 |
1 files changed, 331 insertions, 15 deletions
diff --git a/mbbsd/visio.c b/mbbsd/visio.c index 789b6e60..59a69c2e 100644 --- a/mbbsd/visio.c +++ b/mbbsd/visio.c @@ -3,12 +3,17 @@ /* * visio.c - * High-level virtual screen input output control + * High-Level virtual screen input output control + * + * Author: piaip, 2008 * * This is not the original visio.c from maple3. - * m3 visio = (ptt) visio+screen. + * We just borrowed its file name and then + * re-implemented everything :) + * + * m3 visio = (ptt) visio+screen/term. + * * This visio contains only high level UI element/widgets. - * In fact the only APIs from m3 are vmsg/vget... * * To add API here, please... * (1) name the API in prefix of 'v'. @@ -20,6 +25,7 @@ // ---- DEFINITION --------------------------------------------------- #define MAX_COL (t_columns-1) #define SAFE_MAX_COL (MAX_COL-1) +#define VBUFLEN (ANSILINELEN) // ---- UTILITIES ---------------------------------------------------- inline void @@ -35,17 +41,251 @@ nblank(int n) outnc(n, ' '); } +// this is a special strlen to speed up processing. +// warning: x MUST be #define x "msg". +// otherwise you need to use real strlen. +#define MACROSTRLEN(x) (sizeof(x)-1) + +// ---- VSOREF API -------------------------------------------------- + +/** + * vscr_save(): 傳回目前畫面的備份物件。 + */ +VSOREF +vscr_save(void) +{ + // TODO optimize memory allocation someday. + screen_backup_t *o = (screen_backup_t*)malloc(sizeof(screen_backup_t)); + assert(o); + scr_dump(o); + return o; +} + +/** + * vscr_restore(obj): 使用並刪除畫面的備份物件。 + */ +void +vscr_restore(VSOREF obj) +{ + screen_backup_t *o = (screen_backup_t*)o; + if (o) + { + scr_restore(o); + memset(o, 0, sizeof(screen_backup_t)); + free(o); + } +} + +/** + * vcur_save(): 傳回目前游標的備份物件。 + */ +VSOREF +vcur_save(void) +{ + // XXX 偷懶不 new object 了, pointer 夠大 + int y, x; + getyx(&y, &x); + y = ((unsigned short)y << 16) | (unsigned short)x; + return (VSOREF)NULL + y; +} + +/** + * vcur_restore(obj): 使用並刪除游標的備份物件。 + */ +void +vcur_restore(VSOREF o) +{ + int y, x; + y = (unsigned int)(o - NULL); + x = (unsigned short)(y & 0xFFFF); + y = (unsigned short)(y >> 16); + move(y, x); +} + +// ---- LOW LEVEL API ----------------------------------------------- + +/** + * vpad(n, pattern): 填滿 n 個字元 (使用的格式為 pattern) + * + * @param n 要填滿的字元數 (無法填滿時會使用空白填補) + * @param pattern 填充用的字串 + */ +inline void +vpad(int n, const char *pattern) +{ + int len = strlen(pattern); + // assert(len > 0); + + while (n >= len) + { + outs(pattern); + n -= len; + } + if (n) nblank(n); +} + // ---- HIGH LEVEL API ----------------------------------------------- /** + * vshowmsg(s): 在底部印出指定訊息或單純的暫停訊息 + * + * @param s 指定訊息。 NULL: 任意鍵繼續。 s: 若有 \t 則後面字串靠右 (若無則顯示任意鍵) + */ +void +vshowmsg(const char *msg) +{ + int w = SAFE_MAX_COL; + move(b_lines, 0); clrtoeol(); + + if (!msg) + { + // print default message in middle + outs(VCLR_PAUSE_PAD); + outc(' '); // initial one space + + // VMSG_PAUSE MUST BE A #define STRING. + w -= MACROSTRLEN(VMSG_PAUSE); // strlen_noansi(VMSG_PAUSE); + w--; // initial space + vpad(w/2, VMSG_PAUSE_PAD); + outs(VCLR_PAUSE); + outs(VMSG_PAUSE); + outs(VCLR_PAUSE_PAD); + vpad(w - w/2, VMSG_PAUSE_PAD); + } else { + // print in left, with floating (if \t exists) + char *pfloat = strchr(msg, '\t'); + int szfloat = 0; + int nmsg = 0; + + // print prefix + w -= MACROSTRLEN(VMSG_MSG_PREFIX); // strlen_noansi(VMSG_MSG_PREFIX); + outs(VCLR_MSG); + outs(VMSG_MSG_PREFIX); + + // if have float, calculate float size + if (pfloat) { + nmsg = pfloat - msg; + w -= strlen_noansi(msg) -1; // -1 for \t + pfloat ++; // skip \t + szfloat = strlen_noansi(pfloat); + } else { + pfloat = VMSG_MSG_FLOAT; + szfloat = MACROSTRLEN(VMSG_MSG_FLOAT); // strlen_noansi() + w -= strlen_noansi(msg) + szfloat; + } + + // calculate if we can display float + if (w < 0) + { + w += szfloat; + szfloat = 0; + } + + // print msg body + if (nmsg) + outns(msg, nmsg); + else + outs(msg); + + // print padding for floats + if (w > 0) + nblank(w); + + // able to print float? + if (szfloat) + { + outs(VCLR_MSG_FLOAT); + outs(pfloat); + } + } + + // safe blank + outs(" " ANSI_RESET); +} + +/** + * vans(s): 在底部印出訊息與小輸入欄,並傳回使用者的輸入(轉為小寫)。 + * + * @param s 指定訊息,見 vshowmsg + */ +int +vans(const char *msg) +{ + char buf[3]; + + // move(b_lines, 0); clrtoeol(); + vget(b_lines, 0, msg, buf, sizeof(buf), LCECHO); + return (unsigned char)buf[0]; +} + +/** + * vansf(s, ...): 在底部印出訊息與小輸入欄,並傳回使用者的輸入(轉為小寫)。 + * + * @param s 指定訊息,見 vshowmsg + */ +int +vansf(const char *fmt, ...) +{ + char msg[VBUFLEN]; + va_list ap; + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + return vans(msg); +} + +/** + * vmsg(s): 在底部印出指定訊息或單純的暫停訊息,並傳回使用者的按鍵。 + * + * @param s 指定訊息,見 vshowmsg + */ +int +vmsg(const char *msg) +{ + int i = 0; + + vshowmsg(msg); + + // wait for key + do { + i = igetch(); + } while( i == 0 ); + + // clear message bar + move(b_lines, 0); + clrtoeol(); + + return i; +} + + +/** + * vmsgf(s, ...): 格式化輸出暫停訊息並呼叫 vmsg)。 + * + * @param s 格式化的訊息 + */ +int +vmsgf(const char *fmt,...) +{ + char msg[VBUFLEN]; + va_list ap; + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + return vmsg(msg); +} + +/** * vbarf(s, ...): 格式化輸出左右對齊的字串 (MAX_COL) * - * s 裡 \t 後的內容會對齊右端。 + * @param s 格式化字串 (\t 後的內容會對齊右端) */ void vbarf(const char *s, ...) { - char msg[512], *s2; + char msg[VBUFLEN], *s2; va_list ap; va_start(ap, s); vsnprintf(msg, sizeof(msg), s, ap); @@ -55,46 +295,120 @@ vbarf(const char *s, ...) if (s2) *s2++ = 0; else s2 = ""; - return vbar(msg, s2); + return vbarlr(msg, s2); } /** - * vbar(l, r): 左右對齊畫滿螢幕 (MAX_COL) - * - * 注意 r 的後面不會補空白。 - * l r 都可以含 ANSI 控制碼。 + * vbarlr(l, r): 左右對齊畫滿螢幕 (MAX_COL) * 注意: 目前的實作自動認定游標已在行開頭。 + * + * @param l 靠左對齊的字串 (可含 ANSI 碼) + * @param r 靠右對齊的字串 (可含 ANSI 碼,後面不會補空白) */ void -vbar(const char *l, const char *r) +vbarlr(const char *l, const char *r) { // TODO strlen_noansi 跑兩次... 其實 l 可以邊 output 邊算。 int szl = strlen_noansi(l), szr = strlen_noansi(r); + // assume we are already in (y, 0) clrtoeol(); outs(l); szl = MAX_COL - szl; + if (szl > szr) { nblank(szl - szr); szl = szr; } + if (szl == szr) outs(r); else if (szl > 0) nblank(szl); + outs(ANSI_RESET); } +// ---- THEMED FORMATTING OUTPUT ------------------------------------- + +/** + * vs_header(title, mid, right): 清空螢幕並輸出完整標題 (MAX_COL) + * + * @param title: 靠左的主要標題,不會被切斷。 + * @param mid: 置中說明,可被切齊。 + * @param right: 靠右的說明,空間夠才會顯示。 + */ +void +vs_header(const char *title, const char *mid, const char *right) +{ + int w = MAX_COL; + int szmid = mid ? strlen(mid) : 0; + int szright = right ? strlen(right) : 0; + + clear(); + outs(VCLR_HEADER); + + if (title) + { + outs(VMSG_HDR_PREFIX); + outs(title); + outs(VMSG_HDR_POSTFIX); + w -= MACROSTRLEN(VMSG_HDR_PREFIX) + MACROSTRLEN(VMSG_HDR_POSTFIX); + w -= strlen(title); + } + + // determine if we can display right message, and + // if if we need to truncate mid. + if (szmid + szright > w) + szright = 0; + + if (szmid >= w) + szmid = w; + else { + int l = (MAX_COL-szmid)/2; + l -= (MAX_COL-w); + if (l > 0) + nblank(l), w -= l; + } + + if (szmid) { + outs(VCLR_HEADER_MID); + outns(mid, szmid); + outs(VCLR_HEADER); + } + nblank(w - szmid); + + if (szright) { + outs(VCLR_HEADER_RIGHT); + outs(right); + } + outs(ANSI_RESET "\n"); +} + /** - * vfooter(caption, msg): 在螢幕底部印出格式化的 caption msg + * vs_hdr(title): 清空螢幕並輸出簡易的標題 * - * msg 中若有 () 則會變色, \t 後的文字會靠右。 - * 最後面會自動留一個空白 (以避免自動偵測中文字的問題)。 + * @param title + */ +void +vs_hdr(const char *title) +{ + clear(); + outs(VCLR_HDR VMSG_HDR_PREFIX); + outs(title); + outs(VMSG_HDR_POSTFIX ANSI_RESET "\n"); +} + +/** + * vs_footer(caption, msg): 在螢幕底部印出格式化的 caption msg (不可含 ANSI 碼) + * + * @param caption 左邊的分類字串 + * @param msg 訊息字串, \t 後文字靠右、最後面會自動留一個空白。 */ void -vfooter(const char *caption, const char *msg) +vs_footer(const char *caption, const char *msg) { int i = 0; move(b_lines, 0); clrtoeol(); @@ -111,7 +425,9 @@ vfooter(const char *caption, const char *msg) while (*msg && i < SAFE_MAX_COL) { if (*msg == '(') + { outs(VCLR_FOOTER_QUOTE); + } else if (*msg == '\t') { // if we don't have enough space, ignore whole. |