summaryrefslogtreecommitdiffstats
path: root/mbbsd
diff options
context:
space:
mode:
authorpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2008-04-15 01:16:13 +0800
committerpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2008-04-15 01:16:13 +0800
commita942aaef1cdde595f482a853e6a1291c04b9d2c7 (patch)
tree73c7992b234f85bd7440a33ccf2a6c610cdff42c /mbbsd
parenta2f6c27927c37f89b888f9e4c1afe96049bdf406 (diff)
downloadpttbbs-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')
-rw-r--r--mbbsd/admin.c2
-rw-r--r--mbbsd/angel.c29
-rw-r--r--mbbsd/announce.c6
-rw-r--r--mbbsd/board.c2
-rw-r--r--mbbsd/mail.c2
-rw-r--r--mbbsd/pmore.c13
-rw-r--r--mbbsd/read.c4
-rw-r--r--mbbsd/register.c6
-rw-r--r--mbbsd/stuff.c154
-rw-r--r--mbbsd/talk.c9
-rw-r--r--mbbsd/term.c7
-rw-r--r--mbbsd/visio.c346
12 files changed, 415 insertions, 165 deletions
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.