死亡航班13百度云:第五章 面板库(panel)开发及应用

来源:百度文库 编辑:偶看新闻 时间:2024/05/02 13:47:51
5.1面板程序简介
5.1.1面板概念
通过前面的几章,我们知道窗口实际上终端屏幕上的一个矩形区域,在上面我们可以使用curses函数进行输入和输出。到目前为止你应该对curses库中的窗口很熟悉了,你也许已经开始准备编写一个稍具规模的curses程序了,但是你可能会遇到新的情况。
通常情况下,如果是小规模的应用程序,屏幕上的窗口都是平铺的,它们之间不互相重迭,这样的话curses函数会工作的很好。但是如果程序的规模稍微大一点,屏幕上窗口之间的重迭却是不可避免的。这种重迭导致的直接结果就是对窗口的管理变得更加复杂,甚至可能是一场恶梦。为了能够显示这些窗口,我们必须不断的调用wnoutrefresh()和doupdate()进行刷新,而且必须记住它们的刷新显示顺序。这是相当繁琐的事情。另一方面这种窗口重迭并不意味着窗口重迭部分就不可见。窗口重迭部分对一些curses函数来说仍然是可见的。
为了能够解决上面的问题,我们希望能够对窗口进行扩展,使得窗口之间具有一定的深度,然后在需要某个窗口的时候能够象使用堆栈中的元素那样,将它弹出自动完成显示,而不需要我们自己去进行各种刷新显示。curses包中的面板库就是为了解决这个问题而引进的。
面板库最早是在AT&T UNIX系统中引进的,目前大部分的UNIX和Linux都支持。面板不是窗口,它有自己的数据结构(见5.1.2)。每个面板都有一个关联窗口,而面板本身具有一定的层次关系,因此面板实际上间接的维持了它的关联窗口之间的层次关系。通过面板我们可以很方便的处理窗口的重迭和显示刷新。
在引进面板的概念的时候我们还需引入一个面板组的概念,英文名称为deck。它的概念相当于一个堆栈,所有的面板都叠放在面板组中。面板的深度仅仅是相对面板组中其余的面板和标准屏幕stdscr而言。标准屏幕位于所有的面板的最下面,它的相对深度为0。处于面板组最上面的面板是完全可见的,其余的面板根据它们的位置可能部分可见或者完全隐藏,也可能完全可见。面板、面板组、面板窗口之间的关系如图5.1所示。

