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 | |
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
-rw-r--r-- | include/config.h | 4 | ||||
-rw-r--r-- | include/proto.h | 20 | ||||
-rw-r--r-- | include/visio.h | 48 | ||||
-rw-r--r-- | mbbsd/admin.c | 2 | ||||
-rw-r--r-- | mbbsd/angel.c | 29 | ||||
-rw-r--r-- | mbbsd/announce.c | 6 | ||||
-rw-r--r-- | mbbsd/board.c | 2 | ||||
-rw-r--r-- | mbbsd/mail.c | 2 | ||||
-rw-r--r-- | mbbsd/pmore.c | 13 | ||||
-rw-r--r-- | mbbsd/read.c | 4 | ||||
-rw-r--r-- | mbbsd/register.c | 6 | ||||
-rw-r--r-- | mbbsd/stuff.c | 154 | ||||
-rw-r--r-- | mbbsd/talk.c | 9 | ||||
-rw-r--r-- | mbbsd/term.c | 7 | ||||
-rw-r--r-- | mbbsd/visio.c | 346 |
15 files changed, 467 insertions, 185 deletions
diff --git a/include/config.h b/include/config.h index aec5b3df..76b393a3 100644 --- a/include/config.h +++ b/include/config.h @@ -155,10 +155,6 @@ #define THREAD_SEARCH_RANGE (500) #endif -#ifndef HAVE_JCEE /* 大學聯考查榜系統 */ -#define HAVE_JCEE 0 -#endif - #ifndef MEM_CHECK #define MEM_CHECK 0x98761234 #endif diff --git a/include/proto.h b/include/proto.h index a223ac78..bdea4f75 100644 --- a/include/proto.h +++ b/include/proto.h @@ -46,6 +46,12 @@ void CallAngel(void); void angel_toggle_pause(); void angel_load_my_fullnick(char *buf, int szbuf); // full nick! const char *angel_get_nick(); +void pressanykey_or_callangel(void); +#ifdef PLAY_ANGEL +#define PRESSANYKEY() pressanykey_or_callangel() +#else // !PLAY_ANGEL +#define PRESSANYKEY() pressanykey() +#endif /* announce */ int a_menu(const char *maintitle, const char *path, int lastlevel, int lastbid, char *trans_buffer); @@ -598,13 +604,9 @@ int search_aidu(char *bfile, aidu_t aidu); int log_user(const char *fmt, ...) GCC_CHECK_FORMAT(1,2); time4_t gettime(int line, time4_t dt, const char* head); void setcalfile(char *buf, char *userid); -void stand_title(const char *title); char getans(const char *fmt,...) GCC_CHECK_FORMAT(1,2); -int getkey(const char *fmt,...) GCC_CHECK_FORMAT(1,2); -int vmsgf(const char *fmt,...) GCC_CHECK_FORMAT(1,2); -int vmsg(const char *msg); -int show_file(const char *filename, int y, int lines, int mode); -void bell(void); +int getkey(const char *fmt,...) GCC_CHECK_FORMAT(1,2); +int show_file(const char *filename, int y, int lines, int mode); int cursor_key(int row, int column); int search_num(int ch, int max); int is_BM(const char *list); @@ -628,9 +630,6 @@ int is_uBM(const char *list, const char *id); void *MALLOC(int size); void FREE(void *ptr); #endif -#ifdef PLAY_ANGEL -void pressanykey_or_callangel(void); -#endif void syncnow(void); /* syspost */ @@ -681,8 +680,9 @@ int j_ticket_main(void); /* term */ void init_tty(void); -int term_init(void); +int term_init(void); void term_resize(int w, int h); +void bell(void); /* topsong */ void sortsong(void); diff --git a/include/visio.h b/include/visio.h index e4241c52..542fab28 100644 --- a/include/visio.h +++ b/include/visio.h @@ -11,17 +11,53 @@ #include "ansi.h" // we need it. // THEME DEFINITION ---------------------------------------------------- +#define VCLR_HEADER ANSI_COLOR(1;37;46) // was: TITLE_COLOR +#define VCLR_HEADER_MID ANSI_COLOR(1;33;46) +#define VCLR_HEADER_RIGHT ANSI_COLOR(1;37;46) +#define VCLR_HDR ANSI_COLOR(1;37;46) #define VCLR_FOOTER_CAPTION ANSI_COLOR(0;34;46) #define VCLR_FOOTER ANSI_COLOR(0;30;47) #define VCLR_FOOTER_QUOTE ANSI_COLOR(0;31;47) +#define VCLR_MSG_FLOAT ANSI_COLOR(1;33;46) +#define VCLR_MSG ANSI_COLOR(1;36;44) +#define VCLR_PAUSE_PAD ANSI_COLOR(1;34;44) +#define VCLR_PAUSE ANSI_COLOR(1;37;44) + +#define VMSG_PAUSE " 請按任意鍵繼續 " +#define VMSG_PAUSE_PAD "▄" +#define VMSG_MSG_FLOAT " [按任意鍵繼續]" +#define VMSG_MSG_PREFIX " ◆ " +#define VMSG_HDR_PREFIX "【 " +#define VMSG_HDR_POSTFIX " 】" + +// DATATYPE DEFINITION ------------------------------------------------- +typedef void * VSOREF; // generic visio object reference // API DEFINITION ---------------------------------------------------- -// int vmsg(char *msg); -// int vmsgf(const char *fmt,...); // int vans(char *prompt); // prompt at bottom and return y/n in lower case. -// void vs_bar(char *title); e// like stand_title -void vfooter(const char *caption, const char *prompt); -void vbarf (const char *s, ...); -void vbar (const char *l, const char *r); +// void vs_bar(char *title); // like stand_title +void vpad (int n, const char *pattern); +void vbarf (const char *s, ...) GCC_CHECK_FORMAT(1,2); +void vbarlr (const char *l, const char *r); +int vmsgf (const char *fmt,...) GCC_CHECK_FORMAT(1,2); +int vmsg (const char *msg); +int vansf (const char *fmt,...) GCC_CHECK_FORMAT(1,2); +int vans (const char *msg); +void vshowmsg(const char *msg); + +// vs_*: formatted and themed virtual screen layout +// you cannot use ANSI escapes in these APIs. +void vs_header (const char *title, const char *mid, const char *right); // vs_head, showtitle +void vs_hdr (const char *title); // vs_bar, stand_title +void vs_footer (const char *caption, const char *prompt); + +// compatible macros +#define stand_title vs_hdr + +// VSOREF API +VSOREF vscr_save (void); +void vscr_restore(VSOREF); +VSOREF vcur_save (void); +void vcur_restore(VSOREF); #endif // _VISIO_H diff --git a/mbbsd/admin.c b/mbbsd/admin.c index 500d1c1e..a67d165e 100644 --- a/mbbsd/admin.c +++ b/mbbsd/admin.c @@ -798,7 +798,7 @@ x_file(void) dashf(fn) ? ANSI_COLOR(1;32) : ANSI_COLOR(1;30;40), v, fn); } - vfooter(" 編輯系統檔案 ", + vs_footer(" 編輯系統檔案 ", " (jk/↑↓/0-9)移動 (Enter/→)編輯 (d)刪除 \t(q/←)跳出"); cursor_show(1+sel-page*rows, 0); switch((i = vkey())) diff --git a/mbbsd/angel.c b/mbbsd/angel.c index 11fe97eb..360c85d0 100644 --- a/mbbsd/angel.c +++ b/mbbsd/angel.c @@ -470,4 +470,33 @@ CallAngel(){ entered = 0; } +void +pressanykey_or_callangel(){ + int ch; + + if (!HasUserPerm(PERM_LOGINOK)) + { + pressanykey(); + return; + } + + // TODO use visio API instead. + outmsg( + VCLR_PAUSE_PAD " ▄▄▄▄ " + ANSI_COLOR(32) "H " ANSI_COLOR(36) "呼叫小天使" ANSI_COLOR(34) + " ▄▄▄▄" ANSI_COLOR(37;44) " 請按 " ANSI_COLOR(36) "空白鍵 " + ANSI_COLOR(37) "繼續 " ANSI_COLOR(1;34) + "▄▄▄▄▄▄▄▄▄▄▄▄▄▄ " ANSI_RESET); + do { + ch = igetch(); + if (ch == 'h' || ch == 'H'){ + CallAngel(); + break; + } + } while ((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n')); + move(b_lines, 0); + clrtoeol(); + refresh(); +} + #endif // PLAY_ANGEL diff --git a/mbbsd/announce.c b/mbbsd/announce.c index 21eccf21..0a592630 100644 --- a/mbbsd/announce.c +++ b/mbbsd/announce.c @@ -258,18 +258,18 @@ a_showmenu(menu_t * pm) { // something in queue char buf[STRLEN]; snprintf(buf, sizeof(buf), "【已標記(複製) %d 項】", copyqueue_querysize()); - vfooter(buf, pm->level == 0 ? + vs_footer(buf, pm->level == 0 ? " (c)標記/複製 - 無管理權限,無法貼上 " : " (c)標記/複製 (p)貼上/取消/重設標記 (a)附加至文章後\t(q/←)離開 (h)說明"); } else if(pm->level) { // BM - vfooter(" 【板 主】 ", + vs_footer(" 【板 主】 ", " (n)新增文章 (g)新增目錄 (e)編輯檔案\t(q/←)離開 (h)說明"); } else { // normal user - vfooter(" 【功\能鍵】 ", + vs_footer(" 【功\能鍵】 ", " (k↑j↓)移動游標 (enter/→)讀取資料\t(q/←)離開 (h)說明"); } return 1; diff --git a/mbbsd/board.c b/mbbsd/board.c index 46b2633d..936bd642 100644 --- a/mbbsd/board.c +++ b/mbbsd/board.c @@ -1073,7 +1073,7 @@ get_fav_type(boardstat_t *ptr) static void brdlist_foot(void) { - vfooter(" 選擇看板 ", + vs_footer(" 選擇看板 ", IS_LISTING_FAV() ? " (c)新文章模式 (v/V)標為已讀/未讀 (y)列出全部 (m)切換最愛" : " (c)新文章模式 (v/V)標為已讀/未讀 (y)篩選列表 (m)切換最愛"); diff --git a/mbbsd/mail.c b/mbbsd/mail.c index 1b05e801..b82c5070 100644 --- a/mbbsd/mail.c +++ b/mbbsd/mail.c @@ -901,7 +901,7 @@ read_new_mail(void * voidfptr, void *optarg) return more_result; } - vfooter(" 信件處理 ", + vs_footer(" 信件處理 ", " (R)回信 (x)站內轉寄 (y)回群組信 (d/D)刪信"); switch (igetch()) { diff --git a/mbbsd/pmore.c b/mbbsd/pmore.c index 8bf14636..f4e3b074 100644 --- a/mbbsd/pmore.c +++ b/mbbsd/pmore.c @@ -152,6 +152,19 @@ #include "bbs.h" // ---------------------------------------------------------------- <PORTING> +// Generic Porting +// ---------------------------------------------------------------- +// You need to have following APIs to support pmore: +// vkey(): return user input, with KEY_* translated (igetch()) +// vmsg(): print a message at bottom line, pause and return user pressed key +// outc()/outs()/prints(): character/string/formatstr output (w/ANSI ability) +// t_columns / b_lines / t_lines: current terminal dimension +// clear() / clrtobol() / clrtoeol(): screen clear API +// move(): move cursor location +// scroll() / rscroll() / refresh(): scroll / reverse-scroll / refresh screen +// getdata / getdata_buf : query user input +// +// ---------------------------------------------------------------- // Maple3 Porting // ---------------------------------------------------------------- // To use pmore in Maple3(itoc), you need to: diff --git a/mbbsd/read.c b/mbbsd/read.c index e239a67b..0e52f8b5 100644 --- a/mbbsd/read.c +++ b/mbbsd/read.c @@ -1353,10 +1353,10 @@ i_read(int cmdmode, const char *direct, void (*dotitle) (), /* no break */ case READ_REDRAW: if (curredit & EDIT_MAIL) - vfooter(" 鴻雁往返 ", + vs_footer(" 鴻雁往返 ", " (R)回信 (x)站內轉寄 (y)回群組信 (d/D)刪信 (m)保留標記\t(←/q)離開"); else - vfooter(" 文章選讀 ", + vs_footer(" 文章選讀 ", " (y)回應(X)推文(x)轉錄 (=[]<>)相關主題(/?a)搜尋標題/作者 (b)進板畫面"); break; diff --git a/mbbsd/register.c b/mbbsd/register.c index ef71fbf0..d5a6ac88 100644 --- a/mbbsd/register.c +++ b/mbbsd/register.c @@ -934,7 +934,7 @@ toregister(char *email, char *phone, char *career, char *mobile) "%s:%s:<Email>", phone, career); #ifdef HAVEMOBILE if (phone != NULL && email[1] == 0 && tolower(email[0]) == 'm') - sprintf(cuser.justify, sizeof(cuser.justify), + snprintf(cuser.justify, sizeof(cuser.justify), "%s:%s:<Mobile>", phone, career); #endif email_justify(&cuser); @@ -1522,8 +1522,8 @@ regform_reject(const char *userid, const char *reason, const RegformEntry *pre) static void prompt_regform_ui() { - vfooter(" 審核 ", - " (y)接受(n)拒絕(d)刪除 (s)跳過(u)復原 (空白/PgDn)儲存+下頁 (q/END)結束"); + vs_footer(" 審核 ", + " (y)接受(n)拒絕(d)丟掉 (s)跳過(u)復原 (空白/PgDn)儲存+下頁 (q/END)結束"); } static void diff --git a/mbbsd/stuff.c b/mbbsd/stuff.c index c9109601..3ce0407d 100644 --- a/mbbsd/stuff.c +++ b/mbbsd/stuff.c @@ -170,30 +170,8 @@ void syncnow(void) #endif } -#ifdef PLAY_ANGEL -void -pressanykey_or_callangel(){ - int ch; - - outmsg( - ANSI_COLOR(1;34;44) " ▄▄▄▄ " - ANSI_COLOR(32) "H " ANSI_COLOR(36) "呼叫小天使" ANSI_COLOR(34) - " ▄▄▄▄" ANSI_COLOR(37;44) " 請按 " ANSI_COLOR(36) "空白鍵 " - ANSI_COLOR(37) "繼續 " ANSI_COLOR(1;34) - "▄▄▄▄▄▄▄▄▄▄▄▄▄▄ " ANSI_RESET); - do { - ch = igetch(); - if (ch == 'h' || ch == 'H'){ - CallAngel(); - break; - } - } while ((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n')); - move(b_lines, 0); - clrtoeol(); - refresh(); -} -#endif - +// TODO +// move this function to visio.c /** * 給 printf format 的參數,印到最底下一行。 * 傳回使用者的選擇(char)。 @@ -212,6 +190,8 @@ getans(const char *fmt,...) return ans[0]; } +// TODO +// move this function to visio.c int getkey(const char *fmt,...) { @@ -223,82 +203,8 @@ getkey(const char *fmt,...) return vmsg(msg); } -static const char *msg_pressanykey_full = - ANSI_COLOR(37;44) " 請按" ANSI_COLOR(36) " 任意鍵 " ANSI_COLOR(37) "繼續 " ANSI_COLOR(34); -#define msg_pressanykey_full_len (18) - - // what is 200/1431/506/201? -static const char* msg_pressanykey_trail = - ANSI_COLOR(33;46) " [按任意鍵繼續] " ANSI_RESET; -#define msg_pressanykey_trail_len (16+1+4) /* 4 for head */ - -int -vmsg(const char *msg) -{ - int len = msg ? strlen(msg) : 0; - int i = 0; - - if(len == 0) msg = NULL; - - move(b_lines, 0); - clrtoeol(); - - if(!msg) - { - /* msg_pressanykey_full */ - int w = (t_columns - msg_pressanykey_full_len - 8) / 2; - int pad = 0; - - outs(ANSI_COLOR(1;34;44) " "); - pad += 1; - for (i = 0; i < w; i += 2) - outs("▄"), pad+=2; - outs(msg_pressanykey_full), pad+= msg_pressanykey_full_len; - /* pad now points to position of current cursor. */ - pad = t_columns - pad -2 ; - /* pad is now those left . */ - if (pad > 0) - { - for (i = 0; i <= pad-2; i += 2) - outs("▄"); - if (i == pad-1) - outs(" "); - } - outs(ANSI_RESET); - } else { - /* msg_pressanykey_trail */ - outs(ANSI_COLOR(1;36;44) " ◆ "); - if(len >= t_columns - msg_pressanykey_trail_len) - len = t_columns - msg_pressanykey_trail_len; - while (i++ < len) - outc(*msg++); - i--; - while (i++ < t_columns - msg_pressanykey_trail_len) - outc(' '); - outs(msg_pressanykey_trail); - } - - do { - i = igetch(); - } while( i == 0 ); - - move(b_lines, 0); - clrtoeol(); - return i; -} - -int -vmsgf(const char *fmt,...) -{ - char msg[512]; - va_list ap; - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - msg[sizeof(msg)-1] = 0; - return vmsg(msg); -} - +// TODO +// move this function to visio.c /** * 從第 y 列開始 show 出 filename 檔案中的前 lines 行。 * mode 為 output 的模式,參數同 strip_ansi。 @@ -352,15 +258,8 @@ show_file(const char *filename, int y, int lines, int mode) return ret; } -void -bell(void) -{ - char c; - - c = Ctrl('G'); - write(1, &c, 1); -} - +// TODO +// move this function to visio.c or visio.c int search_num(int ch, int max) { @@ -383,17 +282,8 @@ search_num(int ch, int max) return clen - 1; } -/** - * 在螢幕左上角 show 出 "【title】" - * @param title - */ -void -stand_title(const char *title) -{ - clear(); - prints(ANSI_COLOR(1;37;46) "【 %s 】" ANSI_RESET "\n", title); -} - +// TODO +// move this function to visio.c or visio.c void cursor_show(int row, int column) { @@ -402,6 +292,8 @@ cursor_show(int row, int column) move(row, column + 1); } +// TODO +// move this function to visio.c or visio.c void cursor_clear(int row, int column) { @@ -409,6 +301,8 @@ cursor_clear(int row, int column) outs(STR_UNCUR); } +// TODO +// move this function to visio.c or visio.c int cursor_key(int row, int column) { @@ -421,6 +315,8 @@ cursor_key(int row, int column) return ch; } +// TODO +// move this function to visio.c or visio.c void printdash(const char *mesg, int msglen) { @@ -462,6 +358,8 @@ log_user(const char *fmt, ...) return log_filef(filename, LOG_CREAT, "%s: %s %s", cuser.userid, msg, Cdate(&now)); } +// TODO +// move this function to visio.c or visio.c void show_help(const char * const helptext[]) { @@ -477,25 +375,17 @@ show_help(const char * const helptext[]) else prints(" %s\n", str); } -#ifdef PLAY_ANGEL - if (HasUserPerm(PERM_LOGINOK)) - pressanykey_or_callangel(); - else -#endif - pressanykey(); + PRESSANYKEY(); } +// TODO +// move this function to visio.c or visio.c void show_helpfile(const char *helpfile) { clear(); show_file((char *)helpfile, 0, b_lines, SHOWFILE_ALLOW_ALL); -#ifdef PLAY_ANGEL - if (HasUserPerm(PERM_LOGINOK)) - pressanykey_or_callangel(); - else -#endif - pressanykey(); + PRESSANYKEY(); } /* ----------------------------------------------------- */ diff --git a/mbbsd/talk.c b/mbbsd/talk.c index 3cf50c71..ac3fc9b0 100644 --- a/mbbsd/talk.c +++ b/mbbsd/talk.c @@ -1917,12 +1917,7 @@ t_showhelp(void) outs("(Y) 顯示正在看什麼板\n"); #endif } -#ifdef PLAY_ANGEL - if (HasUserPerm(PERM_LOGINOK)) - pressanykey_or_callangel(); - else -#endif - pressanykey(); + PRESSANYKEY(); } /* Kaede show friend description */ @@ -2306,7 +2301,7 @@ draw_pickup(int drawall, pickup_t * pickup, int pickup_way, modestr[currutmp->angelpause % ANGELPAUSE_MODES]); } else #endif - vfooter(" 休閒聊天 ", + vs_footer(" 休閒聊天 ", " (TAB/f)排序/好友 (a/o)交友 (q/w)查詢/丟水球 (t/m)聊天/寫信\t(h)說明"); } move(1, 0); diff --git a/mbbsd/term.c b/mbbsd/term.c index dffe7d79..8cac2583 100644 --- a/mbbsd/term.c +++ b/mbbsd/term.c @@ -76,3 +76,10 @@ term_init(void) return YEA; } +void +bell(void) +{ + const char c = Ctrl('G'); + write(1, &c, 1); +} + 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. |