图5.1
另外,需要区分好基垫pad和面板panel之间的区别。基垫也是一种窗口,只是它的范围可以超出标准屏幕的大小,其余的与普通窗口没有任何区别,而面板并不是窗口。
最后还需要注意的是即使引入面板库之后,窗口还是不具有相对深度,它的相对深度实际上通过面板的相对深度间接获取而来的,并不是面板库使得它本身具有的相对深度。
5.1.2面板数据结构
不同的系统中对PANEL结构的定义可能有所不同,SCO OpenServer和Solaris系统中的结构定义如下:
typedef struct PANEL
{
WINDOW *win;//面板关联窗口
int wstarty;
int wendy;
int wstartx;
int wendx;      //面板位置
struct _obscured_list *obscured;
struct PANEL *below, *above;//相邻面板指针
char *user;//面板的用户指针
} PANEL;
其中_obscured_list的结构定义如下:
typedef struct _obscured_list
{
struct PANEL *panel_p;
int start, end;
struct _obscured_list *next;
} _obscured_list;
而在Linux的GNU Ncurses4.2的最新版本中定义稍微有点不同:
typedef struct panel
{
WINDOW *win;
struct panel *below;
struct panel *above;
NCURSES_CONST void *user;
} PANEL;
其中NCURSES_CONST为const类型。
不同的系统对面板的定义虽然有所差异,但是它们都包含了关联窗口的指针以及它们相邻的面板的指针。面板之间实际上是通过链表结构联合起来的,对面板的一切操作都是对链表的操作。
5.1.3使用面板
面板的使用一般遵循下面几个步骤:
■ 创建面板的关联窗口。
■ 按照预先设定顺序在面板组中的创建面板。创建面板使用new_panel()。
■ 调用update_panel()将面板组中的面板输出到虚拟屏幕上,然后调用doupdate()将面板显示出来。
■ 使用面板库函数对面板进行操作,比如显示面板,隐藏面板或者移动面板等等。
■ 程序运行结束后使用del_panel()删除面板。
为了在程序中能够使用面板,我们必须在文件中包含面板库的头文件:
#include 
同时在编译的时候我们使用下面的命令:
cc [flags] files –lpanel –lcurses
或者
gcc [flags] files –lpanel –lcurses
下面我们给出一个简单的面板的示例程序,它演示了一个简单的面板程序的创建以及销毁的过程,程序中我们没有对面板进行任何的操作,在后面的部分我们会将这个例子进行适当的扩展以配合一些面板操作函数的使用讲解。
程序5-1 简单面板使用示例程序
程序名称 simppanel.c
编译命令 cc –o simppanel  simppanel.c –lpanel -lcurses
#include 
#include 
int main()
{
WINDOW *my_wins[3];//面板关联窗口
PANEL  *my_panels[3];//面板组定义
int lines = 10, cols = 40, y = 2, x = 4, i;
initscr();
cbreak();
noecho();
/* Create windows for the panels */
my_wins[0] = newwin(lines, cols, y, x);
my_wins[1] = newwin(lines, cols, y + 1, x + 5);
my_wins[2] = newwin(lines, cols, y + 2, x + 10);
for(i = 0; i < 3; +++i)
box(my_wins[i], 0, 0);
/* 创建面板 */     /* Order is bottom up */
my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */
my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1*/
my_panels[2] = new_panel(my_wins[2]);  /* Push 2, order: stdscr-0-1-2*/
/* 更新面板顺序,第二个面板处于面板组的最上面,完全可见 */
update_panels();
/* 将三个面板在屏幕上显示出来 */
doupdate();
getch();
endwin();

}程序执行效果如图5.2所示:
图5.2
5.3面板窗口基本操作
这一部分我们探讨关于面板的一些基本操作,通过这些函数的理解,希望能够进一步加深对面板概念的理解,并能够在实际问题中灵活应用。
面板操作的函数主要包括下面几个方面:
■ 创建以及删除面板
■ 获取面板的关联窗口
■ 显示以及隐藏面板
■ 移动面板
■ 更改面板在面板库中的深度
■ 获取当前面板的相邻面板
■ 获取当前面板的关联面板
■ 更改面板关联窗口
■ 设置和获取当前面板的用户指针
5.3.1创建和删除面板
在使用面板之前,首先必须创建面板。创建新的面板我们使用new_panel()函数。函数语法如表5.1所示。面板一旦创建它将位于面板组的最上面。它的参数是一个指向面板关联窗口的指针。
表5.1 面板创建函数概述
头文件
curses.h   panel.h
概述
PANEL *new_panel(window)
WINDOW *window;
如果面板创建成功,则返回指向面板的指针,同时传递给函数的参数窗口将与这个创建的面板关联起来,创建面板的大小和位置跟参数窗口一样。如果没有足够的内存或者窗口指针为NULL,创建操作失败,返回NULL。
在创建面板之前,必须首先创建窗口,然后将窗口的指针保存在面板结构中,下面的代码演示了创建的过程。
WINDOW *win;
PANEL *pptr;
win=newwin(2,6,0,3);
pptr=new_panel(win);//执行之后pptr将保存新面板的指针。
当你创建一个新的面板的时候,它自动的放置在面板组的最上面。一旦你调用doupdate()来调整面板的可见性,最上面的面板就可以完全可见了。通常一个面板只有在它的整个区域完全没有被其余的面板覆盖的时候,它才是可见的。当两个面板重迭的时候,较高层次的会掩盖较低层次的面板。如果面板之间不存在重迭,新面板从逻辑上讲仍然是位于原来的面板之上,仅从表面上我们实在看不出那个层次高,那个层次低。
一旦程序退出我们必须将以前创建的面板删除,否则容易发生内存泄漏。del_panel()函数可以用来删除面板。但是del_panel()并不能删除与面板关联窗口,如果需要删除窗口,你必须调用delwin()。函数语法如表5.2所示。
表5.2 面板删除函数概述
头文件
curses.h   panel.h
概述
int del_panel(panel)
PANEL *panel;
返回
成功
失败
OK
ERR
需要提醒的是,如果你想同时删除一个面板以及与它关联的窗口,你必须首先删除面板,而不是窗口。在调用delwin()之前你必须调用del_panel()。然而,当删除面板之后,你不一定非要删除关联窗口,有必要的话你可以将它关联到其余的面板上。如果函数执行成功,返回OK,否则返回ERR。一般如果panel指针为NULL的时候可能会导致失败。
下面的代码删除了指定的面板panel 。
PANEL *panel;
WINDOW *win=panel_window(panel);
del_panel(panel);
delwin(win);
5.3.2获取面板窗口指针
在创建面板的时候每一个面板都是与一个窗口关联的。不跟任何窗口关联的面板是不存在的。很多情况下我们需要对面板窗口进行操作,因此我们必须能够获取面板的关联窗口。如果你对使用的面板库中PANEL结构熟悉的话你可以直接访问PANEL结构中的win成员,更好的方法我们可以使用函数panel_window()。函数语法如表5.3所示。
表5.3 获取面板关联窗口函数概述
头文件
curses.h   panel.h
概述
WINDOW *panel_window(panel)
PANEL *panel;
一旦获取了关联窗口指针,我们就可以将它传递给相应的函数进行操作了。例如,如果你需要在面板窗口的 (y,x)处插入字符‘c’,那么你可以使用函数mvwinsch(win,y,x,c)完成。win是从panel_window()返回的指针。
WINDOW *win;
PANEL *panel;
int y,x;
chtype c;
win=panel_window(panel);
mvinsch(win,y,x,c);
5.3.3 面板更新
一旦某个面板发生了改变,那么可能导致整个屏幕的改变以及面板组中面板之间的深度的改变,因此必须能够在面板改变之后进行及时更新,使得屏幕显示正确的结果。curses中使用update_panels()函数对所有的面板进行更新。函数语法如表5.4所示。
表5.4 面板更新函数概述
头文件
curses.h   panel.h
概述
void update_panels()
返回
成功
失败
OK
ERR
然而,与窗口刷新函数一样,update_panels()并不立即刷新终端屏幕,只有在你调用doupdate()之后更改结果才会显示出来。为了避免在一个隐藏的面板上显示文本,你不能直接用curses函数wnoutrefresh()和wrefresh()对面板窗口进行操作。
通常我们建议使用update_panels()和doupdate()联合起来刷新显示整个面板组中的面板。大多数情况下我们不推荐直接使用wnoutrefresh()和wrefresh()来对面板关联窗口进行刷新,这样的话,大量的工作得自己去处理。即使你非要这样做,它也不可能有正确的显示,除非与刷新窗口关联的面板在面板组中处于最上部或者在屏幕上没有其余的面板窗口覆盖它。记住,所有的面板总是位于标准屏幕stdscr之上的。当移动或者删除一个面板的时候,stdscr总是使用可见的面板进行单独刷新,这样看起来它总是位于所有的面板的最下面。虽然stdscr与其余的面板比较也有相对深度,但是由于它不是真正意义上的面板,因此一些面板函数例如top_panel()和bottom_panel()对它来说并不适用。
另一方面,由于标准屏幕一直处于面板组的最下面,因此当你与面板库打交道的时候如果需要改变stdscr,即使你根本就没有对实际面板进行任何改动,你也必须调用update_panels()进行更新,而不是通常的窗口刷新函数。
如果你想在面板窗口中进行输入,你可以使用wgetch() ,但必须确保输入窗口完全没有被覆盖。函数wgetch()中会自动调用wrefresh()。
总的来说,为了更新面板,同时以正确的深度关系进行显示,你就要象下面一样:
WINDOW *win;
update_panels();
doupdate();
在后面的例子中我们基本上每个程序都是用到了更新函数,我们可以通过具体的实例加深这个函数的理解。
5.3.4调整面板相对深度
面板的相对深度调整包括两种,一是把面板调整到所有的面板组的最上部,另外一种就是调整到所有的最底部。在任何一种情况下,其余的面板之间的相对深度仍然保持不变。函数语法如表5.5所示。
表5.5 面板深度调整函数概述
头文件
curses.h   panel.h
概述
int top_panel(panel)
PANEL *panel;
int bottom_panel(panel)
PANEL *panel;
返回
成功
失败
OK
ERR
top_panel()将它的参数面板“弹出”到面板组的最上面,而bottom_panel()则是“压入”到最底部。这两个行为与对栈的“pop”和“push”非常相像。
这两个函数对所操作面板的大小,关联窗口的内容以及面板组中其余的面板之间的相对深度没有任何的影响。如果操作执行成功,返回OK ,否则返回ERR。失败的原因可能参数panel为NULL,或者面板在调用之前已经通过函数hide_panel()进行了隐藏。这两个函数只对当前面板组中的可见面板有效,对隐藏面板无效。
程序5-2演示了如何使用top_panel()函数调整各个面板的层次关系。使用按键可以依次将各个面板显示出来,使用‘q’可以将当前顶层面板置于底层。
程序5-2 面板窗口层次调整示例程序
程序名称 panel_browse.c
编译命令 cc –o panel_browse panel_browse.c –lpanel –lcurses
程序使用的面板库函数:
new_panel(),set_panel_userptr(),update_panels(),panel_userptr(),
top_panel(),bottom_panel()
#include 
#include 
#define NLINES 10
#define NCOLS 40
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string);
int main()
{
WINDOW *my_wins[3];
PANEL  *my_panels[3];
PANEL  *top;//最上面的面板
int ch;
/* 初始化curses库 */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
/*关联面板和窗口 */     /* Order is bottom up */
my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */
my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */
my_panels[2] = new_panel(my_wins[2]); /*Push 2,order: stdscr-0-1-2 */
/*将面板用户指针关联到下一个需要显示的面板*/
set_panel_userptr(my_panels[0], my_panels[1]);
set_panel_userptr(my_panels[1], my_panels[2]);
set_panel_userptr(my_panels[2], my_panels[0]);
/*更新*/
update_panels();
mvprintw(LINES - 5, 10,"使用Tab键进行窗口切换 (F1退出)");
doupdate();
top = my_panels[2]; /* Store the top panel pointer */
while((ch = getch()) != KEY_F(1))
{    switch(ch)
{    case 9:
/*如果是TAB键,则根据面板用户指针获取下一个需要显示的面板*/
top = (PANEL *)panel_userptr(top);
top_panel(top); /* Make it as the top panel */
break;
case 113:/*如果为回车,则将当前面板切换到最下面*/
top = (PANEL *)panel_userptr(top);
bottom_panel(top);
break;
}
/*重新更新面板的显示顺序*/
update_panels();
doupdate();
}
endwin();
return 0;
}
/*生成窗口*/
void init_wins(WINDOW **wins, int n)
{
int x, y, i;
char label[80];
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{    wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{    int startx, starty, height, width;
getbegyx(win, starty, startx);
getmaxyx(win, height, width);
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width - 2);
mvwaddch(win, 2, width - 1, ACS_RTEE);
print_in_middle(win, 1, 0, width, label);
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)
{    int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width - length)/ 2;
x = startx + (int)temp;
mvwprintw(win, y, x, "%s", string);
refresh();
}

程序运行界面如下图:图5.3
程序中我们使用了panel_userptr()和set_panel_userptr()两个面板用户指针函数,它们主要用来保存额外的面板数据,具体的用法在5.3.8节介绍。
5.3.5在屏幕上移动面板
移动面板实际上就是需要移动面板的关联窗口,但是你不能直接调用函数mvwin()来移动。为了保证能够使用update_panel()进行正确的屏幕更新,面板系统必须知道所有面板关联窗口的位置,因此面板移动函数必须有能力通知面板系统移动后窗口的新位置,但是函数mvwin()不可能做到这一点。为了能够移动面板窗口,必须使用新的函数move_panel()。它移动面板同时通知面板系统它们的移动情况。函数语法如表5.6所示。
表5.6 move_panel()函数概述
头文件
curses.h   panel.h
概述
int move_panel(panel,firstrow,firstcol)
PANEL *panel;
Int firstrow,firstcol;
返回
成功
失败
OK
ERR
使用函数move_panel()的时候,面板的大小,面板窗口以及它与其余的面板之间的相对深度保持不变。如果操作执行成功,函数返回OK ,否则返回ERR。如果面板不能移动,或者没有足够的内存来满足需求的话,可能会导致操作失败。在这样情况下原先的面板保持不变。
如果你需要讲面板panel的左上角移动到位置(22,45)处,你可以象下面一样处理。
PANEL *panel;
move_panel(panel,22,45);
下面给出的是一个完整的例子,主要演示了如何进行面板移动,同时演示了如何修改面板的尺寸大小。在例子中你可以上面那样使用键调整显示各个面板的可见性,然后通过按键’r’更改面板的尺寸,通过’m’移动面板。一旦按下’r’和’m’之后,你可以通过方向键来实际修改面板的位置和大小。调整之后通过回车可以将调整之后的结果显示出来。程序如下:
程序5-3 面板移动以及尺寸调整示例
程序名称 panel_move.c
编译命令 cc –o panel_move panel_move.c –lpanel –lcurses
程序使用的面板库函数:
new_panel(),set_panel_userptr(),update_panels(),panel_userptr(),
top_panel(),bottom_panel(),panel_window(),replace_panel(),
move_panel()
#include 
#include 
typedef struct _PANEL_DATA {
int x,  /* Startx */
y,      /* Starty */
w,      /* Width  */
h;      /* Height */
char label[80];  /* Label for the window */
int label_color; /* Color number for the label */
PANEL *next;     /* Pointer to the next Panel in the cycle */
}PANEL_DATA;
#define NLINES 10
#define NCOLS 40
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string);
void set_user_ptrs(PANEL **panels, int n);
int main()
{
WINDOW *my_wins[3];
PANEL  *my_panels[3];
PANEL_DATA  *top;
PANEL *stack_top;
WINDOW *temp_win, *old_win;
int ch;
int newx, newy, neww, newh;
int size = FALSE, move = FALSE;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
/* Attach a panel to each window */     /* Order is bottom up */
my_panels[0] = new_panel(my_wins[0]);    /* Push 0, order: stdscr-0 */
my_panels[1] = new_panel(my_wins[1]);  /* Push 1, order: stdscr-0-1 */
my_panels[2] = new_panel(my_wins[2]);/*Push 2, order: stdscr-0-1-2 */
set_user_ptrs(my_panels, 3);
/* Update the stacking order. 2nd panel will be on top */
update_panels();
/* Show it on the screen */
mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");
doupdate();
stack_top = my_panels[2];
top = (PANEL_DATA *)panel_userptr(stack_top);
newx = top->x;
newy = top->y;
neww = top->w;
newh = top->h;
while((ch = getch()) != KEY_F(1))
{    switch(ch)
{    case 9:        /* Tab */
top = (PANEL_DATA *)panel_userptr(stack_top);
top_panel(top->next);
stack_top = top->next;
top = (PANEL_DATA *)panel_userptr(stack_top);
newx = top->x;
newy = top->y;
neww = top->w;
newh = top->h;
break;
case 'r':    /* Re-Size*/
size = TRUE;
mvprintw(LINES - 3, 0, "Entered Resizing :Use Arrow Keys to resize and press  to end resizing");
refresh();
break;
case 'm':    /* Move */
mvprintw(LINES - 3, 0, "Entered Moving: Use Arrow Keys to Move and press  to end moving");
refresh();
move = TRUE;
break;
case KEY_LEFT:
if(size == TRUE)
{    --newx;
++neww;
}
if(move == TRUE)
--newx;
break;
case KEY_RIGHT:
if(size == TRUE)
{    ++newx;
--neww;
}
if(move == TRUE)
++newx;
break;
case KEY_UP:
if(size == TRUE)
{    --newy;
++newh;
}
if(move == TRUE)
--newy;
break;
case KEY_DOWN:
if(size == TRUE)
{    ++newy;
--newh;
}
if(move == TRUE)
++newy;
break;
case 10:    /* Enter */
move(LINES - 3, 0);
clrtoeol();
refresh();
if(size == TRUE)
{
old_win = panel_window(stack_top);
temp_win = newwin(newh, neww, newy, newx);
replace_panel(stack_top, temp_win);
win_show(temp_win, top->label, top->label_color);
delwin(old_win);
size = FALSE;
}
if(move == TRUE)
{    move_panel(stack_top, newy, newx);
move = FALSE;
}
break;
}
update_panels();
doupdate();
}
endwin();
return 0;
}
/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{    int x, y, i;
char label[80];
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{    wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
/* Set the PANEL_DATA structures for individual panels */
void set_user_ptrs(PANEL **panels, int n)
{    PANEL_DATA *ptrs;
WINDOW *win;
int x, y, w, h, i;
char temp[80];
ptrs = (PANEL_DATA *)calloc(n, sizeof(PANEL_DATA));
for(i = 0;i < n; ++i)
{    win = panel_window(panels[i]);
getbegyx(win, y, x);
getmaxyx(win, h, w);
ptrs[i].x = x;
ptrs[i].y = y;
ptrs[i].w = w;
ptrs[i].h = h;
sprintf(temp, "Window Number %d", i + 1);
strcpy(ptrs[i].label, temp);
ptrs[i].label_color = i + 1;
if(i + 1 == n)
ptrs[i].next = panels[0];
else
ptrs[i].next = panels[i + 1];
set_panel_userptr(panels[i], &ptrs[i]);
}
}
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{    int startx, starty, height, width;
getbegyx(win, starty, startx);
getmaxyx(win, height, width);
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width - 2);
mvwaddch(win, 2, width - 1, ACS_RTEE);
print_in_middle(win, 1, 0, width, label);
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)
{    int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width - length)/ 2;
x = startx + (int)temp;
mvwprintw(win, y, x, "%s", string);
refresh();
}
程序中通过两个布尔变量size和move来记录用户的请求:移动还是调整大小。一旦用户按下了键‘r’,size将赋值为真,这样用户移动方向键时候将执行调整大小的操作。否则一旦用户按下了‘m’,move将赋值为真,这样用户移动方向键时候执行移动操作。
5.3.6隐藏/显示面板
在面板使用过程中,如果面板暂时不需要,我们可以将它临时隐藏,这意味着我们必须将隐藏面板从面板组中移开,因为面板组中只存放可见面板。面板隐藏后它们在内存中占用的资源并不会释放。另一方面由于一些操作函数只能作用于可见面板,因此在执行这些操作的时候我们必须能够判断当前面板是隐藏还是显示,面板库中为此提供了函数panel_hidden(),如表5.7所示。
表5.7 面板隐藏函数概述
头文件
curses.h   panel.h
概述
int hide_panel(panel)
PANEL *panel;
int panel_hidden(panel)
PANEL *panel;
返回
成功
失败
OK
ERR
如果你灵活的运用hide_panel(),可能会使你的程序的性能有一个较大的提高。比如在某个时刻一个面板没有任何的部分需要显示,那么你可以临时隐藏它,这样对update_panels()的临时调用能够使程序执行更快。一个例子就是弹出式消息框,大部分情况下我们并不需要,但是一旦需要我们可以使用show_panel()显示,这样你就可以节省创建消息框的开销,而不必每次弹出的时候都创建消息框。
虽然隐藏的面板不会在屏幕上刷新,但是大多数的面板操作仍然适合它们,例外的是操作top_panel(),bottom_panel()和hide_panel(),它们不能适用于隐藏面板,因为这些函数的参数必须指向面板组中存在的面板,而隐藏面板已经不在面板组中了。
panel_hidden()用来判断一个面板的当前状态:隐藏还是显示。函数通过返回一个布尔变量来判断面板是否隐藏,如果返回TRUE,表明面板隐藏,否则没有隐藏。在调用top_panel()和bottom_panel()以及hide_panel()函数之前你最好调用这个函数进行判断,以防止发生错误。下面的代码演示了这种用法。
PANEL *panel;
if(!panel_hidden(panel))
top_panel(panel);
如果你需要将隐藏的面板重新放回到面板组中,你必须调用show_panel(),show_panel()可以是隐藏窗口恢复为可见状态。函数语法如表5.8所示。
表5.8 面板显示函数概述
头文件
curses.h   panel.h
概述
int show_panel(panel)
PANEL *panel;
返回
成功
失败
OK
ERR
注意,这个面板必须确实是使用hide_panel()隐藏过的。如果执行成功返回OK,否则返回ERR。失败的原因一是可能内存不够,二是面板并没有隐藏。例如,将上面隐藏的panel2返回到面板组中,代码片断可以如下:
PANEL *panel2;
show_panel(panel2);
一旦一个隐藏面板重新显示的时候,它总是位于所有的面板的最上面。
下面的例子演示了如何隐藏和显示面板。通过按键‘a’,‘b’,‘c’隐藏a、b、c三个面板,第二次按键将又将隐藏的面板显示出来。程序中定义了一个用户数据结构PANEL_DATA来记录与面板关联的数据,其中PANEL_DATA->hide记录当前的面板状态:隐藏还是显示。
程序5-4 面板隐藏示例程序
程序名称 hidepanel.c
编译命令 cc –o hidepanel  hidepanel.c –lpanel –lcurses
程序使用的面板库函数:
new_panel(),set_panel_userptr(),update_panels(),panel_userptr(),
hide_panel(),show_panel(),
#include 
#include 
typedef struct _PANEL_DATA {
int hide;    /* TRUE if panel is hidden */
}PANEL_DATA;
#define NLINES 10
#define NCOLS 40
void init_wins(WINDOW **wins, int n);
void win_show(WINDOW *win, char *label, int label_color);
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string);
int main()
{
WINDOW *my_wins[3];
PANEL  *my_panels[3];
PANEL_DATA panel_datas[3];
PANEL_DATA *temp;
int ch;
/* Initialize curses */
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
init_wins(my_wins, 3);
/* Attach a panel to each window */     /* Order is bottom up */
my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */
my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */
my_panels[2] = new_panel(my_wins[2]); /* Push 2,order:stdscr-0-1-2 */
/* Initialize panel datas saying that nothing is hidden */
panel_datas[0].hide = FALSE;
panel_datas[1].hide = FALSE;
panel_datas[2].hide = FALSE;
set_panel_userptr(my_panels[0], &panel_datas[0]);
set_panel_userptr(my_panels[1], &panel_datas[1]);
set_panel_userptr(my_panels[2], &panel_datas[2]);
/* Update the stacking order. 2nd panel will be on top */
update_panels();
/* Show it on the screen */
mvprintw(LINES - 3, 0, "Show or Hide a window with 'a'(first window)  'b'(Second Window)  'c'(Third Window)");
mvprintw(LINES - 2, 0, "F1 to Exit");
doupdate();
while((ch = getch()) != KEY_F(1))
{    switch(ch)
{    case 'a':
temp = (PANEL_DATA *)panel_userptr(my_panels[0]);
if(temp->hide == FALSE)
{    hide_panel(my_panels[0]);
temp->hide = TRUE;
}
else
{    show_panel(my_panels[0]);
temp->hide = FALSE;
}
break;
case 'b':
temp = (PANEL_DATA *)panel_userptr(my_panels[1]);
if(temp->hide == FALSE)
{    hide_panel(my_panels[1]);
temp->hide = TRUE;
}
else
{    show_panel(my_panels[1]);
temp->hide = FALSE;
}
break;
case 'c':
temp = (PANEL_DATA *)panel_userptr(my_panels[2]);
if(temp->hide == FALSE)
{    hide_panel(my_panels[2]);
temp->hide = TRUE;
}
else
{    show_panel(my_panels[2]);
temp->hide = FALSE;
}
break;
}
update_panels();
doupdate();
}
endwin();
return 0;
}
/* Put all the windows */
void init_wins(WINDOW **wins, int n)
{    int x, y, i;
char label[80];
y = 2;
x = 10;
for(i = 0; i < n; ++i)
{    wins[i] = newwin(NLINES, NCOLS, y, x);
sprintf(label, "Window Number %d", i + 1);
win_show(wins[i], label, i + 1);
y += 3;
x += 7;
}
}
/* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color)
{    int startx, starty, height, width;
getbegyx(win, starty, startx);
getmaxyx(win, height, width);
box(win, 0, 0);
mvwaddch(win, 2, 0, ACS_LTEE);
mvwhline(win, 2, 1, ACS_HLINE, width - 2);
mvwaddch(win, 2, width - 1, ACS_RTEE);
print_in_middle(win, 1, 0, width, label);
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)
{    int length, x, y;
float temp;
if(win == NULL)
win = stdscr;
getyx(win, y, x);
if(startx != 0)
x = startx;
if(starty != 0)
y = starty;
if(width == 0)
width = 80;
length = strlen(string);
temp = (width - length)/ 2;
x = startx + (int)temp;
mvwprintw(win, y, x, "%s", string);
refresh();
}
5.3.7获取相邻面板
有的时候我们获取了指定的面板,希望能够继续得到它上面的或者下面的面板,这时候我们可以使用下面如表5.9所示的这两个函数。在对面板组中所有的面板从上到下遍历的时候非常有用。
表5.9 面板相邻面板获取函数概述
头文件
curses.h   panel.h
概述
PANEL *panel_above(panel)
PANEL *panel;
PANEL *panel_below(panel)
PANEL *panel;
函数panel_above()立即返回给定面板上面的面板,如果它的参数为NULL,它返回最底层的那个面板,如果给定的面板已经处于最顶端或者隐藏,或者根本没有可见的面板的话,函数返回NULL 。
函数panel_below()立即返回给定面板下面的面板,如果它的参数为NULL,它返回最上层的那个面板。如果给定的面板已经位于最下面或者隐藏的话,或者根本就没有可见面板,函数返回NULL。如果面板组中没有可见面板,原因通常可能有下面几种:
■ 它们已经使用hide_panel()函数进行了隐藏。
■ 所有的面板已经被删除了。
■ 没有创建任何面板。
如果你需要对所有的面板进行操作或者在它们之间查找特定的面板,你可以把他们放在一个循环中。例如,下面的例子隐藏所有的面板。
PANEL *panel, *pnl;
for(panel=panel_above(null);panel;panel=panel_above(panel))
{
pnl=panel;
hide_panel(panel);
}
5.3.8设置或获取面板的用户指针
有的时候我们可能需要在面板中增加一些额外的信息,比如面板的名称,面板的介绍等等。这些信息的类型可能是任意类型,比如程序5-2中我们就是将一个面板作为另外一个面板的附加信息。为此面板库在面板结构PANEL中设置了usr指针。通过这个指针,我们可以将任意类型的数据与面板关联起来。面板初始创建时候,这个指针为NULL。为了使用这个指针,你可以给它赋值或者在赋值以后获取这个值。函数语法如表5.10所示。
表5.10 面板用户指针函数概述
头文件
curses.h   panel.h
概述
int * set_panel_userptr(panel,ptr)
PANEL *panel;
char *ptr;
char *panel_userptr(panel)
PANEL *panel;
char * ptr;
set_panel_userptr()将给定面板的用户指针设置成给定的值,如果操作成功,返回OK,否则如果panel参数为NULL,则返回ERR。panel_userptr()返回给定面板的用户指针如果panel指针为NULL,返回值也为NULL。
在你的应用程序中,你可以使用这两个函数存储和获取包含信息的任何结构类型的指针。例如,在下面的例子中你可能需要使用它们为一个用来作为弹出消息框的隐藏面板存储标题文本。
程序5-5 窗口用户指针示例程序
程序名称 panel_userptr.c
编译命令 cc –o panel_userptr  panel_userptr.c –lpanel –lcurses
#include 
#include 
PANEL *msg_panel;
char *message=”Pop-up Message Here”;
static int CURSES=FALSE;
static void start_curses()
{
CURSES=TRUE;
initscr();
nonl();
raw();
noecho();
wclear(stdscr);
}
static end_curses()
{
if(CURSES)
{
CURSES=FALSE;
endwin();
}
}
int display_deck(show_it)
int show_it;
{
WINDOW *w;
int  rows,cols;
if(show_it)
{
show_panel(msg_panel);
w=panel_window(msg_panel);
getmaxyx(w,rows,cols);
wmove(w,(rows-1),((cols-1)-strlen(message))/2);
waddstr(w,panel_userptr(msg_panel));
}
update_panels();
doupdate();
if(show_it)
hide_panel(msg_panel);
}
main()
{
int show_mess=FALSE;
start_curses();
msg_panel=new_panel(newwin(10,10,5,60));
set_panel_userptr(msg_panel,message);
hide_panel(msg_panel);
show_mess=TRUE;
display_deck(show_mess);
end_curses();
}
当我们创建了一个窗口以及与它关联的面板后,main()调用set_panel_userptr()设置面板的用户指针指向面板的弹出式消息字符。函数hide_panel()从面板中隐藏面板,这样它就不正常显示。然后用户自定义函数display_deck()检查是否有消息显示,如果有的话,它调用show_panel()将隐藏的面板返回到面板组中,使得面板在下次更新和刷新的时候能够可见。panel_userptr()返回的消息字符然后就会输出到面板窗口上。最后update_panel()会调整面板组中的所有面板的相互可见性,同时调用doupdate()刷新屏幕。这时候消息就能看得见了。
5.3.9更改面板关联窗口
为了将与一个面板关联的窗口替换为另外一个窗口,你可以通过调用replace_panel()来实现。当调用之后,面板仍然保持在面板组中原有的深度。函数语法如表5.11所示。
表5.11 replace_panel函数概述
头文件
curses.h   panel.h
概述
int  replace_panel(panel,window)
PANEL *panel;
WINDOW *window;
返回
成功
失败
OK
ERR
如果函数执行成功,则返回OK,否则返回ERR,并且保持原来的窗口不变。如果用来替换的窗口指针为NULL或者没有足够的内存,那么replace_panel()操作失败。
下面的代码演示了replace_panel()函数的用法。你首先将窗口win1与面板关联,然后又更换为win2, 程序代码如下:
WINDOW *win1, *win2;
PANEL *panel;
panel=new_panel(win1);
replace_panel(panel,win2);