| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> |
| <HTML |
| ><HEAD |
| ><TITLE |
| > NCURSES Programming HOWTO </TITLE |
| ><META |
| NAME="GENERATOR" |
| CONTENT="Modular DocBook HTML Stylesheet Version 1.79"></HEAD |
| ><BODY |
| CLASS="ARTICLE" |
| BGCOLOR="#FFFFFF" |
| TEXT="#000000" |
| LINK="#0000FF" |
| VLINK="#840084" |
| ALINK="#0000FF" |
| ><DIV |
| CLASS="ARTICLE" |
| ><DIV |
| CLASS="TITLEPAGE" |
| ><H1 |
| CLASS="TITLE" |
| ><A |
| NAME="AEN2" |
| >NCURSES Programming HOWTO</A |
| ></H1 |
| ><H3 |
| CLASS="AUTHOR" |
| ><A |
| NAME="AEN4" |
| > Pradeep Padala </A |
| ></H3 |
| ><DIV |
| CLASS="AFFILIATION" |
| ><DIV |
| CLASS="ADDRESS" |
| ><P |
| CLASS="ADDRESS" |
| ><CODE |
| CLASS="EMAIL" |
| ><<A |
| HREF="mailto:ppadala@gmail.com" |
| >ppadala@gmail.com</A |
| >></CODE |
| ></P |
| ></DIV |
| ></DIV |
| ><P |
| CLASS="PUBDATE" |
| >v1.9, 2005-06-20<BR></P |
| ><DIV |
| CLASS="REVHISTORY" |
| ><TABLE |
| WIDTH="100%" |
| BORDER="0" |
| ><TR |
| ><TH |
| ALIGN="LEFT" |
| VALIGN="TOP" |
| COLSPAN="3" |
| ><B |
| >Revision History</B |
| ></TH |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.9</TD |
| ><TD |
| ALIGN="LEFT" |
| >2005-06-20</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >The license has been changed to the MIT-style license used |
| by NCURSES. Note that the programs are also re-licensed under this.</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.8</TD |
| ><TD |
| ALIGN="LEFT" |
| >2005-06-17</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Lots of updates. Added references and perl examples. |
| Changes to examples. Many grammatical and stylistic changes to the |
| content. Changes to NCURSES history.</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.7.1</TD |
| ><TD |
| ALIGN="LEFT" |
| >2002-06-25</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Added a README file for building and instructions |
| for building from source.</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.7</TD |
| ><TD |
| ALIGN="LEFT" |
| >2002-06-25</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Added "Other formats" section and made a lot of fancy |
| changes to the programs. Inlining of programs is gone.</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.6.1</TD |
| ><TD |
| ALIGN="LEFT" |
| >2002-02-24</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Removed the old Changelog section, cleaned the makefiles</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.6</TD |
| ><TD |
| ALIGN="LEFT" |
| >2002-02-16</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Corrected a lot of spelling mistakes, added ACS variables |
| section</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.5</TD |
| ><TD |
| ALIGN="LEFT" |
| >2002-01-05</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Changed structure to present proper TOC</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.3.1</TD |
| ><TD |
| ALIGN="LEFT" |
| >2001-07-26</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Corrected maintainers paragraph, Corrected stable release number</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.3</TD |
| ><TD |
| ALIGN="LEFT" |
| >2001-07-24</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Added copyright notices to main document (LDP license) |
| and programs (GPL), Corrected |
| printw_example.</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.2</TD |
| ><TD |
| ALIGN="LEFT" |
| >2001-06-05</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Incorporated ravi's changes. Mainly to introduction, menu, |
| form, justforfun sections</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| >Revision 1.1</TD |
| ><TD |
| ALIGN="LEFT" |
| >2001-05-22</TD |
| ><TD |
| ALIGN="LEFT" |
| >Revised by: ppadala</TD |
| ></TR |
| ><TR |
| ><TD |
| ALIGN="LEFT" |
| COLSPAN="3" |
| >Added "a word about window" section, Added scanw_example.</TD |
| ></TR |
| ></TABLE |
| ></DIV |
| ><DIV |
| ><DIV |
| CLASS="ABSTRACT" |
| ><P |
| ></P |
| ><A |
| NAME="AEN67" |
| ></A |
| ><P |
| > <SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >This document is intended to be an "All in One" guide for programming with |
| ncurses and its sister libraries. We graduate from a simple "Hello World" |
| program to more complex form manipulation. No prior experience in ncurses is |
| assumed. Send comments to <A |
| HREF="mailto:ppadala@gmail.com" |
| TARGET="_top" |
| >this address</A |
| > |
| </I |
| ></SPAN |
| > |
| </P |
| ><P |
| ></P |
| ></DIV |
| ></DIV |
| ><HR></DIV |
| ><DIV |
| CLASS="TOC" |
| ><DL |
| ><DT |
| ><B |
| >Table of Contents</B |
| ></DT |
| ><DT |
| >1. <A |
| HREF="#INTRO" |
| >Introduction</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >1.1. <A |
| HREF="#WHATIS" |
| >What is NCURSES?</A |
| ></DT |
| ><DT |
| >1.2. <A |
| HREF="#WHATCANWEDO" |
| >What we can do with NCURSES</A |
| ></DT |
| ><DT |
| >1.3. <A |
| HREF="#WHERETOGETIT" |
| >Where to get it</A |
| ></DT |
| ><DT |
| >1.4. <A |
| HREF="#PURPOSE" |
| >Purpose/Scope of the document</A |
| ></DT |
| ><DT |
| >1.5. <A |
| HREF="#ABOUTPROGRAMS" |
| >About the Programs</A |
| ></DT |
| ><DT |
| >1.6. <A |
| HREF="#OTHERFORMATS" |
| >Other Formats of the document</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >1.6.1. <A |
| HREF="#LISTFORMATS" |
| >Readily available formats from tldp.org</A |
| ></DT |
| ><DT |
| >1.6.2. <A |
| HREF="#BUILDSOURCE" |
| >Building from source</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >1.7. <A |
| HREF="#CREDITS" |
| >Credits</A |
| ></DT |
| ><DT |
| >1.8. <A |
| HREF="#WISHLIST" |
| >Wish List</A |
| ></DT |
| ><DT |
| >1.9. <A |
| HREF="#COPYRIGHT" |
| >Copyright</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >2. <A |
| HREF="#HELLOWORLD" |
| >Hello World !!!</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >2.1. <A |
| HREF="#COMPILECURSES" |
| >Compiling With the NCURSES Library</A |
| ></DT |
| ><DT |
| >2.2. <A |
| HREF="#DISSECTION" |
| >Dissection</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >2.2.1. <A |
| HREF="#ABOUT-INITSCR" |
| >About initscr()</A |
| ></DT |
| ><DT |
| >2.2.2. <A |
| HREF="#MYST-REFRESH" |
| >The mysterious refresh()</A |
| ></DT |
| ><DT |
| >2.2.3. <A |
| HREF="#ABOUT-ENDWIN" |
| >About endwin()</A |
| ></DT |
| ></DL |
| ></DD |
| ></DL |
| ></DD |
| ><DT |
| >3. <A |
| HREF="#GORY" |
| >The Gory Details</A |
| ></DT |
| ><DT |
| >4. <A |
| HREF="#INIT" |
| >Initialization</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >4.1. <A |
| HREF="#ABOUTINIT" |
| >Initialization functions</A |
| ></DT |
| ><DT |
| >4.2. <A |
| HREF="#RAWCBREAK" |
| >raw() and cbreak()</A |
| ></DT |
| ><DT |
| >4.3. <A |
| HREF="#ECHONOECHO" |
| >echo() and noecho()</A |
| ></DT |
| ><DT |
| >4.4. <A |
| HREF="#KEYPAD" |
| >keypad()</A |
| ></DT |
| ><DT |
| >4.5. <A |
| HREF="#HALFDELAY" |
| >halfdelay()</A |
| ></DT |
| ><DT |
| >4.6. <A |
| HREF="#MISCINIT" |
| >Miscellaneous Initialization functions</A |
| ></DT |
| ><DT |
| >4.7. <A |
| HREF="#INITEX" |
| >An Example</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >5. <A |
| HREF="#AWORDWINDOWS" |
| >A Word about Windows</A |
| ></DT |
| ><DT |
| >6. <A |
| HREF="#PRINTW" |
| >Output functions</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >6.1. <A |
| HREF="#ADDCHCLASS" |
| >addch() class of functions</A |
| ></DT |
| ><DT |
| >6.2. <A |
| HREF="#AEN298" |
| >mvaddch(), waddch() and mvwaddch()</A |
| ></DT |
| ><DT |
| >6.3. <A |
| HREF="#PRINTWCLASS" |
| >printw() class of functions</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >6.3.1. <A |
| HREF="#PRINTWMVPRINTW" |
| >printw() and mvprintw</A |
| ></DT |
| ><DT |
| >6.3.2. <A |
| HREF="#WPRINTWMVWPRINTW" |
| >wprintw() and mvwprintw</A |
| ></DT |
| ><DT |
| >6.3.3. <A |
| HREF="#VWPRINTW" |
| >vwprintw()</A |
| ></DT |
| ><DT |
| >6.3.4. <A |
| HREF="#SIMPLEPRINTWEX" |
| >A Simple printw example</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >6.4. <A |
| HREF="#ADDSTRCLASS" |
| >addstr() class of functions</A |
| ></DT |
| ><DT |
| >6.5. <A |
| HREF="#ACAUTION" |
| >A word of caution</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >7. <A |
| HREF="#SCANW" |
| >Input functions</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >7.1. <A |
| HREF="#GETCHCLASS" |
| >getch() class of functions</A |
| ></DT |
| ><DT |
| >7.2. <A |
| HREF="#SCANWCLASS" |
| >scanw() class of functions</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >7.2.1. <A |
| HREF="#SCANWMVSCANW" |
| >scanw() and mvscanw</A |
| ></DT |
| ><DT |
| >7.2.2. <A |
| HREF="#WSCANWMVWSCANW" |
| >wscanw() and mvwscanw()</A |
| ></DT |
| ><DT |
| >7.2.3. <A |
| HREF="#VWSCANW" |
| >vwscanw()</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >7.3. <A |
| HREF="#GETSTRCLASS" |
| >getstr() class of functions</A |
| ></DT |
| ><DT |
| >7.4. <A |
| HREF="#GETSTREX" |
| >Some examples</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >8. <A |
| HREF="#ATTRIB" |
| >Attributes</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >8.1. <A |
| HREF="#ATTRIBDETAILS" |
| >The details</A |
| ></DT |
| ><DT |
| >8.2. <A |
| HREF="#ATTRONVSATTRSET" |
| >attron() vs attrset()</A |
| ></DT |
| ><DT |
| >8.3. <A |
| HREF="#ATTR_GET" |
| >attr_get()</A |
| ></DT |
| ><DT |
| >8.4. <A |
| HREF="#ATTR_FUNCS" |
| >attr_ functions</A |
| ></DT |
| ><DT |
| >8.5. <A |
| HREF="#WATTRFUNCS" |
| >wattr functions</A |
| ></DT |
| ><DT |
| >8.6. <A |
| HREF="#CHGAT" |
| >chgat() functions</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >9. <A |
| HREF="#WINDOWS" |
| >Windows</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >9.1. <A |
| HREF="#WINDOWBASICS" |
| >The basics</A |
| ></DT |
| ><DT |
| >9.2. <A |
| HREF="#LETBEWINDOW" |
| >Let there be a Window !!!</A |
| ></DT |
| ><DT |
| >9.3. <A |
| HREF="#BORDEREXEXPL" |
| >Explanation</A |
| ></DT |
| ><DT |
| >9.4. <A |
| HREF="#OTHERSTUFF" |
| >The other stuff in the example</A |
| ></DT |
| ><DT |
| >9.5. <A |
| HREF="#OTHERBORDERFUNCS" |
| >Other Border functions</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >10. <A |
| HREF="#COLOR" |
| >Colors</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >10.1. <A |
| HREF="#COLORBASICS" |
| >The basics</A |
| ></DT |
| ><DT |
| >10.2. <A |
| HREF="#CHANGECOLORDEFS" |
| >Changing Color Definitions</A |
| ></DT |
| ><DT |
| >10.3. <A |
| HREF="#COLORCONTENT" |
| >Color Content</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >11. <A |
| HREF="#KEYS" |
| >Interfacing with the key board</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >11.1. <A |
| HREF="#KEYSBASICS" |
| >The Basics</A |
| ></DT |
| ><DT |
| >11.2. <A |
| HREF="#SIMPLEKEYEX" |
| >A Simple Key Usage example</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >12. <A |
| HREF="#MOUSE" |
| >Interfacing with the mouse</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >12.1. <A |
| HREF="#MOUSEBASICS" |
| >The Basics</A |
| ></DT |
| ><DT |
| >12.2. <A |
| HREF="#GETTINGEVENTS" |
| >Getting the events</A |
| ></DT |
| ><DT |
| >12.3. <A |
| HREF="#MOUSETOGETHER" |
| >Putting it all Together</A |
| ></DT |
| ><DT |
| >12.4. <A |
| HREF="#MISCMOUSEFUNCS" |
| >Miscellaneous Functions</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >13. <A |
| HREF="#SCREEN" |
| >Screen Manipulation</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >13.1. <A |
| HREF="#GETYX" |
| >getyx() functions</A |
| ></DT |
| ><DT |
| >13.2. <A |
| HREF="#SCREENDUMP" |
| >Screen Dumping</A |
| ></DT |
| ><DT |
| >13.3. <A |
| HREF="#WINDOWDUMP" |
| >Window Dumping</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >14. <A |
| HREF="#MISC" |
| >Miscellaneous features</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >14.1. <A |
| HREF="#CURSSET" |
| >curs_set()</A |
| ></DT |
| ><DT |
| >14.2. <A |
| HREF="#TEMPLEAVE" |
| >Temporarily Leaving Curses mode</A |
| ></DT |
| ><DT |
| >14.3. <A |
| HREF="#ACSVARS" |
| >ACS_ variables</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >15. <A |
| HREF="#OTHERLIB" |
| >Other libraries</A |
| ></DT |
| ><DT |
| >16. <A |
| HREF="#PANELS" |
| >Panel Library</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >16.1. <A |
| HREF="#PANELBASICS" |
| >The Basics</A |
| ></DT |
| ><DT |
| >16.2. <A |
| HREF="#COMPILEPANELS" |
| >Compiling With the Panels Library</A |
| ></DT |
| ><DT |
| >16.3. <A |
| HREF="#PANELBROWSING" |
| >Panel Window Browsing</A |
| ></DT |
| ><DT |
| >16.4. <A |
| HREF="#USERPTRUSING" |
| >Using User Pointers</A |
| ></DT |
| ><DT |
| >16.5. <A |
| HREF="#PANELMOVERESIZE" |
| >Moving and Resizing Panels</A |
| ></DT |
| ><DT |
| >16.6. <A |
| HREF="#PANELSHOWHIDE" |
| >Hiding and Showing Panels</A |
| ></DT |
| ><DT |
| >16.7. <A |
| HREF="#PANELABOVE" |
| >panel_above() and panel_below() Functions</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >17. <A |
| HREF="#MENUS" |
| >Menus Library</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >17.1. <A |
| HREF="#MENUBASICS" |
| >The Basics</A |
| ></DT |
| ><DT |
| >17.2. <A |
| HREF="#COMPILEMENUS" |
| >Compiling With the Menu Library</A |
| ></DT |
| ><DT |
| >17.3. <A |
| HREF="#MENUDRIVER" |
| >Menu Driver: The work horse of the menu system</A |
| ></DT |
| ><DT |
| >17.4. <A |
| HREF="#MENUWINDOWS" |
| >Menu Windows</A |
| ></DT |
| ><DT |
| >17.5. <A |
| HREF="#SCROLLMENUS" |
| >Scrolling Menus</A |
| ></DT |
| ><DT |
| >17.6. <A |
| HREF="#MULTICOLUMN" |
| >Multi Columnar Menus</A |
| ></DT |
| ><DT |
| >17.7. <A |
| HREF="#MULTIVALUEMENUS" |
| >Multi Valued Menus</A |
| ></DT |
| ><DT |
| >17.8. <A |
| HREF="#MENUOPT" |
| >Menu Options</A |
| ></DT |
| ><DT |
| >17.9. <A |
| HREF="#MENUUSERPTR" |
| >The useful User Pointer</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >18. <A |
| HREF="#FORMS" |
| >Forms Library</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >18.1. <A |
| HREF="#FORMBASICS" |
| >The Basics</A |
| ></DT |
| ><DT |
| >18.2. <A |
| HREF="#COMPILEFORMS" |
| >Compiling With the Forms Library</A |
| ></DT |
| ><DT |
| >18.3. <A |
| HREF="#PLAYFIELDS" |
| >Playing with Fields</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >18.3.1. <A |
| HREF="#FETCHINFO" |
| >Fetching Size and Location of Field</A |
| ></DT |
| ><DT |
| >18.3.2. <A |
| HREF="#MOVEFIELD" |
| >Moving the field</A |
| ></DT |
| ><DT |
| >18.3.3. <A |
| HREF="#JUSTIFYFIELD" |
| >Field Justification</A |
| ></DT |
| ><DT |
| >18.3.4. <A |
| HREF="#FIELDDISPATTRIB" |
| >Field Display Attributes</A |
| ></DT |
| ><DT |
| >18.3.5. <A |
| HREF="#FIELDOPTIONBITS" |
| >Field Option Bits</A |
| ></DT |
| ><DT |
| >18.3.6. <A |
| HREF="#FIELDSTATUS" |
| >Field Status</A |
| ></DT |
| ><DT |
| >18.3.7. <A |
| HREF="#FIELDUSERPTR" |
| >Field User Pointer</A |
| ></DT |
| ><DT |
| >18.3.8. <A |
| HREF="#VARIABLESIZEFIELDS" |
| >Variable-Sized Fields</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >18.4. <A |
| HREF="#FORMWINDOWS" |
| >Form Windows</A |
| ></DT |
| ><DT |
| >18.5. <A |
| HREF="#FILEDVALIDATE" |
| >Field Validation</A |
| ></DT |
| ><DT |
| >18.6. <A |
| HREF="#FORMDRIVER" |
| >Form Driver: The work horse of the forms system</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >18.6.1. <A |
| HREF="#PAGENAVREQ" |
| >Page Navigation Requests</A |
| ></DT |
| ><DT |
| >18.6.2. <A |
| HREF="#INTERFIELDNAVREQ" |
| >Inter-Field Navigation Requests</A |
| ></DT |
| ><DT |
| >18.6.3. <A |
| HREF="#INTRAFIELDNAVREQ" |
| >Intra-Field Navigation Requests</A |
| ></DT |
| ><DT |
| >18.6.4. <A |
| HREF="#SCROLLREQ" |
| >Scrolling Requests</A |
| ></DT |
| ><DT |
| >18.6.5. <A |
| HREF="#EDITREQ" |
| >Editing Requests</A |
| ></DT |
| ><DT |
| >18.6.6. <A |
| HREF="#ORDERREQ" |
| >Order Requests</A |
| ></DT |
| ><DT |
| >18.6.7. <A |
| HREF="#APPLICCOMMANDS" |
| >Application Commands</A |
| ></DT |
| ></DL |
| ></DD |
| ></DL |
| ></DD |
| ><DT |
| >19. <A |
| HREF="#TOOLS" |
| >Tools and Widget Libraries</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >19.1. <A |
| HREF="#CDK" |
| >CDK (Curses Development Kit)</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >19.1.1. <A |
| HREF="#WIDGETLIST" |
| >Widget List</A |
| ></DT |
| ><DT |
| >19.1.2. <A |
| HREF="#CDKATTRACT" |
| >Some Attractive Features</A |
| ></DT |
| ><DT |
| >19.1.3. <A |
| HREF="#CDKCONCLUSION" |
| >Conclusion</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >19.2. <A |
| HREF="#DIALOG" |
| >The dialog</A |
| ></DT |
| ><DT |
| >19.3. <A |
| HREF="#PERLCURSES" |
| >Perl Curses Modules CURSES::FORM and CURSES::WIDGETS</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >20. <A |
| HREF="#JUSTFORFUN" |
| >Just For Fun !!!</A |
| ></DT |
| ><DD |
| ><DL |
| ><DT |
| >20.1. <A |
| HREF="#GAMEOFLIFE" |
| >The Game of Life</A |
| ></DT |
| ><DT |
| >20.2. <A |
| HREF="#MAGIC" |
| >Magic Square</A |
| ></DT |
| ><DT |
| >20.3. <A |
| HREF="#HANOI" |
| >Towers of Hanoi</A |
| ></DT |
| ><DT |
| >20.4. <A |
| HREF="#QUEENS" |
| >Queens Puzzle</A |
| ></DT |
| ><DT |
| >20.5. <A |
| HREF="#SHUFFLE" |
| >Shuffle</A |
| ></DT |
| ><DT |
| >20.6. <A |
| HREF="#TT" |
| >Typing Tutor</A |
| ></DT |
| ></DL |
| ></DD |
| ><DT |
| >21. <A |
| HREF="#REF" |
| >References</A |
| ></DT |
| ></DL |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="INTRO" |
| >1. Introduction</A |
| ></H2 |
| ><P |
| >In the olden days of teletype terminals, terminals were away from computers and |
| were connected to them through serial cables. The terminals could be configured |
| by sending a series of bytes. All the capabilities (such as |
| moving the cursor to a new location, erasing part of the screen, scrolling the |
| screen, changing modes etc.) of terminals could be accessed through these |
| series of bytes. These control seeuqnces are usually called escape sequences, |
| because they start |
| with an escape(0x1B) character. Even today, with proper emulation, we can send |
| escape sequences to the emulator and achieve the same effect on a terminal |
| window.</P |
| ><P |
| >Suppose you wanted to print a line in color. Try typing this on your console.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >echo "^[[0;31;40mIn Color"</PRE |
| ><P |
| >The first character is an escape character, which looks like two characters ^ |
| and [. To be able to print it, you have to press CTRL+V and then the ESC key. |
| All the others are normal printable characters. You should be able to see the |
| string "In Color" in red. It stays that way and to revert back to the original |
| mode type this.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >echo "^[[0;37;40m"</PRE |
| ><P |
| >Now, what do these magic characters mean? Difficult to comprehend? They might |
| even be different for different terminals. So the designers of UNIX invented a |
| mechanism named <TT |
| CLASS="LITERAL" |
| >termcap</TT |
| >. It is a file that |
| lists all the capabilities of a particular terminal, along with the escape |
| sequences needed to achieve a particular effect. In the later years, this was |
| replaced by <TT |
| CLASS="LITERAL" |
| >terminfo</TT |
| >. Without delving too |
| much into details, this mechanism allows application |
| programs to query the terminfo database and obtain the control characters to be |
| sent to a terminal or terminal emulator.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WHATIS" |
| >1.1. What is NCURSES?</A |
| ></H3 |
| ><P |
| > |
| You might be wondering, what the import of all this technical gibberish is. In |
| the above scenario, every application program is supposed to query the terminfo |
| and perform the necessary stuff (sending control characters etc.). It soon became |
| difficult to manage this complexity and this gave birth to 'CURSES'. Curses is |
| a pun on the name "cursor optimization". The Curses library forms a wrapper |
| over working with raw terminal codes, and provides highly flexible and |
| efficient API (Application Programming Interface). It provides functions to |
| move the cursor, create windows, produce colors, play with mouse etc. The |
| application programs need not worry about the underlying terminal capabilities.</P |
| ><P |
| >So what is NCURSES? NCURSES is a clone of the original System V Release 4.0 |
| (SVr4) curses. It is a freely distributable library, fully compatible with |
| older version of curses. In short, it is a library of functions that manages |
| an application's display on character-cell terminals. In the remainder of the |
| document, the terms curses and ncurses are used interchangeably. </P |
| ><P |
| >A detailed history of NCURSES can be found in the NEWS file from the source |
| distribution. The current package is maintained by |
| <A |
| HREF="mailto:dickey@his.com" |
| TARGET="_top" |
| >Thomas Dickey</A |
| >. |
| You can contact the maintainers at <A |
| HREF="mailto:bug-ncurses@gnu.org" |
| TARGET="_top" |
| >bug-ncurses@gnu.org</A |
| >.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WHATCANWEDO" |
| >1.2. What we can do with NCURSES</A |
| ></H3 |
| ><P |
| >NCURSES not only creates a wrapper over terminal capabilities, but also gives a |
| robust framework to create nice looking UI (User Interface)s in text mode. It |
| provides functions to create windows etc. Its sister libraries panel, menu and |
| form provide an extension to the basic curses library. These libraries usually |
| come along with curses. One can create applications that contain multiple |
| windows, menus, panels and forms. Windows can be managed independently, can |
| provide 'scrollability' and even can be hidden.</P |
| ><P |
| > |
| Menus provide the user with an easy command selection option. Forms allow the |
| creation of easy-to-use data entry and display windows. Panels extend the |
| capabilities of ncurses to deal with overlapping and stacked windows.</P |
| ><P |
| >These are just some of the basic things we can do with ncurses. As we move |
| along, We will see all the capabilities of these libraries. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WHERETOGETIT" |
| >1.3. Where to get it</A |
| ></H3 |
| ><P |
| >All right, now that you know what you can do with ncurses, you must be rearing |
| to get started. NCURSES is usually shipped with your installation. In case |
| you don't have the library or want to compile it on your own, read on.</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >Compiling the package</I |
| ></SPAN |
| > </P |
| ><P |
| >NCURSES can be obtained from <A |
| HREF="ftp://ftp.gnu.org/pub/gnu/ncurses/ncurses.tar.gz" |
| TARGET="_top" |
| >ftp://ftp.gnu.org/pub/gnu/ncurses/ncurses.tar.gz</A |
| > or any of the ftp |
| sites mentioned in <A |
| HREF="http://www.gnu.org/order/ftp.html" |
| TARGET="_top" |
| >http://www.gnu.org/order/ftp.html</A |
| >. </P |
| ><P |
| >Read the README and INSTALL files for details on to how to install it. It |
| usually involves the following operations.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > tar zxvf ncurses<version>.tar.gz # unzip and untar the archive |
| cd ncurses<version> # cd to the directory |
| ./configure # configure the build according to your |
| # environment |
| make # make it |
| su root # become root |
| make install # install it</PRE |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >Using the RPM </I |
| ></SPAN |
| ></P |
| ><P |
| >NCURSES RPM can be found and downloaded from <A |
| HREF="http://rpmfind.net" |
| TARGET="_top" |
| >http://rpmfind.net </A |
| >. The RPM can be installed with the following |
| command after becoming root.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > rpm -i <downloaded rpm></PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PURPOSE" |
| >1.4. Purpose/Scope of the document</A |
| ></H3 |
| ><P |
| >This document is intended to be a "All in One" guide for programming with |
| ncurses and its sister libraries. We graduate from a simple "Hello World" |
| program to more complex form manipulation. No prior experience in ncurses is |
| assumed. The writing is informal, but a lot of detail is provided for |
| each of the examples.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ABOUTPROGRAMS" |
| >1.5. About the Programs</A |
| ></H3 |
| ><P |
| >All the programs in the document are available in zipped form |
| <A |
| HREF="http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/ncurses_programs.tar.gz" |
| TARGET="_top" |
| >here</A |
| >. Unzip and untar it. The directory structure looks like this.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >ncurses |
| | |
| |----> JustForFun -- just for fun programs |
| |----> basics -- basic programs |
| |----> demo -- output files go into this directory after make |
| | | |
| | |----> exe -- exe files of all example programs |
| |----> forms -- programs related to form library |
| |----> menus -- programs related to menus library |
| |----> panels -- programs related to panels library |
| |----> perl -- perl equivalents of the examples (contributed |
| | by Anuradha Ratnaweera) |
| |----> Makefile -- the top level Makefile |
| |----> README -- the top level README file. contains instructions |
| |----> COPYING -- copyright notice</PRE |
| ><P |
| >The individual directories contain the following files.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >Description of files in each directory |
| -------------------------------------- |
| JustForFun |
| | |
| |----> hanoi.c -- The Towers of Hanoi Solver |
| |----> life.c -- The Game of Life demo |
| |----> magic.c -- An Odd Order Magic Square builder |
| |----> queens.c -- The famous N-Queens Solver |
| |----> shuffle.c -- A fun game, if you have time to kill |
| |----> tt.c -- A very trivial typing tutor |
| |
| basics |
| | |
| |----> acs_vars.c -- ACS_ variables example |
| |----> hello_world.c -- Simple "Hello World" Program |
| |----> init_func_example.c -- Initialization functions example |
| |----> key_code.c -- Shows the scan code of the key pressed |
| |----> mouse_menu.c -- A menu accessible by mouse |
| |----> other_border.c -- Shows usage of other border functions apa |
| | -- rt from box() |
| |----> printw_example.c -- A very simple printw() example |
| |----> scanw_example.c -- A very simple getstr() example |
| |----> simple_attr.c -- A program that can print a c file with |
| | -- comments in attribute |
| |----> simple_color.c -- A simple example demonstrating colors |
| |----> simple_key.c -- A menu accessible with keyboard UP, DOWN |
| | -- arrows |
| |----> temp_leave.c -- Demonstrates temporarily leaving curses mode |
| |----> win_border.c -- Shows Creation of windows and borders |
| |----> with_chgat.c -- chgat() usage example |
| |
| forms |
| | |
| |----> form_attrib.c -- Usage of field attributes |
| |----> form_options.c -- Usage of field options |
| |----> form_simple.c -- A simple form example |
| |----> form_win.c -- Demo of windows associated with forms |
| |
| menus |
| | |
| |----> menu_attrib.c -- Usage of menu attributes |
| |----> menu_item_data.c -- Usage of item_name() etc.. functions |
| |----> menu_multi_column.c -- Creates multi columnar menus |
| |----> menu_scroll.c -- Demonstrates scrolling capability of menus |
| |----> menu_simple.c -- A simple menu accessed by arrow keys |
| |----> menu_toggle.c -- Creates multi valued menus and explains |
| | -- REQ_TOGGLE_ITEM |
| |----> menu_userptr.c -- Usage of user pointer |
| |----> menu_win.c -- Demo of windows associated with menus |
| |
| panels |
| | |
| |----> panel_browse.c -- Panel browsing through tab. Usage of user |
| | -- pointer |
| |----> panel_hide.c -- Hiding and Un hiding of panels |
| |----> panel_resize.c -- Moving and resizing of panels |
| |----> panel_simple.c -- A simple panel example |
| |
| perl |
| |----> 01-10.pl -- Perl equivalents of first ten example programs</PRE |
| ><P |
| >There is a top level Makefile included in the main directory. It builds all the |
| files and puts the ready-to-use exes in demo/exe directory. You can also |
| do selective make by going into the corresponding directory. Each directory |
| contains a README file explaining the purpose of each c file in the directory.</P |
| ><P |
| >For every example, I have included path name for the file relative to the |
| examples directory. </P |
| ><P |
| > If you prefer browsing individual programs, point your browser to |
| <A |
| HREF="http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/ncurses_programs/" |
| TARGET="_top" |
| >http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/ncurses_programs/</A |
| ></P |
| ><P |
| >All the programs are released under the same license that is used by ncurses |
| (MIT-style). This gives you the ability to do pretty much anything other than |
| claiming them as yours. Feel free to use them in your programs as appropriate.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="OTHERFORMATS" |
| >1.6. Other Formats of the document</A |
| ></H3 |
| ><P |
| >This howto is also availabe in various other formats on the tldp.org site. |
| Here are the links to other formats of this document.</P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="LISTFORMATS" |
| >1.6.1. Readily available formats from tldp.org</A |
| ></H4 |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><A |
| HREF="http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/pdf/NCURSES-Programming-HOWTO.pdf" |
| TARGET="_top" |
| >Acrobat PDF Format</A |
| ></P |
| ></LI |
| ><LI |
| ><P |
| ><A |
| HREF="http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/ps/NCURSES-Programming-HOWTO.ps.gz" |
| TARGET="_top" |
| >PostScript Format</A |
| ></P |
| ></LI |
| ><LI |
| ><P |
| ><A |
| HREF="http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html/NCURSES-Programming-HOWTO-html.tar.gz" |
| TARGET="_top" |
| >In Multiple HTML pages</A |
| ></P |
| ></LI |
| ><LI |
| ><P |
| ><A |
| HREF="http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html_single/NCURSES-Programming-HOWTO.html" |
| TARGET="_top" |
| >In One big HTML format</A |
| ></P |
| ></LI |
| ></UL |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="BUILDSOURCE" |
| >1.6.2. Building from source</A |
| ></H4 |
| ><P |
| >If above links are broken or if you want to experiment with sgml read on. |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > Get both the source and the tar,gzipped programs, available at |
| http://cvsview.tldp.org/index.cgi/LDP/howto/docbook/ |
| NCURSES-HOWTO/NCURSES-Programming-HOWTO.sgml |
| http://cvsview.tldp.org/index.cgi/LDP/howto/docbook/ |
| NCURSES-HOWTO/ncurses_programs.tar.gz |
| |
| Unzip ncurses_programs.tar.gz with |
| tar zxvf ncurses_programs.tar.gz |
| |
| Use jade to create various formats. For example if you just want to create |
| the multiple html files, you would use |
| jade -t sgml -i html -d <path to docbook html stylesheet> |
| NCURSES-Programming-HOWTO.sgml |
| to get pdf, first create a single html file of the HOWTO with |
| jade -t sgml -i html -d <path to docbook html stylesheet> -V nochunks |
| NCURSES-Programming-HOWTO.sgml > NCURSES-ONE-BIG-FILE.html |
| then use htmldoc to get pdf file with |
| htmldoc --size universal -t pdf --firstpage p1 -f <output file name.pdf> |
| NCURSES-ONE-BIG-FILE.html |
| for ps, you would use |
| htmldoc --size universal -t ps --firstpage p1 -f <output file name.ps> |
| NCURSES-ONE-BIG-FILE.html</PRE |
| ></P |
| ><P |
| >See <A |
| HREF="http://www.tldp.org/LDP/LDP-Author-Guide/" |
| TARGET="_top" |
| >LDP Author guide</A |
| > for more details. If all else failes, mail me at |
| <A |
| HREF="ppadala@gmail.com" |
| TARGET="_top" |
| >ppadala@gmail.com</A |
| ></P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="CREDITS" |
| >1.7. Credits</A |
| ></H3 |
| ><P |
| >I thank <A |
| HREF="mailto:sharath_1@usa.net" |
| TARGET="_top" |
| >Sharath</A |
| > and Emre Akbas for |
| helping me with few sections. The introduction was initially written by sharath. |
| I rewrote it with few excerpts taken from his initial work. Emre helped in |
| writing printw and scanw sections.</P |
| ><P |
| >Perl equivalents of the example programs are contributed by <A |
| HREF="mailto:Aratnaweera@virtusa.com" |
| TARGET="_top" |
| >Anuradha Ratnaweera</A |
| >. </P |
| ><P |
| >Then comes <A |
| HREF="mailto:parimi@ece.arizona.edu" |
| TARGET="_top" |
| >Ravi Parimi</A |
| >, my |
| dearest friend, who has been on this project before even one line was written. |
| He constantly bombarded me with suggestions and patiently reviewed the whole |
| text. He also checked each program on Linux and Solaris. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WISHLIST" |
| >1.8. Wish List</A |
| ></H3 |
| ><P |
| >This is the wish list, in the order of priority. If you have a wish or you want |
| to work on completing the wish, mail <A |
| HREF="mailto:ppadala@gmail.com" |
| TARGET="_top" |
| >me</A |
| >. </P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| >Add examples to last parts of forms section.</P |
| ></LI |
| ><LI |
| ><P |
| >Prepare a Demo showing all the programs and allow the user to browse through |
| description of each program. Let the user compile and see the program in action. |
| A dialog based interface is preferred.</P |
| ></LI |
| ><LI |
| ><P |
| >Add debug info. _tracef, _tracemouse stuff.</P |
| ></LI |
| ><LI |
| ><P |
| >Accessing termcap, terminfo using functions provided by ncurses |
| package.</P |
| ></LI |
| ><LI |
| ><P |
| >Working on two terminals simultaneously.</P |
| ></LI |
| ><LI |
| ><P |
| >Add more stuff to miscellaneous section.</P |
| ></LI |
| ></UL |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COPYRIGHT" |
| >1.9. Copyright</A |
| ></H3 |
| ><P |
| >Copyright © 2001 by Pradeep Padala. </P |
| ><P |
| >Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, distribute with |
| modifications, sublicense, and/or sell copies of the Software, and to permit |
| persons to whom the Software is furnished to do so, subject to the following |
| conditions:</P |
| ><P |
| >The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software.</P |
| ><P |
| >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR |
| IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</P |
| ><P |
| >Except as contained in this notice, the name(s) of the above copyright holders |
| shall not be used in advertising or otherwise to promote the sale, use or |
| other dealings in this Software without prior written authorization. </P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="HELLOWORLD" |
| >2. Hello World !!!</A |
| ></H2 |
| ><P |
| >Welcome to the world of curses. Before we plunge into the library and look into |
| its various features, let's write a simple program and say |
| hello to the world. </P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COMPILECURSES" |
| >2.1. Compiling With the NCURSES Library</A |
| ></H3 |
| ><P |
| >To use ncurses library functions, you have to include ncurses.h in your |
| programs. To link the |
| program with ncurses the flag -lncurses should be added.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > #include <ncurses.h> |
| . |
| . |
| . |
| |
| compile and link: gcc <program file> -lncurses</PRE |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BHW" |
| ></A |
| ><P |
| ><B |
| >Example 1. The Hello World !!! Program </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| int main() |
| { |
| initscr(); /* Start curses mode */ |
| printw("Hello World !!!"); /* Print Hello World */ |
| refresh(); /* Print it on to the real screen */ |
| getch(); /* Wait for user input */ |
| endwin(); /* End curses mode */ |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="DISSECTION" |
| >2.2. Dissection</A |
| ></H3 |
| ><P |
| > |
| The above program prints "Hello World !!!" to the screen and exits. This |
| program shows how to initialize curses and do screen manipulation and |
| end curses mode. Let's dissect it line by line. </P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="ABOUT-INITSCR" |
| >2.2.1. About initscr()</A |
| ></H4 |
| ><P |
| >The function initscr() initializes the terminal in curses mode. In some |
| implementations, it clears the screen and presents a blank screen. To do any |
| screen manipulation using curses package this has to be called first. This |
| function initializes the curses system and allocates memory for our present |
| window (called <TT |
| CLASS="LITERAL" |
| >stdscr</TT |
| >) and some other data-structures. Under extreme |
| cases this function might fail due to insufficient memory to allocate memory |
| for curses library's data structures. </P |
| ><P |
| > |
| After this is done, we can do a variety of initializations to customize |
| our curses settings. These details will be explained <A |
| HREF="#INIT" |
| >later </A |
| >.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="MYST-REFRESH" |
| >2.2.2. The mysterious refresh()</A |
| ></H4 |
| ><P |
| >The next line printw prints the string "Hello World !!!" on to the screen. This |
| function is analogous to normal printf in all respects except that it prints |
| the data on a window called stdscr at the current (y,x) co-ordinates. Since our |
| present co-ordinates are at 0,0 the string is printed at the left hand corner |
| of the window.</P |
| ><P |
| >This brings us to that mysterious refresh(). Well, when we called printw |
| the data is actually written to an imaginary window, which is not updated |
| on the screen yet. The job of printw is to update a few flags |
| and data structures and write the data to a buffer corresponding to stdscr. |
| In order to show it on the screen, we need to call refresh() and tell the |
| curses system to dump the contents on the screen.</P |
| ><P |
| >The philosophy behind all this is to allow the programmer to do multiple updates |
| on the imaginary screen or windows and do a refresh once all his screen update |
| is done. refresh() checks the window and updates only the portion which has been |
| changed. This improves performance and offers greater flexibility too. But, it is |
| sometimes frustrating to beginners. A common mistake committed by beginners is |
| to forget to call refresh() after they did some update through printw() class of |
| functions. I still forget to add it sometimes :-) </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="ABOUT-ENDWIN" |
| >2.2.3. About endwin()</A |
| ></H4 |
| ><P |
| >And finally don't forget to end the curses mode. Otherwise your terminal might |
| behave strangely after the program quits. endwin() frees the memory taken by |
| curses sub-system and its data structures and puts the terminal in normal |
| mode. This function must be called after you are done with the curses mode. </P |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="GORY" |
| >3. The Gory Details</A |
| ></H2 |
| ><P |
| >Now that we have seen how to write a simple curses program let's get into the |
| details. There are many functions that help customize what you see on screen and |
| many features which can be put to full use. </P |
| ><P |
| >Here we go...</P |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="INIT" |
| >4. Initialization</A |
| ></H2 |
| ><P |
| >We now know that to initialize curses system the function initscr() has to be |
| called. There are functions which can be called after this initialization to |
| customize our curses session. We may ask the curses system to set the terminal |
| in raw mode or initialize color or initialize the mouse etc.. Let's discuss some |
| of the functions that are normally called immediately after initscr();</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ABOUTINIT" |
| >4.1. Initialization functions</A |
| ></H3 |
| ><P |
| > </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="RAWCBREAK" |
| >4.2. raw() and cbreak()</A |
| ></H3 |
| ><P |
| >Normally the terminal driver buffers the characters a user types until a new |
| line or carriage return is encountered. But most programs require that the |
| characters be available as soon as the user types them. The above two functions |
| are used to disable line buffering. The difference between these two functions |
| is in the way control characters like suspend (CTRL-Z), interrupt and quit |
| (CTRL-C) are passed to the program. In the raw() mode these characters are |
| directly passed to the program without generating a signal. In the |
| <TT |
| CLASS="LITERAL" |
| >cbreak()</TT |
| > mode these control characters are |
| interpreted as any other character by the terminal driver. I personally prefer |
| to use raw() as I can exercise greater control over what the user does.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ECHONOECHO" |
| >4.3. echo() and noecho()</A |
| ></H3 |
| ><P |
| > |
| These functions control the echoing of characters typed by the user to the |
| terminal. <TT |
| CLASS="LITERAL" |
| >noecho()</TT |
| > switches off echoing. The |
| reason you might want to do this is to gain more control over echoing or to |
| suppress unnecessary echoing while taking input from the user through the |
| getch() etc. functions. Most of the interactive programs call |
| <TT |
| CLASS="LITERAL" |
| >noecho()</TT |
| > at initialization and do the echoing |
| of characters in a controlled manner. It gives the programmer the flexibility |
| of echoing characters at any place in the window without updating current (y,x) |
| co-ordinates. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="KEYPAD" |
| >4.4. keypad()</A |
| ></H3 |
| ><P |
| >This is my favorite initialization function. It enables the reading of function |
| keys like F1, F2, arrow keys etc. Almost every interactive program enables this, |
| as arrow keys are a major part of any User Interface. Do |
| <TT |
| CLASS="LITERAL" |
| >keypad(stdscr, TRUE) </TT |
| > to enable this feature |
| for the regular screen (stdscr). You will learn more about key management in |
| later sections of this document.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="HALFDELAY" |
| >4.5. halfdelay()</A |
| ></H3 |
| ><P |
| >This function, though not used very often, is a useful one at times. |
| halfdelay()is called to enable the half-delay mode, which is similar to the |
| cbreak() mode in that characters typed are immediately available to program. |
| However, it waits for 'X' tenths of a second for input and then returns ERR, if |
| no input is available. 'X' is the timeout value passed to the function |
| halfdelay(). This function is useful when you want to ask the user for input, |
| and if he doesn't respond with in certain time, we can do some thing else. One |
| possible example is a timeout at the password prompt. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MISCINIT" |
| >4.6. Miscellaneous Initialization functions</A |
| ></H3 |
| ><P |
| >There are few more functions which are called at initialization to |
| customize curses behavior. They are not used as extensively as those mentioned |
| above. Some of them are explained where appropriate.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="INITEX" |
| >4.7. An Example</A |
| ></H3 |
| ><P |
| >Let's write a program which will clarify the usage of these functions.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BINFU" |
| ></A |
| ><P |
| ><B |
| >Example 2. Initialization Function Usage example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| int main() |
| { int ch; |
| |
| initscr(); /* Start curses mode */ |
| raw(); /* Line buffering disabled */ |
| keypad(stdscr, TRUE); /* We get F1, F2 etc.. */ |
| noecho(); /* Don't echo() while we do getch */ |
| |
| printw("Type any character to see it in bold\n"); |
| ch = getch(); /* If raw() hadn't been called |
| * we have to press enter before it |
| * gets to the program */ |
| if(ch == KEY_F(1)) /* Without keypad enabled this will */ |
| printw("F1 Key pressed");/* not get to us either */ |
| /* Without noecho() some ugly escape |
| * charachters might have been printed |
| * on screen */ |
| else |
| { printw("The pressed key is "); |
| attron(A_BOLD); |
| printw("%c", ch); |
| attroff(A_BOLD); |
| } |
| refresh(); /* Print it on to the real screen */ |
| getch(); /* Wait for user input */ |
| endwin(); /* End curses mode */ |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This program is self-explanatory. But I used functions which aren't explained |
| yet. The function <TT |
| CLASS="LITERAL" |
| >getch()</TT |
| > is used to get a |
| character from user. It is equivalent to normal |
| <TT |
| CLASS="LITERAL" |
| >getchar()</TT |
| > except that we can disable the line |
| buffering to avoid <enter> after input. Look for more about |
| <TT |
| CLASS="LITERAL" |
| >getch()</TT |
| >and reading keys in the <A |
| HREF="#KEYS" |
| > key management section </A |
| >. The functions attron and attroff |
| are used to switch some attributes on and off respectively. In the example I |
| used them to print the character in bold. These functions are explained in detail |
| later.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="AWORDWINDOWS" |
| >5. A Word about Windows</A |
| ></H2 |
| ><P |
| > |
| Before we plunge into the myriad ncurses functions, let me clear few things |
| about windows. Windows are explained in detail in following <A |
| HREF="#WINDOWS" |
| > sections </A |
| ></P |
| ><P |
| >A Window is an imaginary screen defined by curses system. A window does not mean |
| a bordered window which you usually see on Win9X platforms. When curses is |
| initialized, it creates a default window named |
| <TT |
| CLASS="LITERAL" |
| >stdscr</TT |
| > which represents your 80x25 (or the size |
| of window in which you are running) screen. If you are doing simple tasks like |
| printing few strings, reading input etc., you can safely use this single window |
| for all of your purposes. You can also create windows and call functions which |
| explicitly work on the specified window.</P |
| ><P |
| >For example, if you call</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > printw("Hi There !!!"); |
| refresh();</PRE |
| ><P |
| >It prints the string on stdscr at the present cursor position. Similarly the |
| call to refresh(), works on stdscr only. </P |
| ><P |
| >Say you have created <A |
| HREF="#WINDOWS" |
| >windows</A |
| > then you have to |
| call a function with a 'w' added to the usual function.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > wprintw(win, "Hi There !!!"); |
| wrefresh(win);</PRE |
| ><P |
| >As you will see in the rest of the document, naming of functions follow the |
| same convention. For each function there usually are three more functions.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > printw(string); /* Print on stdscr at present cursor position */ |
| mvprintw(y, x, string);/* Move to (y, x) then print string */ |
| wprintw(win, string); /* Print on window win at present cursor position */ |
| /* in the window */ |
| mvwprintw(win, y, x, string); /* Move to (y, x) relative to window */ |
| /* co-ordinates and then print */</PRE |
| ><P |
| >Usually the w-less functions are macros which expand to corresponding w-function |
| with stdscr as the window parameter.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="PRINTW" |
| >6. Output functions</A |
| ></H2 |
| ><P |
| >I guess you can't wait any more to see some action. Back to our odyssey of |
| curses functions. Now that curses is initialized, let's interact with |
| world.</P |
| ><P |
| >There are three classes of functions which you can use to do output on screen. |
| <P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >addch() class: Print single character with attributes </P |
| ></LI |
| ><LI |
| ><P |
| >printw() class: Print formatted output similar to printf()</P |
| ></LI |
| ><LI |
| ><P |
| >addstr() class: Print strings</P |
| ></LI |
| ></OL |
| ></P |
| ><P |
| >These functions can be used interchangeably and it's a matter of style as to |
| which class is used. Let's see each one in detail.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ADDCHCLASS" |
| >6.1. addch() class of functions</A |
| ></H3 |
| ><P |
| >These functions put a single character into the current cursor location and |
| advance the position of the cursor. You can give the character to be printed but |
| they usually are used to print a character with some attributes. Attributes are |
| explained in detail in later <A |
| HREF="#ATTRIB" |
| > sections </A |
| > of the |
| document. If a character is associated with an attribute(bold, reverse video |
| etc.), when curses prints the character, it is printed in that attribute.</P |
| ><P |
| >In order to combine a character with some attributes, you have two options:</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| >By OR'ing a single character with the desired attribute macros. These attribute |
| macros could be found in the header file |
| <TT |
| CLASS="LITERAL" |
| >ncurses.h</TT |
| >. For example, you want to print a |
| character ch(of type char) bold and underlined, you would call addch() as below. |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > addch(ch | A_BOLD | A_UNDERLINE);</PRE |
| ></P |
| ></LI |
| ><LI |
| ><P |
| >By using functions like <TT |
| CLASS="LITERAL" |
| >attrset(),attron(),attroff()</TT |
| >. These functions are explained in the <A |
| HREF="#ATTRIB" |
| >Attributes</A |
| > section. Briefly, they manipulate the current attributes of |
| the given window. Once set, the character printed in the window are associated |
| with the attributes until it is turned off.</P |
| ></LI |
| ></UL |
| ><P |
| >Additionally, <TT |
| CLASS="LITERAL" |
| >curses</TT |
| > provides some special |
| characters for character-based graphics. You can draw tables, horizontal or |
| vertical lines, etc. You can find all avaliable characters in the header file |
| <TT |
| CLASS="LITERAL" |
| >ncurses.h</TT |
| >. Try looking for macros beginning |
| with <TT |
| CLASS="LITERAL" |
| >ACS_</TT |
| > in this file. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="AEN298" |
| >6.2. mvaddch(), waddch() and mvwaddch()</A |
| ></H3 |
| ><P |
| ><TT |
| CLASS="LITERAL" |
| >mvaddch()</TT |
| > is used to move the cursor to a |
| given point, and then print. Thus, the calls: |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > move(row,col); /* moves the cursor to row<SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >th</I |
| ></SPAN |
| > row and col<SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >th</I |
| ></SPAN |
| > column */ |
| addch(ch);</PRE |
| > |
| can be replaced by |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > mvaddch(row,col,ch);</PRE |
| ></P |
| ><P |
| ><TT |
| CLASS="LITERAL" |
| >waddch()</TT |
| > is similar to |
| <TT |
| CLASS="LITERAL" |
| >addch()</TT |
| >, except that it adds a character into |
| the given window. (Note that <TT |
| CLASS="LITERAL" |
| >addch()</TT |
| > adds a |
| character into the window <TT |
| CLASS="LITERAL" |
| >stdscr</TT |
| >.)</P |
| ><P |
| >In a similar fashion <TT |
| CLASS="LITERAL" |
| >mvwaddch()</TT |
| > function is |
| used to add a character into the given window at the given coordinates.</P |
| ><P |
| >Now, we are familiar with the basic output function |
| <TT |
| CLASS="LITERAL" |
| >addch()</TT |
| >. But, if we want to print a string, it |
| would be very annoying to print it character by character. Fortunately, |
| <TT |
| CLASS="LITERAL" |
| >ncurses</TT |
| > provides <TT |
| CLASS="LITERAL" |
| >printf</TT |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >-like</I |
| ></SPAN |
| > or |
| <TT |
| CLASS="LITERAL" |
| >puts</TT |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >-like</I |
| ></SPAN |
| > functions.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PRINTWCLASS" |
| >6.3. printw() class of functions</A |
| ></H3 |
| ><P |
| >These functions are similar to <TT |
| CLASS="LITERAL" |
| >printf()</TT |
| > with |
| the added capability of printing at any position on the screen. </P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="PRINTWMVPRINTW" |
| >6.3.1. printw() and mvprintw</A |
| ></H4 |
| ><P |
| >These two functions work much like <TT |
| CLASS="LITERAL" |
| >printf()</TT |
| >. |
| <TT |
| CLASS="LITERAL" |
| >mvprintw()</TT |
| > can be used to move the cursor to a |
| position and then print. If you want to move the cursor first and then print |
| using <TT |
| CLASS="LITERAL" |
| >printw()</TT |
| > function, use |
| <TT |
| CLASS="LITERAL" |
| >move() </TT |
| > first and then use |
| <TT |
| CLASS="LITERAL" |
| >printw()</TT |
| > though I see no point why one should |
| avoid using <TT |
| CLASS="LITERAL" |
| >mvprintw()</TT |
| >, you have the |
| flexibility to manipulate. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="WPRINTWMVWPRINTW" |
| >6.3.2. wprintw() and mvwprintw</A |
| ></H4 |
| ><P |
| >These two functions are similar to above two except that they print in the |
| corresponding window given as argument. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="VWPRINTW" |
| >6.3.3. vwprintw()</A |
| ></H4 |
| ><P |
| >This function is similar to <TT |
| CLASS="LITERAL" |
| >vprintf()</TT |
| >. This can |
| be used when variable number of arguments are to be printed.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="SIMPLEPRINTWEX" |
| >6.3.4. A Simple printw example</A |
| ></H4 |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BPREX" |
| ></A |
| ><P |
| ><B |
| >Example 3. A Simple printw example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> /* ncurses.h includes stdio.h */ |
| #include <string.h> |
| |
| int main() |
| { |
| char mesg[]="Just a string"; /* message to be appeared on the screen */ |
| int row,col; /* to store the number of rows and * |
| * the number of colums of the screen */ |
| initscr(); /* start the curses mode */ |
| getmaxyx(stdscr,row,col); /* get the number of rows and columns */ |
| mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg); |
| /* print the message at the center of the screen */ |
| mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col); |
| printw("Try resizing your window(if possible) and then run this program again"); |
| refresh(); |
| getch(); |
| endwin(); |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Above program demonstrates how easy it is to use <TT |
| CLASS="LITERAL" |
| >printw</TT |
| >. You just feed the coordinates and the message to be appeared |
| on the screen, then it does what you want.</P |
| ><P |
| >The above program introduces us to a new function |
| <TT |
| CLASS="LITERAL" |
| >getmaxyx()</TT |
| >, a macro defined in |
| <TT |
| CLASS="LITERAL" |
| >ncurses.h</TT |
| >. It gives the number of columns and |
| the number of rows in a given window. |
| <TT |
| CLASS="LITERAL" |
| >getmaxyx()</TT |
| > does this by updating the variables |
| given to it. Since <TT |
| CLASS="LITERAL" |
| >getmaxyx()</TT |
| > is not a function |
| we don't pass pointers to it, we just give two integer variables. </P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ADDSTRCLASS" |
| >6.4. addstr() class of functions</A |
| ></H3 |
| ><P |
| ><TT |
| CLASS="LITERAL" |
| >addstr()</TT |
| > is used to put a character string into |
| a given window. This function is similar to calling |
| <TT |
| CLASS="LITERAL" |
| >addch()</TT |
| > once for each character in a given |
| string. This is true for all output functions. There are other functions from |
| this family such as <TT |
| CLASS="LITERAL" |
| >mvaddstr(),mvwaddstr()</TT |
| > and |
| <TT |
| CLASS="LITERAL" |
| >waddstr()</TT |
| >, which obey the naming convention of |
| curses.(e.g. mvaddstr() is similar to the respective calls move() and then |
| addstr().) Another function of this family is addnstr(), which takes an integer |
| parameter(say n) additionally. This function puts at most n characters into the |
| screen. If n is negative, then the entire string will be added. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ACAUTION" |
| >6.5. A word of caution</A |
| ></H3 |
| ><P |
| >All these functions take y co-ordinate first and then x in their arguments. |
| A common mistake by beginners is to pass x,y in that order. If you are |
| doing too many manipulations of (y,x) co-ordinates, think of dividing the |
| screen into windows and manipulate each one separately. Windows are explained |
| in the <A |
| HREF="#WINDOWS" |
| > windows </A |
| > section.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="SCANW" |
| >7. Input functions</A |
| ></H2 |
| ><P |
| >Well, printing without taking input, is boring. Let's see functions which |
| allow us to get input from user. These functions also can be divided into |
| three categories.</P |
| ><P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >getch() class: Get a character</P |
| ></LI |
| ><LI |
| ><P |
| >scanw() class: Get formatted input</P |
| ></LI |
| ><LI |
| ><P |
| >getstr() class: Get strings</P |
| ></LI |
| ></OL |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GETCHCLASS" |
| >7.1. getch() class of functions</A |
| ></H3 |
| ><P |
| >These functions read a single character from the terminal. But there are several |
| subtle facts to consider. For example if you don't use the function cbreak(), |
| curses will not read your input characters contiguously but will begin read them |
| only after a new line or an EOF is encountered. In order to avoid this, the |
| cbreak() function must used so that characters are immediately available to your |
| program. Another widely used function is noecho(). As the name suggests, when |
| this function is set (used), the characters that are keyed in by the user will |
| not show up on the screen. The two functions cbreak() and noecho() are typical |
| examples of key management. Functions of this genre are explained in the |
| <A |
| HREF="#KEYS" |
| >key management section </A |
| >.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="SCANWCLASS" |
| >7.2. scanw() class of functions</A |
| ></H3 |
| ><P |
| >These functions are similar to <TT |
| CLASS="LITERAL" |
| >scanf()</TT |
| > with the |
| added capability of getting the input from any location on the screen.</P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="SCANWMVSCANW" |
| >7.2.1. scanw() and mvscanw</A |
| ></H4 |
| ><P |
| >The usage of these functions is similar to that of |
| <TT |
| CLASS="LITERAL" |
| >sscanf()</TT |
| >, where the line to be scanned is |
| provided by <TT |
| CLASS="LITERAL" |
| >wgetstr()</TT |
| > function. That is, these |
| functions call to <TT |
| CLASS="LITERAL" |
| >wgetstr()</TT |
| > function(explained |
| below) and uses the resulting line for a scan. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="WSCANWMVWSCANW" |
| >7.2.2. wscanw() and mvwscanw()</A |
| ></H4 |
| ><P |
| >These are similar to above two functions except that they read from a window, |
| which is supplied as one of the arguments to these functions. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="VWSCANW" |
| >7.2.3. vwscanw()</A |
| ></H4 |
| ><P |
| >This function is similar to <TT |
| CLASS="LITERAL" |
| >vscanf()</TT |
| >. This can |
| be used when a variable number of arguments are to be scanned.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GETSTRCLASS" |
| >7.3. getstr() class of functions</A |
| ></H3 |
| ><P |
| >These functions are used to get strings from the terminal. In essence, this |
| function performs the same task as would be achieved by a series of calls to |
| <TT |
| CLASS="LITERAL" |
| >getch()</TT |
| > until a newline, carriage return, or |
| end-of-file is received. The resulting string of characters are pointed to by |
| <TT |
| CLASS="LITERAL" |
| >str</TT |
| >, which is a character pointer provided by |
| the user.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GETSTREX" |
| >7.4. Some examples</A |
| ></H3 |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BSCEX" |
| ></A |
| ><P |
| ><B |
| >Example 4. A Simple scanw example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> /* ncurses.h includes stdio.h */ |
| #include <string.h> |
| |
| int main() |
| { |
| char mesg[]="Enter a string: "; /* message to be appeared on the screen */ |
| char str[80]; |
| int row,col; /* to store the number of rows and * |
| * the number of colums of the screen */ |
| initscr(); /* start the curses mode */ |
| getmaxyx(stdscr,row,col); /* get the number of rows and columns */ |
| mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg); |
| /* print the message at the center of the screen */ |
| getstr(str); |
| mvprintw(LINES - 2, 0, "You Entered: %s", str); |
| getch(); |
| endwin(); |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="ATTRIB" |
| >8. Attributes</A |
| ></H2 |
| ><P |
| >We have seen an example of how attributes can be used to print characters with |
| some special effects. Attributes, when set prudently, can present information in |
| an easy, understandable manner. The following program takes a C file as input |
| and prints the file with comments in bold. Scan through the code. </P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BSIAT" |
| ></A |
| ><P |
| ><B |
| >Example 5. A Simple Attributes example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >/* pager functionality by Joseph Spainhour" <spainhou@bellsouth.net> */ |
| #include <ncurses.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char *argv[]) |
| { |
| int ch, prev, row, col; |
| prev = EOF; |
| FILE *fp; |
| int y, x; |
| |
| if(argc != 2) |
| { |
| printf("Usage: %s <a c file name>\n", argv[0]); |
| exit(1); |
| } |
| fp = fopen(argv[1], "r"); |
| if(fp == NULL) |
| { |
| perror("Cannot open input file"); |
| exit(1); |
| } |
| initscr(); /* Start curses mode */ |
| getmaxyx(stdscr, row, col); /* find the boundaries of the screeen */ |
| while((ch = fgetc(fp)) != EOF) /* read the file till we reach the end */ |
| { |
| getyx(stdscr, y, x); /* get the current curser position */ |
| if(y == (row - 1)) /* are we are at the end of the screen */ |
| { |
| printw("<-Press Any Key->"); /* tell the user to press a key */ |
| getch(); |
| clear(); /* clear the screen */ |
| move(0, 0); /* start at the beginning of the screen */ |
| } |
| if(prev == '/' && ch == '*') /* If it is / and * then only |
| * switch bold on */ |
| { |
| attron(A_BOLD); /* cut bold on */ |
| getyx(stdscr, y, x); /* get the current curser position */ |
| move(y, x - 1); /* back up one space */ |
| printw("%c%c", '/', ch); /* The actual printing is done here */ |
| } |
| else |
| printw("%c", ch); |
| refresh(); |
| if(prev == '*' && ch == '/') |
| attroff(A_BOLD); /* Switch it off once we got * |
| * and then / */ |
| prev = ch; |
| } |
| endwin(); /* End curses mode */ |
| fclose(fp); |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| > |
| Don't worry about all those initialization and other crap. Concentrate on |
| the while loop. It reads each character in the file and searches for the |
| pattern /*. Once it spots the pattern, it switches the BOLD attribute on with |
| <TT |
| CLASS="LITERAL" |
| > attron()</TT |
| > . When we get the pattern */ it is |
| switched off by <TT |
| CLASS="LITERAL" |
| > attroff()</TT |
| > .</P |
| ><P |
| > |
| The above program also introduces us to two useful functions |
| <TT |
| CLASS="LITERAL" |
| >getyx() </TT |
| > and |
| <TT |
| CLASS="LITERAL" |
| >move()</TT |
| >. The first function gets the |
| co-ordinates of the present cursor into the variables y, x. Since getyx() is a |
| macro we don't have to pass pointers to variables. The function |
| <TT |
| CLASS="LITERAL" |
| >move()</TT |
| > moves the cursor to the co-ordinates |
| given to it. </P |
| ><P |
| > |
| The above program is really a simple one which doesn't do much. On these lines |
| one could write a more useful program which reads a C file, parses it and prints |
| it in different colors. One could even extend it to other languages as well.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ATTRIBDETAILS" |
| >8.1. The details</A |
| ></H3 |
| ><P |
| >Let's get into more details of attributes. The functions <TT |
| CLASS="LITERAL" |
| >attron(), attroff(), attrset() </TT |
| >, and their sister functions |
| <TT |
| CLASS="LITERAL" |
| > attr_get()</TT |
| > etc.. can be used to switch |
| attributes on/off , get attributes and produce a colorful display.</P |
| ><P |
| >The functions attron and attroff take a bit-mask of attributes and switch them |
| on or off, respectively. The following video attributes, which are defined in |
| <curses.h> can be passed to these functions. </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > |
| A_NORMAL Normal display (no highlight) |
| A_STANDOUT Best highlighting mode of the terminal. |
| A_UNDERLINE Underlining |
| A_REVERSE Reverse video |
| A_BLINK Blinking |
| A_DIM Half bright |
| A_BOLD Extra bright or bold |
| A_PROTECT Protected mode |
| A_INVIS Invisible or blank mode |
| A_ALTCHARSET Alternate character set |
| A_CHARTEXT Bit-mask to extract a character |
| COLOR_PAIR(n) Color-pair number n |
| </PRE |
| ><P |
| > |
| The last one is the most colorful one :-) Colors are explained in the |
| <A |
| HREF="#color" |
| TARGET="_top" |
| >next sections</A |
| >.</P |
| ><P |
| >We can OR(|) any number of above attributes to get a combined effect. If you |
| wanted reverse video with blinking characters you can use</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > attron(A_REVERSE | A_BLINK);</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ATTRONVSATTRSET" |
| >8.2. attron() vs attrset()</A |
| ></H3 |
| ><P |
| >Then what is the difference between attron() and attrset()? attrset sets the |
| attributes of window whereas attron just switches on the attribute given to it. |
| So attrset() fully overrides whatever attributes the window previously had and |
| sets it to the new attribute(s). Similarly attroff() just switches off the |
| attribute(s) given to it as an argument. This gives us the flexibility of |
| managing attributes easily.But if you use them carelessly you may loose track of |
| what attributes the window has and garble the display. This is especially true |
| while managing menus with colors and highlighting. So decide on a consistent |
| policy and stick to it. You can always use <TT |
| CLASS="LITERAL" |
| > standend()</TT |
| > which is equivalent to <TT |
| CLASS="LITERAL" |
| > attrset(A_NORMAL)</TT |
| > which turns off all attributes and brings you to normal mode.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ATTR_GET" |
| >8.3. attr_get()</A |
| ></H3 |
| ><P |
| > The function attr_get() gets the current attributes and color pair of the |
| window. Though we might not use this as often as the above functions, this is |
| useful in scanning areas of screen. Say we wanted to do some complex update on |
| screen and we are not sure what attribute each character is associated with. |
| Then this function can be used with either attrset or attron to produce the |
| desired effect. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ATTR_FUNCS" |
| >8.4. attr_ functions</A |
| ></H3 |
| ><P |
| >There are series of functions like attr_set(), attr_on etc.. These are similar |
| to above functions except that they take parameters of type |
| <TT |
| CLASS="LITERAL" |
| >attr_t</TT |
| >.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WATTRFUNCS" |
| >8.5. wattr functions</A |
| ></H3 |
| ><P |
| >For each of the above functions we have a corresponding function with 'w' which |
| operates on a particular window. The above functions operate on stdscr. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="CHGAT" |
| >8.6. chgat() functions</A |
| ></H3 |
| ><P |
| >The function chgat() is listed in the end of the man page curs_attr. It actually |
| is a useful one. This function can be used to set attributes for a group of |
| characters without moving. I mean it !!! without moving the cursor :-) It |
| changes the attributes of a given number of characters starting at the current |
| cursor location.</P |
| ><P |
| >We can give -1 as the character count to update till end of line. If you want to |
| change attributes of characters from current position to end of line, just use |
| this.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > chgat(-1, A_REVERSE, 0, NULL);</PRE |
| ><P |
| > |
| This function is useful when changing attributes for characters that are |
| already on the screen. Move to the character from which you want to change and |
| change the attribute. </P |
| ><P |
| >Other functions wchgat(), mvchgat(), wchgat() behave similarly except that the w |
| functions operate on the particular window. The mv functions first move the |
| cursor then perform the work given to them. Actually chgat is a macro which is |
| replaced by a wchgat() with stdscr as the window. Most of the "w-less" functions |
| are macros.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BWICH" |
| ></A |
| ><P |
| ><B |
| >Example 6. Chgat() Usage example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| int main(int argc, char *argv[]) |
| { initscr(); /* Start curses mode */ |
| start_color(); /* Start color functionality */ |
| |
| init_pair(1, COLOR_CYAN, COLOR_BLACK); |
| printw("A Big string which i didn't care to type fully "); |
| mvchgat(0, 0, -1, A_BLINK, 1, NULL); |
| /* |
| * First two parameters specify the position at which to start |
| * Third parameter number of characters to update. -1 means till |
| * end of line |
| * Forth parameter is the normal attribute you wanted to give |
| * to the charcter |
| * Fifth is the color index. It is the index given during init_pair() |
| * use 0 if you didn't want color |
| * Sixth one is always NULL |
| */ |
| refresh(); |
| getch(); |
| endwin(); /* End curses mode */ |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This example also introduces us to the color world of curses. Colors will be |
| explained in detail later. Use 0 for no color.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="WINDOWS" |
| >9. Windows</A |
| ></H2 |
| ><P |
| >Windows form the most important concept in curses. You have seen the standard |
| window stdscr above where all the functions implicitly operated on this window. |
| Now to make design even a simplest GUI, you need to resort to windows. The main |
| reason you may want to use windows is to manipulate parts of the screen |
| separately, for better efficiency, by updating only the windows that need to be |
| changed and for a better design. I would say the last reason is the most |
| important in going for windows. You should always strive for a better and |
| easy-to-manage design in your programs. If you are writing big, complex GUIs |
| this is of pivotal importance before you start doing anything.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WINDOWBASICS" |
| >9.1. The basics</A |
| ></H3 |
| ><P |
| >A Window can be created by calling the function |
| <TT |
| CLASS="LITERAL" |
| >newwin()</TT |
| >. It doesn't create any thing on the |
| screen actually. It allocates memory for a structure to manipulate the window |
| and updates the structure with data regarding the window like it's size, beginy, |
| beginx etc.. Hence in curses, a window is just an abstraction of an imaginary |
| window, which can be manipulated independent of other parts of screen. The |
| function newwin() returns a pointer to structure WINDOW, which can be passed to |
| window related functions like wprintw() etc.. Finally the window can be |
| destroyed with delwin(). It will deallocate the memory associated with the |
| window structure.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="LETBEWINDOW" |
| >9.2. Let there be a Window !!!</A |
| ></H3 |
| ><P |
| >What fun is it, if a window is created and we can't see it. So the fun part |
| begins by displaying the window. The function |
| <TT |
| CLASS="LITERAL" |
| >box()</TT |
| > can be used to draw a border around the |
| window. Let's explore these functions in more detail in this example.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BWIBO" |
| ></A |
| ><P |
| ><B |
| >Example 7. Window Border example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| |
| WINDOW *create_newwin(int height, int width, int starty, int startx); |
| void destroy_win(WINDOW *local_win); |
| |
| int main(int argc, char *argv[]) |
| { WINDOW *my_win; |
| int startx, starty, width, height; |
| int ch; |
| |
| initscr(); /* Start curses mode */ |
| cbreak(); /* Line buffering disabled, Pass on |
| * everty thing to me */ |
| keypad(stdscr, TRUE); /* I need that nifty F1 */ |
| |
| height = 3; |
| width = 10; |
| starty = (LINES - height) / 2; /* Calculating for a center placement */ |
| startx = (COLS - width) / 2; /* of the window */ |
| printw("Press F1 to exit"); |
| refresh(); |
| my_win = create_newwin(height, width, starty, startx); |
| |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_LEFT: |
| destroy_win(my_win); |
| my_win = create_newwin(height, width, starty,--startx); |
| break; |
| case KEY_RIGHT: |
| destroy_win(my_win); |
| my_win = create_newwin(height, width, starty,++startx); |
| break; |
| case KEY_UP: |
| destroy_win(my_win); |
| my_win = create_newwin(height, width, --starty,startx); |
| break; |
| case KEY_DOWN: |
| destroy_win(my_win); |
| my_win = create_newwin(height, width, ++starty,startx); |
| break; |
| } |
| } |
| |
| endwin(); /* End curses mode */ |
| return 0; |
| } |
| |
| WINDOW *create_newwin(int height, int width, int starty, int startx) |
| { WINDOW *local_win; |
| |
| local_win = newwin(height, width, starty, startx); |
| box(local_win, 0 , 0); /* 0, 0 gives default characters |
| * for the vertical and horizontal |
| * lines */ |
| wrefresh(local_win); /* Show that box */ |
| |
| return local_win; |
| } |
| |
| void destroy_win(WINDOW *local_win) |
| { |
| /* box(local_win, ' ', ' '); : This won't produce the desired |
| * result of erasing the window. It will leave it's four corners |
| * and so an ugly remnant of window. |
| */ |
| wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' '); |
| /* The parameters taken are |
| * 1. win: the window on which to operate |
| * 2. ls: character to be used for the left side of the window |
| * 3. rs: character to be used for the right side of the window |
| * 4. ts: character to be used for the top side of the window |
| * 5. bs: character to be used for the bottom side of the window |
| * 6. tl: character to be used for the top left corner of the window |
| * 7. tr: character to be used for the top right corner of the window |
| * 8. bl: character to be used for the bottom left corner of the window |
| * 9. br: character to be used for the bottom right corner of the window |
| */ |
| wrefresh(local_win); |
| delwin(local_win); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="BORDEREXEXPL" |
| >9.3. Explanation</A |
| ></H3 |
| ><P |
| >Don't scream. I know it's a big example. But I have to explain some important |
| things here :-). This program creates a rectangular window that can be moved |
| with left, right, up, down arrow keys. It repeatedly creates and destroys |
| windows as user press a key. Don't go beyond the screen limits. Checking for |
| those limits is left as an exercise for the reader. Let's dissect it by line by line.</P |
| ><P |
| >The <TT |
| CLASS="LITERAL" |
| >create_newwin()</TT |
| > function creates a window |
| with <TT |
| CLASS="LITERAL" |
| >newwin() </TT |
| > and displays a border around it |
| with box. The function <TT |
| CLASS="LITERAL" |
| > destroy_win()</TT |
| > first |
| erases the window from screen by painting a border with ' ' character and then |
| calling <TT |
| CLASS="LITERAL" |
| >delwin()</TT |
| > to deallocate memory related |
| to it. Depending on the key the user presses, starty or startx is changed and a |
| new window is created.</P |
| ><P |
| >In the destroy_win, as you can see, I used wborder instead of box. The reason is |
| written in the comments (You missed it. I know. Read the code :-)). wborder |
| draws a border around the window with the characters given to it as the 4 corner |
| points and the 4 lines. To put it clearly, if you have called wborder as below: |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > wborder(win, '|', '|', '-', '-', '+', '+', '+', '+');</PRE |
| ></P |
| ><P |
| >it produces some thing like </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > +------------+ |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| +------------+</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="OTHERSTUFF" |
| >9.4. The other stuff in the example</A |
| ></H3 |
| ><P |
| >You can also see in the above examples, that I have used the variables COLS, |
| LINES which are initialized to the screen sizes after initscr(). They can be |
| useful in finding screen dimensions and finding the center co-ordinate of the |
| screen as above. The function <TT |
| CLASS="LITERAL" |
| >getch()</TT |
| > as usual |
| gets the key from keyboard and according to the key it does the corresponding |
| work. This type of switch- case is very common in any GUI based programs.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="OTHERBORDERFUNCS" |
| >9.5. Other Border functions</A |
| ></H3 |
| ><P |
| >Above program is grossly inefficient in that with each press of a key, a window |
| is destroyed and another is created. So let's write a more efficient program |
| which uses other border related functions.</P |
| ><P |
| >The following program uses <TT |
| CLASS="LITERAL" |
| >mvhline()</TT |
| > and |
| <TT |
| CLASS="LITERAL" |
| >mvvline()</TT |
| > to achieve similar effect. These two |
| functions are simple. They create a horizontal or vertical line of the specified |
| length at the specified position.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BOTBO" |
| ></A |
| ><P |
| ><B |
| >Example 8. More border functions</B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| typedef struct _win_border_struct { |
| chtype ls, rs, ts, bs, |
| tl, tr, bl, br; |
| }WIN_BORDER; |
| |
| typedef struct _WIN_struct { |
| |
| int startx, starty; |
| int height, width; |
| WIN_BORDER border; |
| }WIN; |
| |
| void init_win_params(WIN *p_win); |
| void print_win_params(WIN *p_win); |
| void create_box(WIN *win, bool flag); |
| |
| int main(int argc, char *argv[]) |
| { WIN win; |
| int ch; |
| |
| initscr(); /* Start curses mode */ |
| start_color(); /* Start the color functionality */ |
| cbreak(); /* Line buffering disabled, Pass on |
| * everty thing to me */ |
| keypad(stdscr, TRUE); /* I need that nifty F1 */ |
| noecho(); |
| init_pair(1, COLOR_CYAN, COLOR_BLACK); |
| |
| /* Initialize the window parameters */ |
| init_win_params(&win); |
| print_win_params(&win); |
| |
| attron(COLOR_PAIR(1)); |
| printw("Press F1 to exit"); |
| refresh(); |
| attroff(COLOR_PAIR(1)); |
| |
| create_box(&win, TRUE); |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_LEFT: |
| create_box(&win, FALSE); |
| --win.startx; |
| create_box(&win, TRUE); |
| break; |
| case KEY_RIGHT: |
| create_box(&win, FALSE); |
| ++win.startx; |
| create_box(&win, TRUE); |
| break; |
| case KEY_UP: |
| create_box(&win, FALSE); |
| --win.starty; |
| create_box(&win, TRUE); |
| break; |
| case KEY_DOWN: |
| create_box(&win, FALSE); |
| ++win.starty; |
| create_box(&win, TRUE); |
| break; |
| } |
| } |
| endwin(); /* End curses mode */ |
| return 0; |
| } |
| void init_win_params(WIN *p_win) |
| { |
| p_win->height = 3; |
| p_win->width = 10; |
| p_win->starty = (LINES - p_win->height)/2; |
| p_win->startx = (COLS - p_win->width)/2; |
| |
| p_win->border.ls = '|'; |
| p_win->border.rs = '|'; |
| p_win->border.ts = '-'; |
| p_win->border.bs = '-'; |
| p_win->border.tl = '+'; |
| p_win->border.tr = '+'; |
| p_win->border.bl = '+'; |
| p_win->border.br = '+'; |
| |
| } |
| void print_win_params(WIN *p_win) |
| { |
| #ifdef _DEBUG |
| mvprintw(25, 0, "%d %d %d %d", p_win->startx, p_win->starty, |
| p_win->width, p_win->height); |
| refresh(); |
| #endif |
| } |
| void create_box(WIN *p_win, bool flag) |
| { int i, j; |
| int x, y, w, h; |
| |
| x = p_win->startx; |
| y = p_win->starty; |
| w = p_win->width; |
| h = p_win->height; |
| |
| if(flag == TRUE) |
| { mvaddch(y, x, p_win->border.tl); |
| mvaddch(y, x + w, p_win->border.tr); |
| mvaddch(y + h, x, p_win->border.bl); |
| mvaddch(y + h, x + w, p_win->border.br); |
| mvhline(y, x + 1, p_win->border.ts, w - 1); |
| mvhline(y + h, x + 1, p_win->border.bs, w - 1); |
| mvvline(y + 1, x, p_win->border.ls, h - 1); |
| mvvline(y + 1, x + w, p_win->border.rs, h - 1); |
| |
| } |
| else |
| for(j = y; j <= y + h; ++j) |
| for(i = x; i <= x + w; ++i) |
| mvaddch(j, i, ' '); |
| |
| refresh(); |
| |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="COLOR" |
| >10. Colors</A |
| ></H2 |
| ><DIV |
| CLASS="SECT2" |
| ><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COLORBASICS" |
| >10.1. The basics</A |
| ></H3 |
| ><P |
| >Life seems dull with no colors. Curses has a nice mechanism to handle colors. |
| Let's get into the thick of the things with a small program.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BSICO" |
| ></A |
| ><P |
| ><B |
| >Example 9. A Simple Color example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string); |
| int main(int argc, char *argv[]) |
| { initscr(); /* Start curses mode */ |
| if(has_colors() == FALSE) |
| { endwin(); |
| printf("Your terminal does not support color\n"); |
| exit(1); |
| } |
| start_color(); /* Start color */ |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| |
| attron(COLOR_PAIR(1)); |
| print_in_middle(stdscr, LINES / 2, 0, 0, "Viola !!! In color ..."); |
| attroff(COLOR_PAIR(1)); |
| getch(); |
| endwin(); |
| } |
| 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(); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >As you can see, to start using color, you should first call the function |
| <TT |
| CLASS="LITERAL" |
| > start_color()</TT |
| >. After that, you can use color |
| capabilities of your terminals using various functions. To find out whether a |
| terminal has color capabilities or not, you can use |
| <TT |
| CLASS="LITERAL" |
| >has_colors()</TT |
| > function, which returns FALSE if |
| the terminal does not support color. </P |
| ><P |
| >Curses initializes all the colors supported by terminal when start_color() is |
| called. These can be accessed by the define constants like |
| <TT |
| CLASS="LITERAL" |
| >COLOR_BLACK </TT |
| > etc. Now to actually start using |
| colors, you have to define pairs. Colors are always used in pairs. That means |
| you have to use the function <TT |
| CLASS="LITERAL" |
| >init_pair() </TT |
| > to |
| define the foreground and background for the pair number you give. After that |
| that pair number can be used as a normal attribute with <TT |
| CLASS="LITERAL" |
| >COLOR_PAIR()</TT |
| >function. This may seem to be cumbersome at first. |
| But this elegant solution allows us to manage color pairs very easily. To |
| appreciate it, you have to look into the the source code of "dialog", a utility |
| for displaying dialog boxes from shell scripts. The developers have defined |
| foreground and background combinations for all the colors they might need and |
| initialized at the beginning. This makes it very easy to set attributes just by |
| accessing a pair which we already have defined as a constant.</P |
| ><P |
| >The following colors are defined in <TT |
| CLASS="LITERAL" |
| >curses.h</TT |
| >. |
| You can use these as parameters for various color functions. |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > COLOR_BLACK 0 |
| COLOR_RED 1 |
| COLOR_GREEN 2 |
| COLOR_YELLOW 3 |
| COLOR_BLUE 4 |
| COLOR_MAGENTA 5 |
| COLOR_CYAN 6 |
| COLOR_WHITE 7</PRE |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="CHANGECOLORDEFS" |
| >10.2. Changing Color Definitions</A |
| ></H3 |
| ><P |
| >The function <TT |
| CLASS="LITERAL" |
| >init_color()</TT |
| >can be used to change |
| the rgb values for the colors defined by curses initially. Say you wanted to |
| lighten the intensity of red color by a minuscule. Then you can use this |
| function as</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > init_color(COLOR_RED, 700, 0, 0); |
| /* param 1 : color name |
| * param 2, 3, 4 : rgb content min = 0, max = 1000 */</PRE |
| ><P |
| >If your terminal cannot change the color definitions, the function returns ERR. |
| The function <TT |
| CLASS="LITERAL" |
| >can_change_color()</TT |
| > can be used to |
| find out whether the terminal has the capability of changing color content or |
| not. The rgb content is scaled from 0 to 1000. Initially RED color is defined |
| with content 1000(r), 0(g), 0(b). </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COLORCONTENT" |
| >10.3. Color Content</A |
| ></H3 |
| ><P |
| >The functions <TT |
| CLASS="LITERAL" |
| >color_content()</TT |
| > and |
| <TT |
| CLASS="LITERAL" |
| >pair_content()</TT |
| > can be used to find the color |
| content and foreground, background combination for the pair. </P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="KEYS" |
| >11. Interfacing with the key board</A |
| ></H2 |
| ><DIV |
| CLASS="SECT2" |
| ><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="KEYSBASICS" |
| >11.1. The Basics</A |
| ></H3 |
| ><P |
| >No GUI is complete without a strong user interface and to interact with the |
| user, a curses program should be sensitive to key presses or the mouse actions |
| done by the user. Let's deal with the keys first.</P |
| ><P |
| >As you have seen in almost all of the above examples, it's very easy to get key |
| input from the user. A simple way of getting key presses is to use |
| <TT |
| CLASS="LITERAL" |
| >getch()</TT |
| > function. The cbreak mode should be |
| enabled to read keys when you are interested in reading individual key hits |
| rather than complete lines of text (which usually end with a carriage return). |
| keypad should be enabled to get the Functions keys, arrow keys etc. See the |
| initialization section for details.</P |
| ><P |
| ><TT |
| CLASS="LITERAL" |
| >getch()</TT |
| > returns an integer corresponding to the |
| key pressed. If it is a normal character, the integer value will be equivalent |
| to the character. Otherwise it returns a number which can be matched with the |
| constants defined in <TT |
| CLASS="LITERAL" |
| >curses.h</TT |
| >. For example if |
| the user presses F1, the integer returned is 265. This can be checked using the |
| macro KEY_F() defined in curses.h. This makes reading keys portable and easy to |
| manage.</P |
| ><P |
| >For example, if you call getch() like this</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > int ch; |
| |
| ch = getch();</PRE |
| ><P |
| >getch() will wait for the user to press a key, (unless you specified a timeout) |
| and when user presses a key, the corresponding integer is returned. Then you can |
| check the value returned with the constants defined in curses.h to match against |
| the keys you want.</P |
| ><P |
| >The following code piece will do that job.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > if(ch == KEY_LEFT) |
| printw("Left arrow is pressed\n");</PRE |
| ><P |
| >Let's write a small program which creates a menu which can be navigated by up |
| and down arrows.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="SIMPLEKEYEX" |
| >11.2. A Simple Key Usage example</A |
| ></H3 |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BSIKE" |
| ></A |
| ><P |
| ><B |
| >Example 10. A Simple Key Usage example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <stdio.h> |
| #include <ncurses.h> |
| |
| #define WIDTH 30 |
| #define HEIGHT 10 |
| |
| int startx = 0; |
| int starty = 0; |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Exit", |
| }; |
| int n_choices = sizeof(choices) / sizeof(char *); |
| void print_menu(WINDOW *menu_win, int highlight); |
| |
| int main() |
| { WINDOW *menu_win; |
| int highlight = 1; |
| int choice = 0; |
| int c; |
| |
| initscr(); |
| clear(); |
| noecho(); |
| cbreak(); /* Line buffering disabled. pass on everything */ |
| startx = (80 - WIDTH) / 2; |
| starty = (24 - HEIGHT) / 2; |
| |
| menu_win = newwin(HEIGHT, WIDTH, starty, startx); |
| keypad(menu_win, TRUE); |
| mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice"); |
| refresh(); |
| print_menu(menu_win, highlight); |
| while(1) |
| { c = wgetch(menu_win); |
| switch(c) |
| { case KEY_UP: |
| if(highlight == 1) |
| highlight = n_choices; |
| else |
| --highlight; |
| break; |
| case KEY_DOWN: |
| if(highlight == n_choices) |
| highlight = 1; |
| else |
| ++highlight; |
| break; |
| case 10: |
| choice = highlight; |
| break; |
| default: |
| mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c); |
| refresh(); |
| break; |
| } |
| print_menu(menu_win, highlight); |
| if(choice != 0) /* User did a choice come out of the infinite loop */ |
| break; |
| } |
| mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]); |
| clrtoeol(); |
| refresh(); |
| endwin(); |
| return 0; |
| } |
| |
| |
| void print_menu(WINDOW *menu_win, int highlight) |
| { |
| int x, y, i; |
| |
| x = 2; |
| y = 2; |
| box(menu_win, 0, 0); |
| for(i = 0; i < n_choices; ++i) |
| { if(highlight == i + 1) /* High light the present choice */ |
| { wattron(menu_win, A_REVERSE); |
| mvwprintw(menu_win, y, x, "%s", choices[i]); |
| wattroff(menu_win, A_REVERSE); |
| } |
| else |
| mvwprintw(menu_win, y, x, "%s", choices[i]); |
| ++y; |
| } |
| wrefresh(menu_win); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="MOUSE" |
| >12. Interfacing with the mouse</A |
| ></H2 |
| ><P |
| >Now that you have seen how to get keys, lets do the same thing from mouse. |
| Usually each UI allows the user to interact with both keyboard and mouse. </P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MOUSEBASICS" |
| >12.1. The Basics</A |
| ></H3 |
| ><P |
| >Before you do any thing else, the events you want to receive have to be enabled |
| with <TT |
| CLASS="LITERAL" |
| >mousemask()</TT |
| >.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > mousemask( mmask_t newmask, /* The events you want to listen to */ |
| mmask_t *oldmask) /* The old events mask */</PRE |
| ><P |
| >The first parameter to above function is a bit mask of events you would like to |
| listen. By default, all the events are turned off. The bit mask <TT |
| CLASS="LITERAL" |
| > ALL_MOUSE_EVENTS</TT |
| > can be used to get all the events.</P |
| ><P |
| >The following are all the event masks:</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > Name Description |
| --------------------------------------------------------------------- |
| BUTTON1_PRESSED mouse button 1 down |
| BUTTON1_RELEASED mouse button 1 up |
| BUTTON1_CLICKED mouse button 1 clicked |
| BUTTON1_DOUBLE_CLICKED mouse button 1 double clicked |
| BUTTON1_TRIPLE_CLICKED mouse button 1 triple clicked |
| BUTTON2_PRESSED mouse button 2 down |
| BUTTON2_RELEASED mouse button 2 up |
| BUTTON2_CLICKED mouse button 2 clicked |
| BUTTON2_DOUBLE_CLICKED mouse button 2 double clicked |
| BUTTON2_TRIPLE_CLICKED mouse button 2 triple clicked |
| BUTTON3_PRESSED mouse button 3 down |
| BUTTON3_RELEASED mouse button 3 up |
| BUTTON3_CLICKED mouse button 3 clicked |
| BUTTON3_DOUBLE_CLICKED mouse button 3 double clicked |
| BUTTON3_TRIPLE_CLICKED mouse button 3 triple clicked |
| BUTTON4_PRESSED mouse button 4 down |
| BUTTON4_RELEASED mouse button 4 up |
| BUTTON4_CLICKED mouse button 4 clicked |
| BUTTON4_DOUBLE_CLICKED mouse button 4 double clicked |
| BUTTON4_TRIPLE_CLICKED mouse button 4 triple clicked |
| BUTTON_SHIFT shift was down during button state change |
| BUTTON_CTRL control was down during button state change |
| BUTTON_ALT alt was down during button state change |
| ALL_MOUSE_EVENTS report all button state changes |
| REPORT_MOUSE_POSITION report mouse movement</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GETTINGEVENTS" |
| >12.2. Getting the events</A |
| ></H3 |
| ><P |
| >Once a class of mouse events have been enabled, getch() class of functions |
| return KEY_MOUSE every time some mouse event happens. Then the mouse event can |
| be retrieved with <TT |
| CLASS="LITERAL" |
| >getmouse()</TT |
| >.</P |
| ><P |
| >The code approximately looks like this:</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > MEVENT event; |
| |
| ch = getch(); |
| if(ch == KEY_MOUSE) |
| if(getmouse(&event) == OK) |
| . /* Do some thing with the event */ |
| . |
| .</PRE |
| ><P |
| > |
| getmouse() returns the event into the pointer given to it. It's a structure |
| which contains</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > typedef struct |
| { |
| short id; /* ID to distinguish multiple devices */ |
| int x, y, z; /* event coordinates */ |
| mmask_t bstate; /* button state bits */ |
| } </PRE |
| ><P |
| >The <TT |
| CLASS="LITERAL" |
| >bstate</TT |
| > is the main variable we are |
| interested in. It tells the button state of the mouse.</P |
| ><P |
| >Then with a code snippet like the following, we can find out what happened.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > if(event.bstate & BUTTON1_PRESSED) |
| printw("Left Button Pressed");</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MOUSETOGETHER" |
| >12.3. Putting it all Together</A |
| ></H3 |
| ><P |
| >That's pretty much interfacing with mouse. Let's create the same menu and enable |
| mouse interaction. To make things simpler, key handling is removed.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BMOME" |
| ></A |
| ><P |
| ><B |
| >Example 11. Access the menu with mouse !!! </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| #define WIDTH 30 |
| #define HEIGHT 10 |
| |
| int startx = 0; |
| int starty = 0; |
| |
| char *choices[] = { "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Exit", |
| }; |
| |
| int n_choices = sizeof(choices) / sizeof(char *); |
| |
| void print_menu(WINDOW *menu_win, int highlight); |
| void report_choice(int mouse_x, int mouse_y, int *p_choice); |
| |
| int main() |
| { int c, choice = 0; |
| WINDOW *menu_win; |
| MEVENT event; |
| |
| /* Initialize curses */ |
| initscr(); |
| clear(); |
| noecho(); |
| cbreak(); //Line buffering disabled. pass on everything |
| |
| /* Try to put the window in the middle of screen */ |
| startx = (80 - WIDTH) / 2; |
| starty = (24 - HEIGHT) / 2; |
| |
| attron(A_REVERSE); |
| mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)"); |
| refresh(); |
| attroff(A_REVERSE); |
| |
| /* Print the menu for the first time */ |
| menu_win = newwin(HEIGHT, WIDTH, starty, startx); |
| print_menu(menu_win, 1); |
| /* Get all the mouse events */ |
| mousemask(ALL_MOUSE_EVENTS, NULL); |
| |
| while(1) |
| { c = wgetch(menu_win); |
| switch(c) |
| { case KEY_MOUSE: |
| if(getmouse(&event) == OK) |
| { /* When the user clicks left mouse button */ |
| if(event.bstate & BUTTON1_PRESSED) |
| { report_choice(event.x + 1, event.y + 1, &choice); |
| if(choice == -1) //Exit chosen |
| goto end; |
| mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]); |
| refresh(); |
| } |
| } |
| print_menu(menu_win, choice); |
| break; |
| } |
| } |
| end: |
| endwin(); |
| return 0; |
| } |
| |
| |
| void print_menu(WINDOW *menu_win, int highlight) |
| { |
| int x, y, i; |
| |
| x = 2; |
| y = 2; |
| box(menu_win, 0, 0); |
| for(i = 0; i < n_choices; ++i) |
| { if(highlight == i + 1) |
| { wattron(menu_win, A_REVERSE); |
| mvwprintw(menu_win, y, x, "%s", choices[i]); |
| wattroff(menu_win, A_REVERSE); |
| } |
| else |
| mvwprintw(menu_win, y, x, "%s", choices[i]); |
| ++y; |
| } |
| wrefresh(menu_win); |
| } |
| |
| /* Report the choice according to mouse position */ |
| void report_choice(int mouse_x, int mouse_y, int *p_choice) |
| { int i,j, choice; |
| |
| i = startx + 2; |
| j = starty + 3; |
| |
| for(choice = 0; choice < n_choices; ++choice) |
| if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice])) |
| { if(choice == n_choices - 1) |
| *p_choice = -1; |
| else |
| *p_choice = choice + 1; |
| break; |
| } |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MISCMOUSEFUNCS" |
| >12.4. Miscellaneous Functions</A |
| ></H3 |
| ><P |
| >The functions mouse_trafo() and wmouse_trafo() can be used to convert to mouse |
| co-ordinates to screen relative co-ordinates. See curs_mouse(3X) man page for details.</P |
| ><P |
| >The mouseinterval function sets the maximum time (in thousands of a |
| second) that can elapse between press and release events in order for |
| them to be recognized as a click. This function returns the previous |
| interval value. The default is one fifth of a second.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="SCREEN" |
| >13. Screen Manipulation</A |
| ></H2 |
| ><P |
| >In this section, we will look into some functions, which allow us to manage the |
| screen efficiently and to write some fancy programs. This is especially |
| important in writing games. </P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GETYX" |
| >13.1. getyx() functions</A |
| ></H3 |
| ><P |
| > The function <TT |
| CLASS="LITERAL" |
| >getyx()</TT |
| > can be used to find out |
| the present cursor co-ordinates. It will fill the values of x and y co-ordinates |
| in the arguments given to it. Since getyx() is a macro you don't have to pass |
| the address of the variables. It can be called as</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > getyx(win, y, x); |
| /* win: window pointer |
| * y, x: y, x co-ordinates will be put into this variables |
| */</PRE |
| ><P |
| >The function getparyx() gets the beginning co-ordinates of the sub window |
| relative to the main window. This is some times useful to update a sub window. |
| When designing fancy stuff like writing multiple menus, it becomes difficult to |
| store the menu positions, their first option co-ordinates etc. A simple solution |
| to this problem, is to create menus in sub windows and later find the starting |
| co-ordinates of the menus by using getparyx().</P |
| ><P |
| >The functions getbegyx() and getmaxyx() store current window's beginning and |
| maximum co-ordinates. These functions are useful in the same way as above in |
| managing the windows and sub windows effectively.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="SCREENDUMP" |
| >13.2. Screen Dumping</A |
| ></H3 |
| ><P |
| >While writing games, some times it becomes necessary to store the state of the |
| screen and restore it back to the same state. The function scr_dump() can be |
| used to dump the screen contents to a file given as an argument. Later it can be |
| restored by scr_restore function. These two simple functions can be used |
| effectively to maintain a fast moving game with changing scenarios. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="WINDOWDUMP" |
| >13.3. Window Dumping</A |
| ></H3 |
| ><P |
| >To store and restore windows, the functions |
| <TT |
| CLASS="LITERAL" |
| >putwin()</TT |
| > and <TT |
| CLASS="LITERAL" |
| >getwin()</TT |
| > can be used. <TT |
| CLASS="LITERAL" |
| >putwin()</TT |
| > puts |
| the present window state into a file, which can be later restored by |
| <TT |
| CLASS="LITERAL" |
| >getwin()</TT |
| >.</P |
| ><P |
| > |
| The function <TT |
| CLASS="LITERAL" |
| >copywin()</TT |
| > can be used to copy a |
| window completely onto another window. It takes the source and destination |
| windows as parameters and according to the rectangle specified, it copies the |
| rectangular region from source to destination window. It's last parameter |
| specifies whether to overwrite or just overlay the contents on to the |
| destination window. If this argument is true, then the copying is |
| non-destructive.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="MISC" |
| >14. Miscellaneous features</A |
| ></H2 |
| ><P |
| >Now you know enough features to write a good curses program, with all bells and |
| whistles. There are some miscellaneous functions which are useful in various |
| cases. Let's go headlong into some of those.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="CURSSET" |
| >14.1. curs_set()</A |
| ></H3 |
| ><P |
| >This function can be used to make the cursor invisible. The parameter to this |
| function should be </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > 0 : invisible or |
| 1 : normal or |
| 2 : very visible.</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="TEMPLEAVE" |
| >14.2. Temporarily Leaving Curses mode</A |
| ></H3 |
| ><P |
| >Some times you may want to get back to cooked mode (normal line buffering mode) |
| temporarily. In such a case you will first need to save the tty modes with a |
| call to <TT |
| CLASS="LITERAL" |
| >def_prog_mode()</TT |
| > and then call |
| <TT |
| CLASS="LITERAL" |
| >endwin()</TT |
| > to end the curses mode. This will |
| leave you in the original tty mode. To get back to curses once you are done, |
| call <TT |
| CLASS="LITERAL" |
| >reset_prog_mode() </TT |
| >. This function returns |
| the tty to the state stored by <TT |
| CLASS="LITERAL" |
| >def_prog_mode()</TT |
| >. Then do refresh(), and you are back to the curses mode. Here |
| is an example showing the sequence of things to be done.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BTELE" |
| ></A |
| ><P |
| ><B |
| >Example 12. Temporarily Leaving Curses Mode </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| int main() |
| { |
| initscr(); /* Start curses mode */ |
| printw("Hello World !!!\n"); /* Print Hello World */ |
| refresh(); /* Print it on to the real screen */ |
| def_prog_mode(); /* Save the tty modes */ |
| endwin(); /* End curses mode temporarily */ |
| system("/bin/sh"); /* Do whatever you like in cooked mode */ |
| reset_prog_mode(); /* Return to the previous tty mode*/ |
| /* stored by def_prog_mode() */ |
| refresh(); /* Do refresh() to restore the */ |
| /* Screen contents */ |
| printw("Another String\n"); /* Back to curses use the full */ |
| refresh(); /* capabilities of curses */ |
| endwin(); /* End curses mode */ |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="ACSVARS" |
| >14.3. ACS_ variables</A |
| ></H3 |
| ><P |
| >If you have ever programmed in DOS, you know about those nifty characters in |
| extended character set. They are printable only on some terminals. NCURSES |
| functions like <TT |
| CLASS="LITERAL" |
| >box()</TT |
| > use these characters. All |
| these variables start with ACS meaning alternative character set. You might have |
| noticed me using these characters in some of the programs above. Here's an example |
| showing all the characters.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="BACSVARS" |
| ></A |
| ><P |
| ><B |
| >Example 13. ACS Variables Example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <ncurses.h> |
| |
| int main() |
| { |
| initscr(); |
| |
| printw("Upper left corner "); addch(ACS_ULCORNER); printw("\n"); |
| printw("Lower left corner "); addch(ACS_LLCORNER); printw("\n"); |
| printw("Lower right corner "); addch(ACS_LRCORNER); printw("\n"); |
| printw("Tee pointing right "); addch(ACS_LTEE); printw("\n"); |
| printw("Tee pointing left "); addch(ACS_RTEE); printw("\n"); |
| printw("Tee pointing up "); addch(ACS_BTEE); printw("\n"); |
| printw("Tee pointing down "); addch(ACS_TTEE); printw("\n"); |
| printw("Horizontal line "); addch(ACS_HLINE); printw("\n"); |
| printw("Vertical line "); addch(ACS_VLINE); printw("\n"); |
| printw("Large Plus or cross over "); addch(ACS_PLUS); printw("\n"); |
| printw("Scan Line 1 "); addch(ACS_S1); printw("\n"); |
| printw("Scan Line 3 "); addch(ACS_S3); printw("\n"); |
| printw("Scan Line 7 "); addch(ACS_S7); printw("\n"); |
| printw("Scan Line 9 "); addch(ACS_S9); printw("\n"); |
| printw("Diamond "); addch(ACS_DIAMOND); printw("\n"); |
| printw("Checker board (stipple) "); addch(ACS_CKBOARD); printw("\n"); |
| printw("Degree Symbol "); addch(ACS_DEGREE); printw("\n"); |
| printw("Plus/Minus Symbol "); addch(ACS_PLMINUS); printw("\n"); |
| printw("Bullet "); addch(ACS_BULLET); printw("\n"); |
| printw("Arrow Pointing Left "); addch(ACS_LARROW); printw("\n"); |
| printw("Arrow Pointing Right "); addch(ACS_RARROW); printw("\n"); |
| printw("Arrow Pointing Down "); addch(ACS_DARROW); printw("\n"); |
| printw("Arrow Pointing Up "); addch(ACS_UARROW); printw("\n"); |
| printw("Board of squares "); addch(ACS_BOARD); printw("\n"); |
| printw("Lantern Symbol "); addch(ACS_LANTERN); printw("\n"); |
| printw("Solid Square Block "); addch(ACS_BLOCK); printw("\n"); |
| printw("Less/Equal sign "); addch(ACS_LEQUAL); printw("\n"); |
| printw("Greater/Equal sign "); addch(ACS_GEQUAL); printw("\n"); |
| printw("Pi "); addch(ACS_PI); printw("\n"); |
| printw("Not equal "); addch(ACS_NEQUAL); printw("\n"); |
| printw("UK pound sign "); addch(ACS_STERLING); printw("\n"); |
| |
| refresh(); |
| getch(); |
| endwin(); |
| |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="OTHERLIB" |
| >15. Other libraries</A |
| ></H2 |
| ><P |
| >Apart from the curses library, there are few text mode libraries, which provide |
| more functionality and a lot of features. The following sections explain three |
| standard libraries which are usually distributed along with curses. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="PANELS" |
| >16. Panel Library</A |
| ></H2 |
| ><P |
| >Now that you are proficient in curses, you wanted to do some thing big. You |
| created a lot of overlapping windows to give a professional windows-type look. |
| Unfortunately, it soon becomes difficult to manage these. The multiple |
| refreshes, updates plunge you into a nightmare. The overlapping windows create |
| blotches, whenever you forget to refresh the windows in the proper order. </P |
| ><P |
| >Don't despair. There's an elegant solution provided in panels library. In the |
| words of developers of ncurses </P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >When your interface design is such that windows may dive deeper into the |
| visibility stack or pop to the top at runtime, the resulting book-keeping can be |
| tedious and difficult to get right. Hence the panels library.</I |
| ></SPAN |
| ></P |
| ><P |
| >If you have lot of overlapping windows, then panels library is the way to go. It |
| obviates the need of doing series of wnoutrefresh(), doupdate() and relieves the |
| burden of doing it correctly(bottom up). The library maintains information about |
| the order of windows, their overlapping and update the screen properly. So why |
| wait? Let's take a close peek into panels.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PANELBASICS" |
| >16.1. The Basics</A |
| ></H3 |
| ><P |
| >Panel object is a window that is implicitly treated as part of a deck including |
| all other panel objects. The deck is treated as a stack with the top panel being |
| completely visible and the other panels may or may not be obscured according to |
| their positions. So the basic idea is to create a stack of overlapping panels |
| and use panels library to display them correctly. There is a function similar to |
| refresh() which, when called , displays panels in the correct order. Functions |
| are provided to hide or show panels, move panels, change its size etc.. The |
| overlapping problem is managed by the panels library during all the calls to |
| these functions. </P |
| ><P |
| >The general flow of a panel program goes like this: |
| |
| <P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >Create the windows (with newwin()) to be attached to the panels.</P |
| ></LI |
| ><LI |
| ><P |
| >Create panels with the chosen visibility order. Stack them up according to the |
| desired visibility. The function new_panel() is used to created panels.</P |
| ></LI |
| ><LI |
| ><P |
| >Call update_panels() to write the panels to the virtual screen in correct |
| visibility order. Do a doupdate() to show it on the screen. </P |
| ></LI |
| ><LI |
| ><P |
| >Mainpulate the panels with show_panel(), hide_panel(), move_panel() etc. Make |
| use of helper functions like panel_hidden() and panel_window(). Make use of user |
| pointer to store custom data for a panel. Use the functions set_panel_userptr() |
| and panel_userptr() to set and get the user pointer for a panel.</P |
| ></LI |
| ><LI |
| ><P |
| >When you are done with the panel use del_panel() to delete the panel.</P |
| ></LI |
| ></OL |
| ></P |
| ><P |
| >Let's make the concepts clear, with some programs. The following is a simple |
| program which creates 3 overlapping panels and shows them on the screen. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COMPILEPANELS" |
| >16.2. Compiling With the Panels Library</A |
| ></H3 |
| ><P |
| >To use panels library functions, you have to include panel.h and to link the |
| program with panels library the flag -lpanel should be added along with |
| -lncurses in that order.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > #include <panel.h> |
| . |
| . |
| . |
| |
| compile and link: gcc <program file> -lpanel -lncurses</PRE |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="PPASI" |
| ></A |
| ><P |
| ><B |
| >Example 14. Panel basics</B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <panel.h> |
| |
| 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); |
| |
| /* |
| * Create borders around the windows so that you can see the effect |
| * of panels |
| */ |
| for(i = 0; i < 3; ++i) |
| box(my_wins[i], 0, 0); |
| |
| /* 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 */ |
| |
| /* Update the stacking order. 2nd panel will be on top */ |
| update_panels(); |
| |
| /* Show it on the screen */ |
| doupdate(); |
| |
| getch(); |
| endwin(); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >As you can see, above program follows a simple flow as explained. The windows |
| are created with newwin() and then they are attached to panels with new_panel(). |
| As we attach one panel after another, the stack of panels gets updated. To put |
| them on screen update_panels() and doupdate() are called.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PANELBROWSING" |
| >16.3. Panel Window Browsing</A |
| ></H3 |
| ><P |
| >A slightly complicated example is given below. This program creates 3 |
| windows which can be cycled through using tab. Have a look at the code.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="PPABR" |
| ></A |
| ><P |
| ><B |
| >Example 15. Panel Window Browsing Example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <panel.h> |
| |
| #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, chtype color); |
| |
| int main() |
| { WINDOW *my_wins[3]; |
| PANEL *my_panels[3]; |
| PANEL *top; |
| int ch; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize all the colors */ |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_GREEN, COLOR_BLACK); |
| init_pair(3, COLOR_BLUE, COLOR_BLACK); |
| init_pair(4, COLOR_CYAN, COLOR_BLACK); |
| |
| 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 up the user pointers to the next panel */ |
| 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 the stacking order. 2nd panel will be on top */ |
| update_panels(); |
| |
| /* Show it on the screen */ |
| attron(COLOR_PAIR(4)); |
| mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); |
| attroff(COLOR_PAIR(4)); |
| doupdate(); |
| |
| top = my_panels[2]; |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case 9: |
| top = (PANEL *)panel_userptr(top); |
| top_panel(top); |
| 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, COLOR_PAIR(label_color)); |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="USERPTRUSING" |
| >16.4. Using User Pointers</A |
| ></H3 |
| ><P |
| >In the above example I used user pointers to find out the next window in the |
| cycle. We can attach custom information to the panel by specifying a user |
| pointer, which can point to any information you want to store. In this case I |
| stored the pointer to the next panel in the cycle. User pointer for a panel can |
| be set with the function <TT |
| CLASS="LITERAL" |
| > set_panel_userptr()</TT |
| >. |
| It can be accessed using the function <TT |
| CLASS="LITERAL" |
| >panel_userptr()</TT |
| > which will return the user pointer for the panel given as |
| argument. After finding the next panel in the cycle It's brought to the top by |
| the function top_panel(). This function brings the panel given as argument to |
| the top of the panel stack. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PANELMOVERESIZE" |
| >16.5. Moving and Resizing Panels</A |
| ></H3 |
| ><P |
| >The function <TT |
| CLASS="LITERAL" |
| >move_panel()</TT |
| > can be used to move a |
| panel to the desired location. It does not change the position of the panel in |
| the stack. Make sure that you use move_panel() instead mvwin() on the window |
| associated with the panel.</P |
| ><P |
| >Resizing a panel is slightly complex. There is no straight forward function |
| just to resize the window associated with a panel. A solution to resize a panel |
| is to create a new window with the desired sizes, change the window associated |
| with the panel using replace_panel(). Don't forget to delete the old window. The |
| window associated with a panel can be found by using the function |
| panel_window().</P |
| ><P |
| >The following program shows these concepts, in supposedly simple program. You |
| can cycle through the window with <TAB> as usual. To resize or move the |
| active panel press 'r' for resize 'm' for moving. Then use arrow keys to resize |
| or move it to the desired way and press enter to end your resizing or moving. |
| This example makes use of user data to get the required data to do the |
| operations. </P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="PPARE" |
| ></A |
| ><P |
| ><B |
| >Example 16. Panel Moving and Resizing example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <panel.h> |
| |
| typedef struct _PANEL_DATA { |
| int x, y, w, h; |
| char label[80]; |
| int label_color; |
| PANEL *next; |
| }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, chtype color); |
| 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(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize all the colors */ |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_GREEN, COLOR_BLACK); |
| init_pair(3, COLOR_BLUE, COLOR_BLACK); |
| init_pair(4, COLOR_CYAN, COLOR_BLACK); |
| |
| 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 */ |
| attron(COLOR_PAIR(4)); |
| mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing"); |
| mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); |
| attroff(COLOR_PAIR(4)); |
| 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; |
| attron(COLOR_PAIR(4)); |
| mvprintw(LINES - 4, 0, "Entered Resizing :Use Arrow Keys to resize and press <ENTER> to end resizing"); |
| refresh(); |
| attroff(COLOR_PAIR(4)); |
| break; |
| case 'm': /* Move */ |
| attron(COLOR_PAIR(4)); |
| mvprintw(LINES - 4, 0, "Entered Moving: Use Arrow Keys to Move and press <ENTER> to end moving"); |
| refresh(); |
| attroff(COLOR_PAIR(4)); |
| 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 - 4, 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; |
| |
| } |
| attron(COLOR_PAIR(4)); |
| mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing"); |
| mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); |
| attroff(COLOR_PAIR(4)); |
| refresh(); |
| 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, COLOR_PAIR(label_color)); |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Concentrate on the main while loop. Once it finds out the type of key pressed, |
| it takes appropriate action. If 'r' is pressed resizing mode is started. After |
| this the new sizes are updated as the user presses the arrow keys. When the user |
| presses <ENTER> present selection ends and panel is resized by using the |
| concept explained. While in resizing mode the program doesn't show how the |
| window is getting resized. It's left as an exercise to the reader to print a |
| dotted border while it gets resized to a new position. </P |
| ><P |
| >When the user presses 'm' the move mode starts. This is a bit simpler than |
| resizing. As the arrow keys are pressed the new position is updated and |
| pressing of <ENTER> causes the panel to be moved by calling the function |
| move_panel().</P |
| ><P |
| >In this program the user data which is represented as PANEL_DATA, plays very |
| important role in finding the associated information with a panel. As written in |
| the comments, the PANEL_DATA stores the panel sizes, label, label color and a |
| pointer to the next panel in the cycle.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PANELSHOWHIDE" |
| >16.6. Hiding and Showing Panels</A |
| ></H3 |
| ><P |
| >A Panel can be hidden by using the function hide_panel(). This function merely |
| removes it form the stack of panels, thus hiding it on the screen once you do |
| update_panels() and doupdate(). It doesn't destroy the PANEL structure |
| associated with the hidden panel. It can be shown again by using the |
| show_panel() function.</P |
| ><P |
| >The following program shows the hiding of panels. Press 'a' or 'b' or 'c' to |
| show or hide first, second and third windows respectively. It uses a user data |
| with a small variable hide, which keeps track of whether the window is hidden or |
| not. For some reason the function |
| <TT |
| CLASS="LITERAL" |
| >panel_hidden()</TT |
| > which tells whether a panel is |
| hidden or not is not working. A bug report was also presented by Michael Andres |
| <A |
| HREF="http://www.geocrawler.com/archives/3/344/1999/9/0/2643549/" |
| TARGET="_top" |
| > here</A |
| ></P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="PPAHI" |
| ></A |
| ><P |
| ><B |
| >Example 17. Panel Hiding and Showing example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <panel.h> |
| |
| 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, chtype color); |
| |
| 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); |
| |
| /* Initialize all the colors */ |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_GREEN, COLOR_BLACK); |
| init_pair(3, COLOR_BLUE, COLOR_BLACK); |
| init_pair(4, COLOR_CYAN, COLOR_BLACK); |
| |
| 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 */ |
| attron(COLOR_PAIR(4)); |
| 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"); |
| |
| attroff(COLOR_PAIR(4)); |
| 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, COLOR_PAIR(label_color)); |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PANELABOVE" |
| >16.7. panel_above() and panel_below() Functions</A |
| ></H3 |
| ><P |
| >The functions <TT |
| CLASS="LITERAL" |
| >panel_above()</TT |
| > and |
| <TT |
| CLASS="LITERAL" |
| >panel_below()</TT |
| > can be used to find out the panel |
| above and below a panel. If the argument to these functions is NULL, then they |
| return a pointer to bottom panel and top panel respectively.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="MENUS" |
| >17. Menus Library</A |
| ></H2 |
| ><P |
| >The menus library provides a nice extension to basic curses, through which you |
| can create menus. It provides a set of functions to create menus. But they have |
| to be customized to give a nicer look, with colors etc. Let's get into the |
| details.</P |
| ><P |
| >A menu is a screen display that assists the user to choose some subset of a |
| given set of items. To put it simple, a menu is a collection of items from which |
| one or more items can be chosen. Some readers might not be aware of multiple |
| item selection capability. Menu library provides functionality to write menus |
| from which the user can chose more than one item as the preferred choice. This |
| is dealt with in a later section. Now it is time for some rudiments.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MENUBASICS" |
| >17.1. The Basics</A |
| ></H3 |
| ><P |
| >To create menus, you first create items, and then post the menu to the display. |
| After that, all the processing of user responses is done in an elegant function |
| menu_driver() which is the work horse of any menu program. </P |
| ><P |
| >The general flow of control of a menu program looks like this. |
| <P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >Initialize curses</P |
| ></LI |
| ><LI |
| ><P |
| >Create items using new_item(). You can specify a name and description for the |
| items.</P |
| ></LI |
| ><LI |
| ><P |
| >Create the menu with new_menu() by specifying the items to be attached with.</P |
| ></LI |
| ><LI |
| ><P |
| >Post the menu with menu_post() and refresh the screen.</P |
| ></LI |
| ><LI |
| ><P |
| >Process the user requests with a loop and do necessary updates to menu with |
| menu_driver.</P |
| ></LI |
| ><LI |
| ><P |
| >Unpost the menu with menu_unpost()</P |
| ></LI |
| ><LI |
| ><P |
| >Free the memory allocated to menu by free_menu()</P |
| ></LI |
| ><LI |
| ><P |
| >Free the memory allocated to the items with free_item() </P |
| ></LI |
| ><LI |
| ><P |
| >End curses </P |
| ></LI |
| ></OL |
| ></P |
| ><P |
| >Let's see a program which prints a simple menu and updates the current selection |
| with up, down arrows. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COMPILEMENUS" |
| >17.2. Compiling With the Menu Library</A |
| ></H3 |
| ><P |
| >To use menu library functions, you have to include menu.h and to link the |
| program with menu library the flag -lmenu should be added along with -lncurses |
| in that order.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > #include <menu.h> |
| . |
| . |
| . |
| |
| compile and link: gcc <program file> -lmenu -lncurses</PRE |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMESI" |
| ></A |
| ><P |
| ><B |
| >Example 18. Menu Basics </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <curses.h> |
| #include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Exit", |
| }; |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| int n_choices, i; |
| ITEM *cur_item; |
| |
| |
| initscr(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *)); |
| |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| my_items[n_choices] = (ITEM *)NULL; |
| |
| my_menu = new_menu((ITEM **)my_items); |
| mvprintw(LINES - 2, 0, "F1 to Exit"); |
| post_menu(my_menu); |
| refresh(); |
| |
| while((c = getch()) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| } |
| } |
| |
| free_item(my_items[0]); |
| free_item(my_items[1]); |
| free_menu(my_menu); |
| endwin(); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This program demonstrates the basic concepts involved in creating a menu using |
| menus library. First we create the items using new_item() and then attach them |
| to the menu with new_menu() function. After posting the menu and refreshing the |
| screen, the main processing loop starts. It reads user input and takes |
| corresponding action. The function menu_driver() is the main work horse of the |
| menu system. The second parameter to this function tells what's to be done with |
| the menu. According to the parameter, menu_driver() does the corresponding task. |
| The value can be either a menu navigational request, an ascii character, or a |
| KEY_MOUSE special key associated with a mouse event.</P |
| ><P |
| >The menu_driver accepts following navigational requests. |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > REQ_LEFT_ITEM Move left to an item. |
| REQ_RIGHT_ITEM Move right to an item. |
| REQ_UP_ITEM Move up to an item. |
| REQ_DOWN_ITEM Move down to an item. |
| REQ_SCR_ULINE Scroll up a line. |
| REQ_SCR_DLINE Scroll down a line. |
| REQ_SCR_DPAGE Scroll down a page. |
| REQ_SCR_UPAGE Scroll up a page. |
| REQ_FIRST_ITEM Move to the first item. |
| REQ_LAST_ITEM Move to the last item. |
| REQ_NEXT_ITEM Move to the next item. |
| REQ_PREV_ITEM Move to the previous item. |
| REQ_TOGGLE_ITEM Select/deselect an item. |
| REQ_CLEAR_PATTERN Clear the menu pattern buffer. |
| REQ_BACK_PATTERN Delete the previous character from the pattern buffer. |
| REQ_NEXT_MATCH Move to the next item matching the pattern match. |
| REQ_PREV_MATCH Move to the previous item matching the pattern match. </PRE |
| ></P |
| ><P |
| >Don't get overwhelmed by the number of options. We will see them slowly one |
| after another. The options of interest in this example are REQ_UP_ITEM and |
| REQ_DOWN_ITEM. These two options when passed to menu_driver, menu driver |
| updates the current item to one item up or down respectively.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MENUDRIVER" |
| >17.3. Menu Driver: The work horse of the menu system</A |
| ></H3 |
| ><P |
| >As you have seen in the above example, menu_driver plays an important role in |
| updating the menu. It is very important to understand various options it takes |
| and what they do. As explained above, the second parameter to menu_driver() can |
| be either a navigational request, a printable character or a KEY_MOUSE key. |
| Let's dissect the different navigational requests.</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_LEFT_ITEM and REQ_RIGHT_ITEM</I |
| ></SPAN |
| ></P |
| ><P |
| >A Menu can be displayed with multiple columns for more than one item. This can |
| be done by using the <TT |
| CLASS="LITERAL" |
| >menu_format()</TT |
| >function. |
| When a multi columnar menu is displayed these requests cause the menu driver to |
| move the current selection to left or right.</P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_UP_ITEM and REQ_DOWN_ITEM </I |
| ></SPAN |
| > </P |
| ><P |
| >These two options you have seen in the above example. These options when given, |
| makes the menu_driver to move the current selection to an item up or down.</P |
| ></LI |
| ><LI |
| ><P |
| > <SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_* options</I |
| ></SPAN |
| > </P |
| ><P |
| >The four options REQ_SCR_ULINE, REQ_SCR_DLINE, REQ_SCR_DPAGE, REQ_SCR_UPAGE are |
| related to scrolling. If all the items in the menu cannot be displayed in the |
| menu sub window, then the menu is scrollable. These requests can be given to the |
| menu_driver to do the scrolling either one line up, down or one page down or up |
| respectively. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_NEXT_ITEM and |
| REQ_PREV_ITEM </I |
| ></SPAN |
| > </P |
| ><P |
| >These requests are self explanatory.</P |
| ></LI |
| ><LI |
| ><P |
| > <SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_TOGGLE_ITEM</I |
| ></SPAN |
| > </P |
| ><P |
| >This request when given, toggles the present selection. This option is to be |
| used only in a multi valued menu. So to use this request the option O_ONEVALUE |
| must be off. This option can be made off or on with set_menu_opts().</P |
| ></LI |
| ><LI |
| ><P |
| > <SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >Pattern Requests </I |
| ></SPAN |
| ></P |
| ><P |
| >Every menu has an associated pattern buffer, which is used to find the nearest |
| match to the ascii characters entered by the user. Whenever ascii characters are |
| given to menu_driver, it puts in to the pattern buffer. It also tries to find |
| the nearest match to the pattern in the items list and moves current selection |
| to that item. The request REQ_CLEAR_PATTERN clears the pattern buffer. The |
| request REQ_BACK_PATTERN deletes the previous character in the pattern buffer. |
| In case the pattern matches more than one item then the matched items can be |
| cycled through REQ_NEXT_MATCH and REQ_PREV_MATCH which move the current |
| selection to the next and previous matches respectively.</P |
| ></LI |
| ><LI |
| ><P |
| > <SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >Mouse Requests</I |
| ></SPAN |
| ></P |
| ><P |
| >In case of KEY_MOUSE requests, according to the mouse position an action is |
| taken accordingly. The action to be taken is explained in the man page as, </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| > If the second argument is the KEY_MOUSE special key, the |
| associated mouse event is translated into one of the above |
| pre-defined requests. Currently only clicks in the user |
| window (e.g. inside the menu display area or the decora­ |
| tion window) are handled. If you click above the display |
| region of the menu, a REQ_SCR_ULINE is generated, if you |
| doubleclick a REQ_SCR_UPAGE is generated and if you |
| tripleclick a REQ_FIRST_ITEM is generated. If you click |
| below the display region of the menu, a REQ_SCR_DLINE is |
| generated, if you doubleclick a REQ_SCR_DPAGE is generated |
| and if you tripleclick a REQ_LAST_ITEM is generated. If |
| you click at an item inside the display area of the menu, |
| the menu cursor is positioned to that item.</I |
| ></SPAN |
| ></PRE |
| ></LI |
| ></UL |
| ><P |
| >Each of the above requests will be explained in the following lines with several |
| examples whenever appropriate.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MENUWINDOWS" |
| >17.4. Menu Windows</A |
| ></H3 |
| ><P |
| >Every menu created is associated with a window and a sub window. The menu window |
| displays any title or border associated with the menu. The menu sub window |
| displays the menu items currently available for selection. But we didn't specify |
| any window or sub window in the simple example. When a window is not specified, |
| stdscr is taken as the main window, and then menu system calculates the sub |
| window size required for the display of items. Then items are displayed in the |
| calculated sub window. So let's play with these windows and display a menu with |
| a border and a title.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMEWI" |
| ></A |
| ><P |
| ><B |
| >Example 19. Menu Windows Usage example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Exit", |
| (char *)NULL, |
| }; |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| WINDOW *my_menu_win; |
| int n_choices, i; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| |
| /* Create items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| |
| /* Crate menu */ |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Create the window to be associated with the menu */ |
| my_menu_win = newwin(10, 40, 4, 4); |
| keypad(my_menu_win, TRUE); |
| |
| /* Set main window and sub window */ |
| set_menu_win(my_menu, my_menu_win); |
| set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1)); |
| |
| /* Set menu mark to the string " * " */ |
| set_menu_mark(my_menu, " * "); |
| |
| /* Print a border around the main window and print a title */ |
| box(my_menu_win, 0, 0); |
| print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1)); |
| mvwaddch(my_menu_win, 2, 0, ACS_LTEE); |
| mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); |
| mvwaddch(my_menu_win, 2, 39, ACS_RTEE); |
| mvprintw(LINES - 2, 0, "F1 to exit"); |
| refresh(); |
| |
| /* Post the menu */ |
| post_menu(my_menu); |
| wrefresh(my_menu_win); |
| |
| while((c = wgetch(my_menu_win)) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| } |
| wrefresh(my_menu_win); |
| } |
| |
| /* Unpost and free all the memory taken up */ |
| unpost_menu(my_menu); |
| free_menu(my_menu); |
| for(i = 0; i < n_choices; ++i) |
| free_item(my_items[i]); |
| endwin(); |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This example creates a menu with a title, border, a fancy line separating title |
| and the items. As you can see, in order to attach a window to a menu the |
| function set_menu_win() has to be used. Then we attach the sub window also. This |
| displays the items in the sub window. You can also set the mark string which |
| gets displayed to the left of the selected item with set_menu_mark().</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="SCROLLMENUS" |
| >17.5. Scrolling Menus</A |
| ></H3 |
| ><P |
| >If the sub window given for a window is not big enough to show all the items, |
| then the menu will be scrollable. When you are on the last item in the present |
| list, if you send REQ_DOWN_ITEM, it gets translated into REQ_SCR_DLINE and the |
| menu scrolls by one item. You can manually give REQ_SCR_ operations to do |
| scrolling. Let's see how it can be done.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMESC" |
| ></A |
| ><P |
| ><B |
| >Example 20. Scrolling Menus example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <curses.h> |
| #include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Choice 5", |
| "Choice 6", |
| "Choice 7", |
| "Choice 8", |
| "Choice 9", |
| "Choice 10", |
| "Exit", |
| (char *)NULL, |
| }; |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| WINDOW *my_menu_win; |
| int n_choices, i; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_CYAN, COLOR_BLACK); |
| |
| /* Create items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| |
| /* Crate menu */ |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Create the window to be associated with the menu */ |
| my_menu_win = newwin(10, 40, 4, 4); |
| keypad(my_menu_win, TRUE); |
| |
| /* Set main window and sub window */ |
| set_menu_win(my_menu, my_menu_win); |
| set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1)); |
| set_menu_format(my_menu, 5, 1); |
| |
| /* Set menu mark to the string " * " */ |
| set_menu_mark(my_menu, " * "); |
| |
| /* Print a border around the main window and print a title */ |
| box(my_menu_win, 0, 0); |
| print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1)); |
| mvwaddch(my_menu_win, 2, 0, ACS_LTEE); |
| mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); |
| mvwaddch(my_menu_win, 2, 39, ACS_RTEE); |
| |
| /* Post the menu */ |
| post_menu(my_menu); |
| wrefresh(my_menu_win); |
| |
| attron(COLOR_PAIR(2)); |
| mvprintw(LINES - 2, 0, "Use PageUp and PageDown to scoll down or up a page of items"); |
| mvprintw(LINES - 1, 0, "Arrow Keys to navigate (F1 to Exit)"); |
| attroff(COLOR_PAIR(2)); |
| refresh(); |
| |
| while((c = wgetch(my_menu_win)) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| case KEY_NPAGE: |
| menu_driver(my_menu, REQ_SCR_DPAGE); |
| break; |
| case KEY_PPAGE: |
| menu_driver(my_menu, REQ_SCR_UPAGE); |
| break; |
| } |
| wrefresh(my_menu_win); |
| } |
| |
| /* Unpost and free all the memory taken up */ |
| unpost_menu(my_menu); |
| free_menu(my_menu); |
| for(i = 0; i < n_choices; ++i) |
| free_item(my_items[i]); |
| endwin(); |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This program is self-explanatory. In this example the number of choices has been |
| increased to ten, which is larger than our sub window size which can hold 6 |
| items. This message has to be explicitly conveyed to the menu system with the |
| function set_menu_format(). In here we specify the number of rows and columns we |
| want to be displayed for a single page. We can specify any number of items to be |
| shown, in the rows variables, if it is less than the height of the sub window. |
| If the key pressed by the user is a PAGE UP or PAGE DOWN, the menu is scrolled a |
| page due to the requests (REQ_SCR_DPAGE and REQ_SCR_UPAGE) given to |
| menu_driver().</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MULTICOLUMN" |
| >17.6. Multi Columnar Menus</A |
| ></H3 |
| ><P |
| >In the above example you have seen how to use the function set_menu_format(). I |
| didn't mention what the cols variable (third parameter) does. Well, If your sub |
| window is wide enough, you can opt to display more than one item per row. This |
| can be specified in the cols variable. To make things simpler, the following |
| example doesn't show descriptions for the items.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMEMUCO" |
| ></A |
| ><P |
| ><B |
| >Example 21. Milt Columnar Menus Example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <curses.h> |
| #include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5", |
| "Choice 6", "Choice 7", "Choice 8", "Choice 9", "Choice 10", |
| "Choice 11", "Choice 12", "Choice 13", "Choice 14", "Choice 15", |
| "Choice 16", "Choice 17", "Choice 18", "Choice 19", "Choice 20", |
| "Exit", |
| (char *)NULL, |
| }; |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| WINDOW *my_menu_win; |
| int n_choices, i; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_CYAN, COLOR_BLACK); |
| |
| /* Create items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| |
| /* Crate menu */ |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Set menu option not to show the description */ |
| menu_opts_off(my_menu, O_SHOWDESC); |
| |
| /* Create the window to be associated with the menu */ |
| my_menu_win = newwin(10, 70, 4, 4); |
| keypad(my_menu_win, TRUE); |
| |
| /* Set main window and sub window */ |
| set_menu_win(my_menu, my_menu_win); |
| set_menu_sub(my_menu, derwin(my_menu_win, 6, 68, 3, 1)); |
| set_menu_format(my_menu, 5, 3); |
| set_menu_mark(my_menu, " * "); |
| |
| /* Print a border around the main window and print a title */ |
| box(my_menu_win, 0, 0); |
| |
| attron(COLOR_PAIR(2)); |
| mvprintw(LINES - 3, 0, "Use PageUp and PageDown to scroll"); |
| mvprintw(LINES - 2, 0, "Use Arrow Keys to navigate (F1 to Exit)"); |
| attroff(COLOR_PAIR(2)); |
| refresh(); |
| |
| /* Post the menu */ |
| post_menu(my_menu); |
| wrefresh(my_menu_win); |
| |
| while((c = wgetch(my_menu_win)) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| case KEY_LEFT: |
| menu_driver(my_menu, REQ_LEFT_ITEM); |
| break; |
| case KEY_RIGHT: |
| menu_driver(my_menu, REQ_RIGHT_ITEM); |
| break; |
| case KEY_NPAGE: |
| menu_driver(my_menu, REQ_SCR_DPAGE); |
| break; |
| case KEY_PPAGE: |
| menu_driver(my_menu, REQ_SCR_UPAGE); |
| break; |
| } |
| wrefresh(my_menu_win); |
| } |
| |
| /* Unpost and free all the memory taken up */ |
| unpost_menu(my_menu); |
| free_menu(my_menu); |
| for(i = 0; i < n_choices; ++i) |
| free_item(my_items[i]); |
| endwin(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Watch the function call to set_menu_format(). It specifies the number of columns |
| to be 3, thus displaying 3 items per row. We have also switched off the showing |
| descriptions with the function menu_opts_off(). There are couple of functions |
| set_menu_opts(), menu_opts_on() and menu_opts() which can be used to manipulate |
| menu options. The following menu options can be specified.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > O_ONEVALUE |
| Only one item can be selected for this menu. |
| |
| O_SHOWDESC |
| Display the item descriptions when the menu is |
| posted. |
| |
| O_ROWMAJOR |
| Display the menu in row-major order. |
| |
| O_IGNORECASE |
| Ignore the case when pattern-matching. |
| |
| O_SHOWMATCH |
| Move the cursor to within the item name while pat­ |
| tern-matching. |
| |
| O_NONCYCLIC |
| Don't wrap around next-item and previous-item, |
| requests to the other end of the menu.</PRE |
| ><P |
| >All options are on by default. You can switch specific attributes on or off with |
| menu_opts_on() and menu_opts_off() functions. You can also use set_menu_opts() |
| to directly specify the options. The argument to this function should be a OR ed |
| value of some of those above constants. The function menu_opts() can be used to |
| find out a menu's present options. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MULTIVALUEMENUS" |
| >17.7. Multi Valued Menus</A |
| ></H3 |
| ><P |
| >You might be wondering what if you switch off the option O_ONEVALUE. Then the |
| menu becomes multi-valued. That means you can select more than one item. This |
| brings us to the request REQ_TOGGLE_ITEM. Let's see it in action.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMETO" |
| ></A |
| ><P |
| ><B |
| >Example 22. Multi Valued Menus example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <curses.h> |
| #include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Choice 5", |
| "Choice 6", |
| "Choice 7", |
| "Exit", |
| }; |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| int n_choices, i; |
| ITEM *cur_item; |
| |
| /* Initialize curses */ |
| initscr(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| my_items[n_choices] = (ITEM *)NULL; |
| |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Make the menu multi valued */ |
| menu_opts_off(my_menu, O_ONEVALUE); |
| |
| mvprintw(LINES - 3, 0, "Use <SPACE> to select or unselect an item."); |
| mvprintw(LINES - 2, 0, "<ENTER> to see presently selected items(F1 to Exit)"); |
| post_menu(my_menu); |
| refresh(); |
| |
| while((c = getch()) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| case ' ': |
| menu_driver(my_menu, REQ_TOGGLE_ITEM); |
| break; |
| case 10: /* Enter */ |
| { char temp[200]; |
| ITEM **items; |
| |
| items = menu_items(my_menu); |
| temp[0] = '\0'; |
| for(i = 0; i < item_count(my_menu); ++i) |
| if(item_value(items[i]) == TRUE) |
| { strcat(temp, item_name(items[i])); |
| strcat(temp, " "); |
| } |
| move(20, 0); |
| clrtoeol(); |
| mvprintw(20, 0, temp); |
| refresh(); |
| } |
| break; |
| } |
| } |
| |
| free_item(my_items[0]); |
| free_item(my_items[1]); |
| free_menu(my_menu); |
| endwin(); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Whew, A lot of new functions. Let's take them one after another. Firstly, the |
| REQ_TOGGLE_ITEM. In a multi-valued menu, the user should be allowed to select |
| or un select more than one item. The request REQ_TOGGLE_ITEM toggles the present |
| selection. In this case when space is pressed REQ_TOGGLE_ITEM request is sent to |
| menu_driver to achieve the result.</P |
| ><P |
| >Now when the user presses <ENTER> we show the items he presently selected. |
| First we find out the items associated with the menu using the function |
| menu_items(). Then we loop through the items to find out if the item is selected |
| or not. The function item_value() returns TRUE if an item is selected. The |
| function item_count() returns the number of items in the menu. The item name can |
| be found with item_name(). You can also find the description associated with an |
| item using item_description().</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MENUOPT" |
| >17.8. Menu Options</A |
| ></H3 |
| ><P |
| >Well, by this time you must be itching for some difference in your menu, with |
| lots of functionality. I know. You want Colors !!!. You want to create nice |
| menus similar to those text mode <A |
| HREF="http://www.jersey.net/~debinjoe/games/" |
| TARGET="_top" |
| >dos games</A |
| >. The functions |
| set_menu_fore() and set_menu_back() can be used to change the attribute of the |
| selected item and unselected item. The names are misleading. They don't change |
| menu's foreground or background which would have been useless. </P |
| ><P |
| >The function set_menu_grey() can be used to set the display attribute for the |
| non-selectable items in the menu. This brings us to the interesting option for |
| an item the one and only O_SELECTABLE. We can turn it off by the function |
| item_opts_off() and after that that item is not selectable. It's like a grayed |
| item in those fancy windows menus. Let's put these concepts in practice with |
| this example</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMEAT" |
| ></A |
| ><P |
| ><B |
| >Example 23. Menu Options example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Choice 5", |
| "Choice 6", |
| "Choice 7", |
| "Exit", |
| }; |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| int n_choices, i; |
| ITEM *cur_item; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_GREEN, COLOR_BLACK); |
| init_pair(3, COLOR_MAGENTA, COLOR_BLACK); |
| |
| /* Initialize items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| my_items[i] = new_item(choices[i], choices[i]); |
| my_items[n_choices] = (ITEM *)NULL; |
| item_opts_off(my_items[3], O_SELECTABLE); |
| item_opts_off(my_items[6], O_SELECTABLE); |
| |
| /* Create menu */ |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Set fore ground and back ground of the menu */ |
| set_menu_fore(my_menu, COLOR_PAIR(1) | A_REVERSE); |
| set_menu_back(my_menu, COLOR_PAIR(2)); |
| set_menu_grey(my_menu, COLOR_PAIR(3)); |
| |
| /* Post the menu */ |
| mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected"); |
| mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)"); |
| post_menu(my_menu); |
| refresh(); |
| |
| while((c = getch()) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| case 10: /* Enter */ |
| move(20, 0); |
| clrtoeol(); |
| mvprintw(20, 0, "Item selected is : %s", |
| item_name(current_item(my_menu))); |
| pos_menu_cursor(my_menu); |
| break; |
| } |
| } |
| unpost_menu(my_menu); |
| for(i = 0; i < n_choices; ++i) |
| free_item(my_items[i]); |
| free_menu(my_menu); |
| endwin(); |
| } |
| </SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MENUUSERPTR" |
| >17.9. The useful User Pointer</A |
| ></H3 |
| ><P |
| >We can associate a user pointer with each item in the menu. It works the same |
| way as user pointer in panels. It's not touched by menu system. You can store |
| any thing you like in that. I usually use it to store the function to be |
| executed when the menu option is chosen (It's selected and may be the user |
| pressed <ENTER>);</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="MMEUS" |
| ></A |
| ><P |
| ><B |
| >Example 24. Menu User Pointer Usage </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <curses.h> |
| #include <menu.h> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| #define CTRLD 4 |
| |
| char *choices[] = { |
| "Choice 1", |
| "Choice 2", |
| "Choice 3", |
| "Choice 4", |
| "Choice 5", |
| "Choice 6", |
| "Choice 7", |
| "Exit", |
| }; |
| void func(char *name); |
| |
| int main() |
| { ITEM **my_items; |
| int c; |
| MENU *my_menu; |
| int n_choices, i; |
| ITEM *cur_item; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| init_pair(2, COLOR_GREEN, COLOR_BLACK); |
| init_pair(3, COLOR_MAGENTA, COLOR_BLACK); |
| |
| /* Initialize items */ |
| n_choices = ARRAY_SIZE(choices); |
| my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *)); |
| for(i = 0; i < n_choices; ++i) |
| { my_items[i] = new_item(choices[i], choices[i]); |
| /* Set the user pointer */ |
| set_item_userptr(my_items[i], func); |
| } |
| my_items[n_choices] = (ITEM *)NULL; |
| |
| /* Create menu */ |
| my_menu = new_menu((ITEM **)my_items); |
| |
| /* Post the menu */ |
| mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected"); |
| mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)"); |
| post_menu(my_menu); |
| refresh(); |
| |
| while((c = getch()) != KEY_F(1)) |
| { switch(c) |
| { case KEY_DOWN: |
| menu_driver(my_menu, REQ_DOWN_ITEM); |
| break; |
| case KEY_UP: |
| menu_driver(my_menu, REQ_UP_ITEM); |
| break; |
| case 10: /* Enter */ |
| { ITEM *cur; |
| void (*p)(char *); |
| |
| cur = current_item(my_menu); |
| p = item_userptr(cur); |
| p((char *)item_name(cur)); |
| pos_menu_cursor(my_menu); |
| break; |
| } |
| break; |
| } |
| } |
| unpost_menu(my_menu); |
| for(i = 0; i < n_choices; ++i) |
| free_item(my_items[i]); |
| free_menu(my_menu); |
| endwin(); |
| } |
| |
| void func(char *name) |
| { move(20, 0); |
| clrtoeol(); |
| mvprintw(20, 0, "Item selected is : %s", name); |
| } </SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="FORMS" |
| >18. Forms Library</A |
| ></H2 |
| ><P |
| >Well. If you have seen those forms on web pages which take input from users and |
| do various kinds of things, you might be wondering how would any one create such |
| forms in text mode display. It's quite difficult to write those nifty forms in |
| plain ncurses. Forms library tries to provide a basic frame work to build and |
| maintain forms with ease. It has lot of features(functions) which manage |
| validation, dynamic expansion of fields etc.. Let's see it in full flow.</P |
| ><P |
| >A form is a collection of fields; each field can be either a label(static text) |
| or a data-entry location. The forms also library provides functions to divide |
| forms into multiple pages. </P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="FORMBASICS" |
| >18.1. The Basics</A |
| ></H3 |
| ><P |
| >Forms are created in much the same way as menus. First the fields related to the |
| form are created with new_field(). You can set options for the fields, so that |
| they can be displayed with some fancy attributes, validated before the field |
| looses focus etc.. Then the fields are attached to form. After this, the form |
| can be posted to display and is ready to receive inputs. On the similar lines to |
| menu_driver(), the form is manipulated with form_driver(). We can send requests |
| to form_driver to move focus to a certain field, move cursor to end of the field |
| etc.. After the user enters values in the fields and validation done, form can |
| be unposted and memory allocated can be freed.</P |
| ><P |
| >The general flow of control of a forms program looks like this. |
| |
| <P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >Initialize curses</P |
| ></LI |
| ><LI |
| ><P |
| >Create fields using new_field(). You can specify the height and |
| width of the field, and its position on the form.</P |
| ></LI |
| ><LI |
| ><P |
| >Create the forms with new_form() by specifying the fields to be |
| attached with.</P |
| ></LI |
| ><LI |
| ><P |
| >Post the form with form_post() and refresh the screen.</P |
| ></LI |
| ><LI |
| ><P |
| >Process the user requests with a loop and do necessary updates |
| to form with form_driver.</P |
| ></LI |
| ><LI |
| ><P |
| >Unpost the menu with form_unpost()</P |
| ></LI |
| ><LI |
| ><P |
| >Free the memory allocated to menu by free_form()</P |
| ></LI |
| ><LI |
| ><P |
| >Free the memory allocated to the items with free_field()</P |
| ></LI |
| ><LI |
| ><P |
| >End curses</P |
| ></LI |
| ></OL |
| ></P |
| ><P |
| >As you can see, working with forms library is much similar to handling menu |
| library. The following examples will explore various aspects of form |
| processing. Let's start the journey with a simple example. first.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="COMPILEFORMS" |
| >18.2. Compiling With the Forms Library</A |
| ></H3 |
| ><P |
| >To use forms library functions, you have to include form.h and to link the |
| program with forms library the flag -lform should be added along with -lncurses |
| in that order.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > #include <form.h> |
| . |
| . |
| . |
| |
| compile and link: gcc <program file> -lform -lncurses</PRE |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="FFOSI" |
| ></A |
| ><P |
| ><B |
| >Example 25. Forms Basics </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <form.h> |
| |
| int main() |
| { FIELD *field[3]; |
| FORM *my_form; |
| int ch; |
| |
| /* Initialize curses */ |
| initscr(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize the fields */ |
| field[0] = new_field(1, 10, 4, 18, 0, 0); |
| field[1] = new_field(1, 10, 6, 18, 0, 0); |
| field[2] = NULL; |
| |
| /* Set field options */ |
| set_field_back(field[0], A_UNDERLINE); /* Print a line for the option */ |
| field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */ |
| /* Field is filled up */ |
| set_field_back(field[1], A_UNDERLINE); |
| field_opts_off(field[1], O_AUTOSKIP); |
| |
| /* Create the form and post it */ |
| my_form = new_form(field); |
| post_form(my_form); |
| refresh(); |
| |
| mvprintw(4, 10, "Value 1:"); |
| mvprintw(6, 10, "Value 2:"); |
| refresh(); |
| |
| /* Loop through to get user requests */ |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_DOWN: |
| /* Go to next field */ |
| form_driver(my_form, REQ_NEXT_FIELD); |
| /* Go to the end of the present buffer */ |
| /* Leaves nicely at the last character */ |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| case KEY_UP: |
| /* Go to previous field */ |
| form_driver(my_form, REQ_PREV_FIELD); |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| default: |
| /* If this is a normal character, it gets */ |
| /* Printed */ |
| form_driver(my_form, ch); |
| break; |
| } |
| } |
| |
| /* Un post form and free the memory */ |
| unpost_form(my_form); |
| free_form(my_form); |
| free_field(field[0]); |
| free_field(field[1]); |
| |
| endwin(); |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Above example is pretty straight forward. It creates two fields with |
| <TT |
| CLASS="LITERAL" |
| >new_field()</TT |
| >. new_field() takes height, width, |
| starty, startx, number of offscreen rows and number of additional working |
| buffers. The fifth argument number of offscreen rows specifies how much of the |
| field to be shown. If it is zero, the entire field is always displayed otherwise |
| the form will be scrollable when the user accesses not displayed parts of the |
| field. The forms library allocates one buffer per field to store the data user |
| enters. Using the last parameter to new_field() we can specify it to allocate |
| some additional buffers. These can be used for any purpose you like.</P |
| ><P |
| >After creating the fields, back ground attribute of both of them is set to an |
| underscore with set_field_back(). The AUTOSKIP option is turned off using |
| field_opts_off(). If this option is turned on, focus will move to the next |
| field in the form once the active field is filled up completely.</P |
| ><P |
| >After attaching the fields to the form, it is posted. Here on, user inputs are |
| processed in the while loop, by making corresponding requests to form_driver. |
| The details of all the requests to the form_driver() are explained later.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PLAYFIELDS" |
| >18.3. Playing with Fields</A |
| ></H3 |
| ><P |
| >Each form field is associated with a lot of attributes. They can be manipulated |
| to get the required effect and to have fun !!!. So why wait? </P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="FETCHINFO" |
| >18.3.1. Fetching Size and Location of Field</A |
| ></H4 |
| ><P |
| >The parameters we have given at the time of creation of a field can be retrieved |
| with field_info(). It returns height, width, starty, startx, number of offscreen |
| rows, and number of additional buffers into the parameters given to it. It is a |
| sort of inverse of new_field().</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int field_info( FIELD *field, /* field from which to fetch */ |
| int *height, *int width, /* field size */ |
| int *top, int *left, /* upper left corner */ |
| int *offscreen, /* number of offscreen rows */ |
| int *nbuf); /* number of working buffers */</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="MOVEFIELD" |
| >18.3.2. Moving the field</A |
| ></H4 |
| ><P |
| >The location of the field can be moved to a different position with |
| move_field().</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int move_field( FIELD *field, /* field to alter */ |
| int top, int left); /* new upper-left corner */</PRE |
| ><P |
| >As usual, the changed position can be queried with field_infor().</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="JUSTIFYFIELD" |
| >18.3.3. Field Justification</A |
| ></H4 |
| ><P |
| >The justification to be done for the field can be fixed using the function |
| set_field_just().</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > int set_field_just(FIELD *field, /* field to alter */ |
| int justmode); /* mode to set */ |
| int field_just(FIELD *field); /* fetch justify mode of field */</PRE |
| ><P |
| >The justification mode valued accepted and returned by these functions are |
| NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, or JUSTIFY_CENTER.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="FIELDDISPATTRIB" |
| >18.3.4. Field Display Attributes</A |
| ></H4 |
| ><P |
| >As you have seen, in the above example, display attribute for the fields can be |
| set with set_field_fore() and setfield_back(). These functions set foreground |
| and background attribute of the fields. You can also specify a pad character |
| which will be filled in the unfilled portion of the field. The pad character is |
| set with a call to set_field_pad(). Default pad value is a space. The functions |
| field_fore(), field_back, field_pad() can be used to query the present |
| foreground, background attributes and pad character for the field. The following |
| list gives the usage of functions.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| > int set_field_fore(FIELD *field, /* field to alter */ |
| chtype attr); /* attribute to set */ |
| |
| chtype field_fore(FIELD *field); /* field to query */ |
| /* returns foreground attribute */ |
| |
| int set_field_back(FIELD *field, /* field to alter */ |
| chtype attr); /* attribute to set */ |
| |
| chtype field_back(FIELD *field); /* field to query */ |
| /* returns background attribute */ |
| |
| int set_field_pad(FIELD *field, /* field to alter */ |
| int pad); /* pad character to set */ |
| |
| chtype field_pad(FIELD *field); /* field to query */ |
| /* returns present pad character */ </PRE |
| ><P |
| >Though above functions seem quite simple, using colors with set_field_fore() may |
| be frustrating in the beginning. Let me first explain about foreground and |
| background attributes of a field. The foreground attribute is associated with |
| the character. That means a character in the field is printed with the attribute |
| you have set with set_field_fore(). Background attribute is the attribute used |
| to fill background of field, whether any character is there or not. So what |
| about colors? Since colors are always defined in pairs, what is the right way to |
| display colored fields? Here's an example clarifying color attributes.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="FFOAT" |
| ></A |
| ><P |
| ><B |
| >Example 26. Form Attributes example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <form.h> |
| |
| int main() |
| { FIELD *field[3]; |
| FORM *my_form; |
| int ch; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize few color pairs */ |
| init_pair(1, COLOR_WHITE, COLOR_BLUE); |
| init_pair(2, COLOR_WHITE, COLOR_BLUE); |
| |
| /* Initialize the fields */ |
| field[0] = new_field(1, 10, 4, 18, 0, 0); |
| field[1] = new_field(1, 10, 6, 18, 0, 0); |
| field[2] = NULL; |
| |
| /* Set field options */ |
| set_field_fore(field[0], COLOR_PAIR(1));/* Put the field with blue background */ |
| set_field_back(field[0], COLOR_PAIR(2));/* and white foreground (characters */ |
| /* are printed in white */ |
| field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */ |
| /* Field is filled up */ |
| set_field_back(field[1], A_UNDERLINE); |
| field_opts_off(field[1], O_AUTOSKIP); |
| |
| /* Create the form and post it */ |
| my_form = new_form(field); |
| post_form(my_form); |
| refresh(); |
| |
| set_current_field(my_form, field[0]); /* Set focus to the colored field */ |
| mvprintw(4, 10, "Value 1:"); |
| mvprintw(6, 10, "Value 2:"); |
| mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields"); |
| refresh(); |
| |
| /* Loop through to get user requests */ |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_DOWN: |
| /* Go to next field */ |
| form_driver(my_form, REQ_NEXT_FIELD); |
| /* Go to the end of the present buffer */ |
| /* Leaves nicely at the last character */ |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| case KEY_UP: |
| /* Go to previous field */ |
| form_driver(my_form, REQ_PREV_FIELD); |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| default: |
| /* If this is a normal character, it gets */ |
| /* Printed */ |
| form_driver(my_form, ch); |
| break; |
| } |
| } |
| |
| /* Un post form and free the memory */ |
| unpost_form(my_form); |
| free_form(my_form); |
| free_field(field[0]); |
| free_field(field[1]); |
| |
| endwin(); |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >Play with the color pairs and try to understand the foreground and background |
| attributes. In my programs using color attributes, I usually set only the |
| background with set_field_back(). Curses simply doesn't allow defining |
| individual color attributes. </P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="FIELDOPTIONBITS" |
| >18.3.5. Field Option Bits</A |
| ></H4 |
| ><P |
| >There is also a large collection of field option bits you can set to control |
| various aspects of forms processing. You can manipulate them with these |
| functions:</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_opts(FIELD *field, /* field to alter */ |
| int attr); /* attribute to set */ |
| |
| int field_opts_on(FIELD *field, /* field to alter */ |
| int attr); /* attributes to turn on */ |
| |
| int field_opts_off(FIELD *field, /* field to alter */ |
| int attr); /* attributes to turn off */ |
| |
| int field_opts(FIELD *field); /* field to query */ </PRE |
| ><P |
| >The function set_field_opts() can be used to directly set attributes of a field |
| or you can choose to switch a few attributes on and off with field_opts_on() and |
| field_opts_off() selectively. Anytime you can query the attributes of a field |
| with field_opts(). The following is the list of available options. By default, |
| all options are on.</P |
| ><P |
| ></P |
| ><DIV |
| CLASS="VARIABLELIST" |
| ><DL |
| ><DT |
| >O_VISIBLE</DT |
| ><DD |
| ><P |
| >Controls whether the field is visible on the screen. Can be used |
| during form processing to hide or pop up fields depending on the value |
| of parent fields.</P |
| ></DD |
| ><DT |
| >O_ACTIVE</DT |
| ><DD |
| ><P |
| >Controls whether the field is active during forms processing (i.e. |
| visited by form navigation keys). Can be used to make labels or derived |
| fields with buffer values alterable by the forms application, not the user.</P |
| ></DD |
| ><DT |
| >O_PUBLIC</DT |
| ><DD |
| ><P |
| >Controls whether data is displayed during field entry. If this option is |
| turned off on a field, the library will accept and edit data in that field, |
| but it will not be displayed and the visible field cursor will not move. |
| You can turn off the O_PUBLIC bit to define password fields.</P |
| ></DD |
| ><DT |
| >O_EDIT</DT |
| ><DD |
| ><P |
| >Controls whether the field's data can be modified. When this option is |
| off, all editing requests except <TT |
| CLASS="LITERAL" |
| >REQ_PREV_CHOICE</TT |
| > and <TT |
| CLASS="LITERAL" |
| >REQ_NEXT_CHOICE</TT |
| >will |
| fail. Such read-only fields may be useful for help messages.</P |
| ></DD |
| ><DT |
| >O_WRAP</DT |
| ><DD |
| ><P |
| >Controls word-wrapping in multi-line fields. Normally, when any |
| character of a (blank-separated) word reaches the end of the current line, the |
| entire word is wrapped to the next line (assuming there is one). When this |
| option is off, the word will be split across the line break.</P |
| ></DD |
| ><DT |
| >O_BLANK</DT |
| ><DD |
| ><P |
| >Controls field blanking. When this option is on, entering a character at |
| the first field position erases the entire field (except for the just-entered |
| character).</P |
| ></DD |
| ><DT |
| >O_AUTOSKIP</DT |
| ><DD |
| ><P |
| >Controls automatic skip to next field when this one fills. Normally, |
| when the forms user tries to type more data into a field than will fit, |
| the editing location jumps to next field. When this option is off, the |
| user's cursor will hang at the end of the field. This option is ignored |
| in dynamic fields that have not reached their size limit.</P |
| ></DD |
| ><DT |
| >O_NULLOK</DT |
| ><DD |
| ><P |
| >Controls whether validation is applied to |
| blank fields. Normally, it is not; the user can leave a field blank |
| without invoking the usual validation check on exit. If this option is |
| off on a field, exit from it will invoke a validation check.</P |
| ></DD |
| ><DT |
| >O_PASSOK</DT |
| ><DD |
| ><P |
| >Controls whether validation occurs on every exit, or only after |
| the field is modified. Normally the latter is true. Setting O_PASSOK |
| may be useful if your field's validation function may change during |
| forms processing.</P |
| ></DD |
| ><DT |
| >O_STATIC</DT |
| ><DD |
| ><P |
| >Controls whether the field is fixed to its initial dimensions. If you |
| turn this off, the field becomes dynamic and will |
| stretch to fit entered data.</P |
| ></DD |
| ></DL |
| ></DIV |
| ><P |
| >A field's options cannot be changed while the field is currently selected. |
| However, options may be changed on posted fields that are not current. </P |
| ><P |
| >The option values are bit-masks and can be composed with logical-or in |
| the obvious way. You have seen the usage of switching off O_AUTOSKIP option. |
| The following example clarifies usage of some more options. Other options |
| are explained where appropriate.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="FFOOP" |
| ></A |
| ><P |
| ><B |
| >Example 27. Field Options Usage example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <form.h> |
| |
| #define STARTX 15 |
| #define STARTY 4 |
| #define WIDTH 25 |
| |
| #define N_FIELDS 3 |
| |
| int main() |
| { FIELD *field[N_FIELDS]; |
| FORM *my_form; |
| int ch, i; |
| |
| /* Initialize curses */ |
| initscr(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize the fields */ |
| for(i = 0; i < N_FIELDS - 1; ++i) |
| field[i] = new_field(1, WIDTH, STARTY + i * 2, STARTX, 0, 0); |
| field[N_FIELDS - 1] = NULL; |
| |
| /* Set field options */ |
| set_field_back(field[1], A_UNDERLINE); /* Print a line for the option */ |
| |
| field_opts_off(field[0], O_ACTIVE); /* This field is a static label */ |
| field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/ |
| field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */ |
| /* after last character is entered */ |
| |
| /* Create the form and post it */ |
| my_form = new_form(field); |
| post_form(my_form); |
| refresh(); |
| |
| set_field_just(field[0], JUSTIFY_CENTER); /* Center Justification */ |
| set_field_buffer(field[0], 0, "This is a static Field"); |
| /* Initialize the field */ |
| mvprintw(STARTY, STARTX - 10, "Field 1:"); |
| mvprintw(STARTY + 2, STARTX - 10, "Field 2:"); |
| refresh(); |
| |
| /* Loop through to get user requests */ |
| while((ch = getch()) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_DOWN: |
| /* Go to next field */ |
| form_driver(my_form, REQ_NEXT_FIELD); |
| /* Go to the end of the present buffer */ |
| /* Leaves nicely at the last character */ |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| case KEY_UP: |
| /* Go to previous field */ |
| form_driver(my_form, REQ_PREV_FIELD); |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| default: |
| /* If this is a normal character, it gets */ |
| /* Printed */ |
| form_driver(my_form, ch); |
| break; |
| } |
| } |
| |
| /* Un post form and free the memory */ |
| unpost_form(my_form); |
| free_form(my_form); |
| free_field(field[0]); |
| free_field(field[1]); |
| |
| endwin(); |
| return 0; |
| }</SPAN |
| ></PRE |
| ></DIV |
| ><P |
| >This example, though useless, shows the usage of options. If used properly, they |
| can present information very effectively in a form. The second field being not |
| O_PUBLIC, does not show the characters you are typing.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="FIELDSTATUS" |
| >18.3.6. Field Status</A |
| ></H4 |
| ><P |
| >The field status specifies whether the field has got edited or not. It is |
| initially set to FALSE and when user enters something and the data buffer gets |
| modified it becomes TRUE. So a field's status can be queried to find out whether |
| it has been modified or not. The following functions can assist in those |
| operations.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_status(FIELD *field, /* field to alter */ |
| int status); /* status to set */ |
| |
| int field_status(FIELD *field); /* fetch status of field */</PRE |
| ><P |
| >It's better to check the field's status only after after leaving the field, as |
| data buffer might not have been updated yet as the validation is still due. To |
| guarantee that right status is returned, call field_status() either (1) in the |
| field's exit validation check routine, (2) from the field's or form's |
| initialization or termination hooks, or (3) just after a REQ_VALIDATION request |
| has been processed by the forms driver</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="FIELDUSERPTR" |
| >18.3.7. Field User Pointer</A |
| ></H4 |
| ><P |
| >Every field structure contains one pointer that can be used by the user for |
| various purposes. It is not touched by forms library and can be used for any |
| purpose by the user. The following functions set and fetch user pointer.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_userptr(FIELD *field, |
| char *userptr); /* the user pointer you wish to associate */ |
| /* with the field */ |
| |
| char *field_userptr(FIELD *field); /* fetch user pointer of the field */</PRE |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="VARIABLESIZEFIELDS" |
| >18.3.8. Variable-Sized Fields</A |
| ></H4 |
| ><P |
| >If you want a dynamically changing field with variable width, this is the |
| feature you want to put to full use. This will allow the user to enter more data |
| than the original size of the field and let the field grow. According to the |
| field orientation it will scroll horizontally or vertically to incorporate the |
| new data.</P |
| ><P |
| >To make a field dynamically growable, the option O_STATIC should be turned off. |
| This can be done with a |
| <PRE |
| CLASS="PROGRAMLISTING" |
| > field_opts_off(field_pointer, O_STATIC);</PRE |
| ></P |
| ><P |
| >But it's usually not advisable to allow a field to grow infinitely. You can set |
| a maximum limit to the growth of the field with |
| <PRE |
| CLASS="PROGRAMLISTING" |
| >int set_max_field(FIELD *field, /* Field on which to operate */ |
| int max_growth); /* maximum growth allowed for the field */</PRE |
| ></P |
| ><P |
| >The field info for a dynamically growable field can be retrieved by |
| <PRE |
| CLASS="PROGRAMLISTING" |
| >int dynamic_field_info( FIELD *field, /* Field on which to operate */ |
| int *prows, /* number of rows will be filled in this */ |
| int *pcols, /* number of columns will be filled in this*/ |
| int *pmax) /* maximum allowable growth will be filled */ |
| /* in this */</PRE |
| > |
| Though field_info work as usual, it is advisable to use this function to get the |
| proper attributes of a dynamically growable field.</P |
| ><P |
| >Recall the library routine new_field; a new field created with height set to one |
| will be defined to be a one line field. A new field created with height greater |
| than one will be defined to be a multi line field. </P |
| ><P |
| >A one line field with O_STATIC turned off (dynamically growable field) will |
| contain a single fixed row, but the number of columns can increase if the user |
| enters more data than the initial field will hold. The number of columns |
| displayed will remain fixed and the additional data will scroll horizontally. </P |
| ><P |
| >A multi line field with O_STATIC turned off (dynamically growable field) will |
| contain a fixed number of columns, but the number of rows can increase if the |
| user enters more data than the initial field will hold. The number of rows |
| displayed will remain fixed and the additional data will scroll vertically.</P |
| ><P |
| >The above two paragraphs pretty much describe a dynamically growable field's |
| behavior. The way other parts of forms library behaves is described below:</P |
| ><P |
| ></P |
| ><OL |
| TYPE="1" |
| ><LI |
| ><P |
| >The field option O_AUTOSKIP will be ignored if the option O_STATIC is off and |
| there is no maximum growth specified for the field. Currently, O_AUTOSKIP |
| generates an automatic REQ_NEXT_FIELD form driver request when the user types in |
| the last character position of a field. On a growable field with no maximum |
| growth specified, there is no last character position. If a maximum growth is |
| specified, the O_AUTOSKIP option will work as normal if the field has grown to |
| its maximum size. </P |
| ></LI |
| ><LI |
| ><P |
| >The field justification will be ignored if the option O_STATIC is off. |
| Currently, set_field_just can be used to JUSTIFY_LEFT, JUSTIFY_RIGHT, |
| JUSTIFY_CENTER the contents of a one line field. A growable one line field will, |
| by definition, grow and scroll horizontally and may contain more data than can |
| be justified. The return from field_just will be unchanged. </P |
| ></LI |
| ><LI |
| ><P |
| >The overloaded form driver request REQ_NEW_LINE will operate the same way |
| regardless of the O_NL_OVERLOAD form option if the field option O_STATIC is off |
| and there is no maximum growth specified for the field. Currently, if the form |
| option O_NL_OVERLOAD is on, REQ_NEW_LINE implicitly generates a REQ_NEXT_FIELD |
| if called from the last line of a field. If a field can grow without bound, |
| there is no last line, so REQ_NEW_LINE will never implicitly generate a |
| REQ_NEXT_FIELD. If a maximum growth limit is specified and the O_NL_OVERLOAD |
| form option is on, REQ_NEW_LINE will only implicitly generate REQ_NEXT_FIELD if |
| the field has grown to its maximum size and the user is on the last line. </P |
| ></LI |
| ><LI |
| ><P |
| >The library call dup_field will work as usual; it will duplicate the field, |
| including the current buffer size and contents of the field being duplicated. |
| Any specified maximum growth will also be duplicated. </P |
| ></LI |
| ><LI |
| ><P |
| >The library call link_field will work as usual; it will duplicate all field |
| attributes and share buffers with the field being linked. If the O_STATIC field |
| option is subsequently changed by a field sharing buffers, how the system reacts |
| to an attempt to enter more data into the field than the buffer will currently |
| hold will depend on the setting of the option in the current field. </P |
| ></LI |
| ><LI |
| ><P |
| >The library call field_info will work as usual; the variable nrow will contain |
| the value of the original call to new_field. The user should use |
| dynamic_field_info, described above, to query the current size of the buffer.</P |
| ></LI |
| ></OL |
| ><P |
| >Some of the above points make sense only after explaining form driver. We will |
| be looking into that in next few sections.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="FORMWINDOWS" |
| >18.4. Form Windows</A |
| ></H3 |
| ><P |
| >The form windows concept is pretty much similar to menu windows. Every form is |
| associated with a main window and a sub window. The form main window displays |
| any title or border associated or whatever the user wishes. Then the sub window |
| contains all the fields and displays them according to their position. This |
| gives the flexibility of manipulating fancy form displaying very easily. </P |
| ><P |
| >Since this is pretty much similar to menu windows, I am providing an example |
| with out much explanation. The functions are similar and they work the same way.</P |
| ><DIV |
| CLASS="EXAMPLE" |
| ><A |
| NAME="FFOWI" |
| ></A |
| ><P |
| ><B |
| >Example 28. Form Windows Example </B |
| ></P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="INLINEMEDIAOBJECT" |
| >#include <form.h> |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); |
| |
| int main() |
| { |
| FIELD *field[3]; |
| FORM *my_form; |
| WINDOW *my_form_win; |
| int ch, rows, cols; |
| |
| /* Initialize curses */ |
| initscr(); |
| start_color(); |
| cbreak(); |
| noecho(); |
| keypad(stdscr, TRUE); |
| |
| /* Initialize few color pairs */ |
| init_pair(1, COLOR_RED, COLOR_BLACK); |
| |
| /* Initialize the fields */ |
| field[0] = new_field(1, 10, 6, 1, 0, 0); |
| field[1] = new_field(1, 10, 8, 1, 0, 0); |
| field[2] = NULL; |
| |
| /* Set field options */ |
| set_field_back(field[0], A_UNDERLINE); |
| field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */ |
| /* Field is filled up */ |
| set_field_back(field[1], A_UNDERLINE); |
| field_opts_off(field[1], O_AUTOSKIP); |
| |
| /* Create the form and post it */ |
| my_form = new_form(field); |
| |
| /* Calculate the area required for the form */ |
| scale_form(my_form, &rows, &cols); |
| |
| /* Create the window to be associated with the form */ |
| my_form_win = newwin(rows + 4, cols + 4, 4, 4); |
| keypad(my_form_win, TRUE); |
| |
| /* Set main window and sub window */ |
| set_form_win(my_form, my_form_win); |
| set_form_sub(my_form, derwin(my_form_win, rows, cols, 2, 2)); |
| |
| /* Print a border around the main window and print a title */ |
| box(my_form_win, 0, 0); |
| print_in_middle(my_form_win, 1, 0, cols + 4, "My Form", COLOR_PAIR(1)); |
| |
| post_form(my_form); |
| wrefresh(my_form_win); |
| |
| mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields"); |
| refresh(); |
| |
| /* Loop through to get user requests */ |
| while((ch = wgetch(my_form_win)) != KEY_F(1)) |
| { switch(ch) |
| { case KEY_DOWN: |
| /* Go to next field */ |
| form_driver(my_form, REQ_NEXT_FIELD); |
| /* Go to the end of the present buffer */ |
| /* Leaves nicely at the last character */ |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| case KEY_UP: |
| /* Go to previous field */ |
| form_driver(my_form, REQ_PREV_FIELD); |
| form_driver(my_form, REQ_END_LINE); |
| break; |
| default: |
| /* If this is a normal character, it gets */ |
| /* Printed */ |
| form_driver(my_form, ch); |
| break; |
| } |
| } |
| |
| /* Un post form and free the memory */ |
| unpost_form(my_form); |
| free_form(my_form); |
| free_field(field[0]); |
| free_field(field[1]); |
| |
| endwin(); |
| return 0; |
| } |
| |
| void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) |
| { 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; |
| wattron(win, color); |
| mvwprintw(win, y, x, "%s", string); |
| wattroff(win, color); |
| refresh(); |
| }</SPAN |
| ></PRE |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="FILEDVALIDATE" |
| >18.5. Field Validation</A |
| ></H3 |
| ><P |
| >By default, a field will accept any data input by the user. It is possible to |
| attach validation to the field. Then any attempt by the user to leave the field, |
| while it contains data that doesn't match the validation type will fail. Some |
| validation types also have a character-validity check for each time a character |
| is entered in the field.</P |
| ><P |
| >Validation can be attached to a field with the following function. |
| <PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| FIELDTYPE *ftype, /* type to associate */ |
| ...); /* additional arguments*/</PRE |
| > |
| Once set, the validation type for a field can be queried with |
| <PRE |
| CLASS="PROGRAMLISTING" |
| >FIELDTYPE *field_type(FIELD *field); /* field to query */</PRE |
| ></P |
| ><P |
| >The form driver validates the data in a field only when data is entered by the |
| end-user. Validation does not occur when </P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| >the application program changes the field value by calling set_field_buffer. </P |
| ></LI |
| ><LI |
| ><P |
| >linked field values are changed indirectly -- by changing the field to which |
| they are linked</P |
| ></LI |
| ></UL |
| ><P |
| >The following are the pre-defined validation types. You can also specify custom |
| validation, though it's a bit tricky and cumbersome.</P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1069" |
| ></A |
| >TYPE_ALPHA</H1 |
| ><P |
| >This field type accepts alphabetic data; no blanks, no digits, no special |
| characters (this is checked at character-entry time). It is set up with: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_ALPHA, /* type to associate */ |
| int width); /* maximum width of field */</PRE |
| ><P |
| >The width argument sets a minimum width of data. The user has to enter at-least |
| width number of characters before he can leave the field. Typically |
| you'll want to set this to the field width; if it's greater than the |
| field width, the validation check will always fail. A minimum width |
| of zero makes field completion optional. </P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1073" |
| ></A |
| >TYPE_ALNUM</H1 |
| ><P |
| >This field type accepts alphabetic data and digits; no blanks, no special |
| characters (this is checked at character-entry time). It is set up with: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_ALNUM, /* type to associate */ |
| int width); /* maximum width of field */</PRE |
| ><P |
| >The width argument sets a minimum width of data. As with |
| TYPE_ALPHA, typically you'll want to set this to the field width; if it's |
| greater than the field width, the validation check will always fail. A |
| minimum width of zero makes field completion optional. </P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1077" |
| ></A |
| >TYPE_ENUM</H1 |
| ><P |
| >This type allows you to restrict a field's values to be among a specified |
| set of string values (for example, the two-letter postal codes for U.S. |
| states). It is set up with: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_ENUM, /* type to associate */ |
| char **valuelist; /* list of possible values */ |
| int checkcase; /* case-sensitive? */ |
| int checkunique); /* must specify uniquely? */</PRE |
| ><P |
| >The valuelist parameter must point at a NULL-terminated list of |
| valid strings. The checkcase argument, if true, makes comparison |
| with the string case-sensitive. </P |
| ><P |
| >When the user exits a TYPE_ENUM field, the validation procedure tries to |
| complete the data in the buffer to a valid entry. If a complete choice string |
| has been entered, it is of course valid. But it is also possible to enter a |
| prefix of a valid string and have it completed for you. </P |
| ><P |
| >By default, if you enter such a prefix and it matches more than one value |
| in the string list, the prefix will be completed to the first matching |
| value. But the checkunique argument, if true, requires prefix |
| matches to be unique in order to be valid. </P |
| ><P |
| >The REQ_NEXT_CHOICE and REQ_PREV_CHOICE input requests can be particularly |
| useful with these fields. </P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1084" |
| ></A |
| >TYPE_INTEGER</H1 |
| ><P |
| >This field type accepts an integer. It is set up as follows: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_INTEGER, /* type to associate */ |
| int padding, /* # places to zero-pad to */ |
| int vmin, int vmax); /* valid range */</PRE |
| ><P |
| >Valid characters consist of an optional leading minus and digits. |
| The range check is performed on exit. If the range maximum is less |
| than or equal to the minimum, the range is ignored. </P |
| ><P |
| >If the value passes its range check, it is padded with as many leading |
| zero digits as necessary to meet the padding argument. </P |
| ><P |
| >A TYPE_INTEGER value buffer can conveniently be interpreted with the C library |
| function atoi(3).</P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1090" |
| ></A |
| >TYPE_NUMERIC</H1 |
| ><P |
| >This field type accepts a decimal number. It is set up as follows: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_NUMERIC, /* type to associate */ |
| int padding, /* # places of precision */ |
| int vmin, int vmax); /* valid range */</PRE |
| ><P |
| >Valid characters consist of an optional leading minus and digits. possibly |
| including a decimal point. The range check is performed on exit. If the |
| range maximum is less than or equal to the minimum, the range is |
| ignored. </P |
| ><P |
| >If the value passes its range check, it is padded with as many trailing |
| zero digits as necessary to meet the padding argument. </P |
| ><P |
| >A TYPE_NUMERIC value buffer can conveniently be interpreted with the C library |
| function atof(3).</P |
| ><H1 |
| CLASS="BRIDGEHEAD" |
| ><A |
| NAME="AEN1096" |
| ></A |
| >TYPE_REGEXP</H1 |
| ><P |
| >This field type accepts data matching a regular expression. It is set up |
| as follows: </P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_field_type(FIELD *field, /* field to alter */ |
| TYPE_REGEXP, /* type to associate */ |
| char *regexp); /* expression to match */</PRE |
| ><P |
| >The syntax for regular expressions is that of regcomp(3). |
| The check for regular-expression match is performed on exit.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="FORMDRIVER" |
| >18.6. Form Driver: The work horse of the forms system</A |
| ></H3 |
| ><P |
| >As in the menu system, form_driver() plays a very important role in forms |
| system. All types of requests to forms system should be funneled through |
| form_driver().</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int form_driver(FORM *form, /* form on which to operate */ |
| int request) /* form request code */</PRE |
| ><P |
| >As you have seen some of the examples above, you have to be in a loop looking |
| for user input and then decide whether it's a field data or a form request. The |
| form requests are then passed to form_driver() to do the work.</P |
| ><P |
| >The requests roughly can be divided into following categories. Different |
| requests and their usage is explained below:</P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="PAGENAVREQ" |
| >18.6.1. Page Navigation Requests</A |
| ></H4 |
| ><P |
| >These requests cause page-level moves through the form, triggering display of a |
| new form screen. A form can be made of multiple pages. If you have a big form |
| with lot of fields and logical sections, then you can divide the form into |
| pages. The function set_new_page() to set a new page at the field specified.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >int set_new_page(FIELD *field,/* Field at which page break to be set or unset */ |
| bool new_page_flag); /* should be TRUE to put a break */</PRE |
| ><P |
| >The following requests allow you to move to different pages</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_PAGE</I |
| ></SPAN |
| > Move to the next form page.</P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_PAGE</I |
| ></SPAN |
| > Move to the previous |
| form page.</P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_FIRST_PAGE</I |
| ></SPAN |
| > Move to the first form page.</P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_LAST_PAGE</I |
| ></SPAN |
| > Move to the last form page. </P |
| ></LI |
| ></UL |
| ><P |
| >These requests treat the list as cyclic; that is, REQ_NEXT_PAGE from the |
| last page goes to the first, and REQ_PREV_PAGE from the first page goes to |
| the last.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="INTERFIELDNAVREQ" |
| >18.6.2. Inter-Field Navigation Requests</A |
| ></H4 |
| ><P |
| >These requests handle navigation between fields on the same page.</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_FIELD</I |
| ></SPAN |
| > |
| Move to next field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_FIELD</I |
| ></SPAN |
| > |
| Move to previous field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_FIRST_FIELD</I |
| ></SPAN |
| > |
| Move to the first field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_LAST_FIELD</I |
| ></SPAN |
| > |
| Move to the last field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SNEXT_FIELD</I |
| ></SPAN |
| > |
| Move to sorted next field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SPREV_FIELD</I |
| ></SPAN |
| > |
| Move to sorted previous field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SFIRST_FIELD</I |
| ></SPAN |
| > |
| Move to the sorted first field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SLAST_FIELD</I |
| ></SPAN |
| > |
| Move to the sorted last field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_LEFT_FIELD</I |
| ></SPAN |
| > |
| Move left to field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_RIGHT_FIELD</I |
| ></SPAN |
| > |
| Move right to field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_UP_FIELD</I |
| ></SPAN |
| > |
| Move up to field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DOWN_FIELD</I |
| ></SPAN |
| > |
| Move down to field. </P |
| ></LI |
| ></UL |
| ><P |
| >These requests treat the list of fields on a page as cyclic; that is, |
| REQ_NEXT_FIELD from the last field goes to the first, and REQ_PREV_FIELD |
| from the first field goes to the last. The order of the fields for these |
| (and the REQ_FIRST_FIELD and REQ_LAST_FIELD requests) is simply the order of |
| the field pointers in the form array (as set up by new_form() or |
| set_form_fields()</P |
| ><P |
| >It is also possible to traverse the fields as if they had been sorted in |
| screen-position order, so the sequence goes left-to-right and top-to-bottom. |
| To do this, use the second group of four sorted-movement requests.</P |
| ><P |
| >Finally, it is possible to move between fields using visual directions up, |
| down, right, and left. To accomplish this, use the third group of four |
| requests. Note, however, that the position of a form for purposes of these |
| requests is its upper-left corner.</P |
| ><P |
| >For example, suppose you have a multi-line field B, and two single-line |
| fields A and C on the same line with B, with A to the left of B and C to the |
| right of B. A REQ_MOVE_RIGHT from A will go to B only if A, B, and C all |
| share the same first line; otherwise it will skip over B to C.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="INTRAFIELDNAVREQ" |
| >18.6.3. Intra-Field Navigation Requests</A |
| ></H4 |
| ><P |
| >These requests drive movement of the edit cursor within the currently |
| selected field.</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_CHAR</I |
| ></SPAN |
| > |
| Move to next character. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_CHAR</I |
| ></SPAN |
| > |
| Move to previous character. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_LINE</I |
| ></SPAN |
| > |
| Move to next line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_LINE</I |
| ></SPAN |
| > |
| Move to previous line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_WORD</I |
| ></SPAN |
| > |
| Move to next word. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_WORD</I |
| ></SPAN |
| > |
| Move to previous word. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_BEG_FIELD</I |
| ></SPAN |
| > |
| Move to beginning of field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_END_FIELD</I |
| ></SPAN |
| > |
| Move to end of field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_BEG_LINE</I |
| ></SPAN |
| > |
| Move to beginning of line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_END_LINE</I |
| ></SPAN |
| > |
| Move to end of line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_LEFT_CHAR</I |
| ></SPAN |
| > |
| Move left in field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_RIGHT_CHAR</I |
| ></SPAN |
| > |
| Move right in field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_UP_CHAR</I |
| ></SPAN |
| > |
| Move up in field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DOWN_CHAR</I |
| ></SPAN |
| > |
| Move down in field. </P |
| ></LI |
| ></UL |
| ><P |
| >Each word is separated from the previous and next characters by whitespace. |
| The commands to move to beginning and end of line or field look for the |
| first or last non-pad character in their ranges.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="SCROLLREQ" |
| >18.6.4. Scrolling Requests</A |
| ></H4 |
| ><P |
| >Fields that are dynamic and have grown and fields explicitly created with |
| offscreen rows are scrollable. One-line fields scroll horizontally; |
| multi-line fields scroll vertically. Most scrolling is triggered by editing |
| and intra-field movement (the library scrolls the field to keep the cursor |
| visible). It is possible to explicitly request scrolling with the following |
| requests:</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_FLINE</I |
| ></SPAN |
| > |
| Scroll vertically forward a line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_BLINE</I |
| ></SPAN |
| > |
| Scroll vertically backward a line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_FPAGE</I |
| ></SPAN |
| > |
| Scroll vertically forward a page. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_BPAGE</I |
| ></SPAN |
| > |
| Scroll vertically backward a page. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_FHPAGE</I |
| ></SPAN |
| > |
| Scroll vertically forward half a page. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_BHPAGE</I |
| ></SPAN |
| > |
| Scroll vertically backward half a page. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_FCHAR</I |
| ></SPAN |
| > |
| Scroll horizontally forward a character. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_BCHAR</I |
| ></SPAN |
| > |
| Scroll horizontally backward a character. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_HFLINE</I |
| ></SPAN |
| > |
| Scroll horizontally one field width forward. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_HBLINE</I |
| ></SPAN |
| > |
| Scroll horizontally one field width backward. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_HFHALF</I |
| ></SPAN |
| > |
| Scroll horizontally one half field width forward. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_SCR_HBHALF</I |
| ></SPAN |
| > |
| Scroll horizontally one half field width backward. </P |
| ></LI |
| ></UL |
| ><P |
| >For scrolling purposes, a page of a field is the height of its visible part.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="EDITREQ" |
| >18.6.5. Editing Requests</A |
| ></H4 |
| ><P |
| >When you pass the forms driver an ASCII character, it is treated as a |
| request to add the character to the field's data buffer. Whether this is an |
| insertion or a replacement depends on the field's edit mode (insertion is |
| the default.</P |
| ><P |
| >The following requests support editing the field and changing the edit mode:</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_INS_MODE</I |
| ></SPAN |
| > |
| Set insertion mode. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_OVL_MODE</I |
| ></SPAN |
| > |
| Set overlay mode. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEW_LINE</I |
| ></SPAN |
| > |
| New line request (see below for explanation). </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_INS_CHAR</I |
| ></SPAN |
| > |
| Insert space at character location. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_INS_LINE</I |
| ></SPAN |
| > |
| Insert blank line at character location. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DEL_CHAR</I |
| ></SPAN |
| > |
| Delete character at cursor. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DEL_PREV</I |
| ></SPAN |
| > |
| Delete previous word at cursor. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DEL_LINE</I |
| ></SPAN |
| > |
| Delete line at cursor. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_DEL_WORD</I |
| ></SPAN |
| > |
| Delete word at cursor. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_CLR_EOL</I |
| ></SPAN |
| > |
| Clear to end of line. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_CLR_EOF</I |
| ></SPAN |
| > |
| Clear to end of field. </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_CLR_FIELD</I |
| ></SPAN |
| > |
| Clear entire field. </P |
| ></LI |
| ></UL |
| ><P |
| >The behavior of the REQ_NEW_LINE and REQ_DEL_PREV requests is complicated |
| and partly controlled by a pair of forms options. The special cases are |
| triggered when the cursor is at the beginning of a field, or on the last |
| line of the field.</P |
| ><P |
| >First, we consider REQ_NEW_LINE:</P |
| ><P |
| >The normal behavior of REQ_NEW_LINE in insert mode is to break the current |
| line at the position of the edit cursor, inserting the portion of the |
| current line after the cursor as a new line following the current and moving |
| the cursor to the beginning of that new line (you may think of this as |
| inserting a newline in the field buffer).</P |
| ><P |
| >The normal behavior of REQ_NEW_LINE in overlay mode is to clear the current |
| line from the position of the edit cursor to end of line. The cursor is then |
| moved to the beginning of the next line.</P |
| ><P |
| >However, REQ_NEW_LINE at the beginning of a field, or on the last line of a |
| field, instead does a REQ_NEXT_FIELD. O_NL_OVERLOAD option is off, this |
| special action is disabled.</P |
| ><P |
| >Now, let us consider REQ_DEL_PREV:</P |
| ><P |
| >The normal behavior of REQ_DEL_PREV is to delete the previous character. If |
| insert mode is on, and the cursor is at the start of a line, and the text on |
| that line will fit on the previous one, it instead appends the contents of |
| the current line to the previous one and deletes the current line (you may |
| think of this as deleting a newline from the field buffer).</P |
| ><P |
| >However, REQ_DEL_PREV at the beginning of a field is instead treated as a |
| REQ_PREV_FIELD.</P |
| ><P |
| >If the O_BS_OVERLOAD option is off, this special action is disabled and the |
| forms driver just returns E_REQUEST_DENIED.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="ORDERREQ" |
| >18.6.6. Order Requests</A |
| ></H4 |
| ><P |
| >If the type of your field is ordered, and has associated functions for |
| getting the next and previous values of the type from a given value, there |
| are requests that can fetch that value into the field buffer:</P |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_NEXT_CHOICE</I |
| ></SPAN |
| > |
| Place the successor value of the current value in the buffer. |
| </P |
| ></LI |
| ><LI |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >REQ_PREV_CHOICE</I |
| ></SPAN |
| > |
| Place the predecessor value of the current value in the buffer. |
| </P |
| ></LI |
| ></UL |
| ><P |
| >Of the built-in field types, only TYPE_ENUM has built-in successor and |
| predecessor functions. When you define a field type of your own (see Custom |
| Validation Types), you can associate our own ordering functions.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="APPLICCOMMANDS" |
| >18.6.7. Application Commands</A |
| ></H4 |
| ><P |
| >Form requests are represented as integers above the curses value greater than |
| KEY_MAX and less than or equal to the constant MAX_COMMAND. A value within this |
| range gets ignored by form_driver(). So this can be used for any purpose by the |
| application. It can be treated as an application specific action and take |
| corresponding action.</P |
| ></DIV |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="TOOLS" |
| >19. Tools and Widget Libraries</A |
| ></H2 |
| ><P |
| > |
| Now that you have seen the capabilities of ncurses and its sister libraries, you |
| are rolling your sleeves up and gearing for a project that heavily manipulates |
| screen. But wait.. It can be pretty difficult to write and maintain complex GUI |
| widgets in plain ncurses or even with the additional libraries. There are some |
| ready-to-use tools and widget libraries that can be used instead of writing your |
| own widgets. You can use some of them, get ideas from the code, or even extend |
| them.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="CDK" |
| >19.1. CDK (Curses Development Kit)</A |
| ></H3 |
| ><P |
| >In the author's words </P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| > |
| CDK stands for 'Curses Development Kit' and it currently contains 21 ready |
| to use widgets which facilitate the speedy development of full screen |
| curses programs. </I |
| ></SPAN |
| ></P |
| ><P |
| >The kit provides some useful widgets, which can be used in your programs |
| directly. It's pretty well written and the documentation is very good. The |
| examples in the examples directory can be a good place to start for beginners. |
| The CDK can be downloaded from <A |
| HREF="http://invisible-island.net/cdk/" |
| TARGET="_top" |
| >http://invisible-island.net/cdk/</A |
| > |
| . Follow the instructions in |
| README file to install it.</P |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="WIDGETLIST" |
| >19.1.1. Widget List</A |
| ></H4 |
| ><P |
| >The following is the list of widgets provided with cdk and their description.</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >Widget Type Quick Description |
| =========================================================================== |
| Alphalist Allows a user to select from a list of words, with |
| the ability to narrow the search list by typing in a |
| few characters of the desired word. |
| Buttonbox This creates a multiple button widget. |
| Calendar Creates a little simple calendar widget. |
| Dialog Prompts the user with a message, and the user |
| can pick an answer from the buttons provided. |
| Entry Allows the user to enter various types of information. |
| File Selector A file selector built from Cdk base widgets. This |
| example shows how to create more complicated widgets |
| using the Cdk widget library. |
| Graph Draws a graph. |
| Histogram Draws a histogram. |
| Item List Creates a pop up field which allows the user to select |
| one of several choices in a small field. Very useful |
| for things like days of the week or month names. |
| Label Displays messages in a pop up box, or the label can be |
| considered part of the screen. |
| Marquee Displays a message in a scrolling marquee. |
| Matrix Creates a complex matrix with lots of options. |
| Menu Creates a pull-down menu interface. |
| Multiple Line Entry A multiple line entry field. Very useful |
| for long fields. (like a description |
| field) |
| Radio List Creates a radio button list. |
| Scale Creates a numeric scale. Used for allowing a user to |
| pick a numeric value and restrict them to a range of |
| values. |
| Scrolling List Creates a scrolling list/menu list. |
| Scrolling Window Creates a scrolling log file viewer. Can add |
| information into the window while its running. |
| A good widget for displaying the progress of |
| something. (akin to a console window) |
| Selection List Creates a multiple option selection list. |
| Slider Akin to the scale widget, this widget provides a |
| visual slide bar to represent the numeric value. |
| Template Creates a entry field with character sensitive |
| positions. Used for pre-formatted fields like |
| dates and phone numbers. |
| Viewer This is a file/information viewer. Very useful |
| when you need to display loads of information. |
| ===========================================================================</PRE |
| ><P |
| >A few of the widgets are modified by Thomas Dickey in recent versions.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="CDKATTRACT" |
| >19.1.2. Some Attractive Features</A |
| ></H4 |
| ><P |
| >Apart from making our life easier with readily usable widgets, cdk solves one |
| frustrating problem with printing multi colored strings, justified strings |
| elegantly. Special formatting tags can be embedded in the strings which are |
| passed to CDK functions. For Example</P |
| ><P |
| >If the string</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| >"</B/1>This line should have a yellow foreground and a blue |
| background.<!1>"</PRE |
| ><P |
| >given as a parameter to newCDKLabel(), it prints the line with yellow foreground |
| and blue background. There are other tags available for justifying string, |
| embedding special drawing characters etc.. Please refer to the man page |
| cdk_display(3X) for details. The man page explains the usage with nice examples.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT3" |
| ><HR><H4 |
| CLASS="SECT3" |
| ><A |
| NAME="CDKCONCLUSION" |
| >19.1.3. Conclusion</A |
| ></H4 |
| ><P |
| >All in all, CDK is a well-written package of widgets, which if used properly can |
| form a strong frame work for developing complex GUI.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="DIALOG" |
| >19.2. The dialog</A |
| ></H3 |
| ><P |
| >Long long ago, in September 1994, when few people knew linux, Jeff Tranter wrote |
| an <A |
| HREF="http://www2.linuxjournal.com/lj-issues/issue5/2807.html" |
| TARGET="_top" |
| >article</A |
| > on dialog in Linux Journal. He starts the article with these words..</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >Linux is based on the Unix operating system, but also features a number of |
| unique and useful kernel features and application programs that often go beyond |
| what is available under Unix. One little-known gem is "dialog", a utility for |
| creating professional-looking dialog boxes from within shell scripts. This |
| article presents a tutorial introduction to the dialog utility, and shows |
| examples of how and where it can be used</I |
| ></SPAN |
| ></P |
| ><P |
| > |
| As he explains, dialog is a real gem in making professional-looking dialog boxes |
| with ease. It creates a variety of dialog boxes, menus, check lists etc.. It is |
| usually installed by default. If not, you can download it from <A |
| HREF="http://invisible-island.net/dialog/" |
| TARGET="_top" |
| >Thomas Dickey</A |
| >'s site. </P |
| ><P |
| >The above-mentioned article gives a very good overview of its uses and |
| capabilites. The man page has more details. It can be used in variety of |
| situations. One good example is building of linux kernel in text mode. Linux |
| kernel uses a modified version of dialog tailored for its needs. </P |
| ><P |
| >dialog was initially designed to be used with shell scripts. If you want to use |
| its functionality in a c program, then you can use libdialog. The documentation |
| regarding this is sparse. Definitive reference is the dialog.h header file which |
| comes with the library. You may need to hack here and there to get the required |
| output. The source is easily customizable. I have used it on a number of |
| occasions by modifying the code.</P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="PERLCURSES" |
| >19.3. Perl Curses Modules CURSES::FORM and CURSES::WIDGETS</A |
| ></H3 |
| ><P |
| >The perl module Curses, Curses::Form and Curses::Widgets give access to curses |
| from perl. If you have curses and basic perl is installed, you can get these |
| modules from <A |
| HREF="http://www.cpan.org/modules/01modules.index.html" |
| TARGET="_top" |
| > CPAN |
| All Modules page</A |
| >. Get the three zipped modules in the Curses category. |
| Once installed you can use these modules from perl scripts like any other |
| module. For more information on perl modules see perlmod man page. The above |
| modules come with good documentation and they have some demo scripts to test the |
| functionality. Though the widgets provided are very rudimentary, these modules |
| provide good access to curses library from perl.</P |
| ><P |
| >Some of my code examples are converted to perl by Anuradha Ratnaweera and they |
| are available in the <TT |
| CLASS="LITERAL" |
| >perl</TT |
| > directory.</P |
| ><P |
| > |
| For more information see man pages Curses(3) , Curses::Form(3) and |
| Curses::Widgets(3). These pages are installed only when the above modules are |
| acquired and installed.</P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="JUSTFORFUN" |
| >20. Just For Fun !!!</A |
| ></H2 |
| ><P |
| >This section contains few programs written by me just for fun. They don't |
| signify a better programming practice or the best way of using ncurses. They are |
| provided here so as to allow beginners to get ideas and add more programs to |
| this section. If you have written a couple of nice, simple programs in curses |
| and want them to included here, contact <A |
| HREF="mailto:ppadala@gmail.com" |
| TARGET="_top" |
| >me</A |
| >.</P |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="GAMEOFLIFE" |
| >20.1. The Game of Life</A |
| ></H3 |
| ><P |
| >Game of life is a wonder of math. In |
| <A |
| HREF="http://www.math.com/students/wonders/life/life.html" |
| TARGET="_top" |
| >Paul Callahan</A |
| >'s words</P |
| ><PRE |
| CLASS="PROGRAMLISTING" |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >The Game of Life (or simply Life) is not a game in the conventional sense. There |
| are no players, and no winning or losing. Once the "pieces" are placed in the |
| starting position, the rules determine everything that happens later. |
| Nevertheless, Life is full of surprises! In most cases, it is impossible to look |
| at a starting position (or pattern) and see what will happen in the future. The |
| only way to find out is to follow the rules of the game.</I |
| ></SPAN |
| ></PRE |
| ><P |
| >This program starts with a simple inverted U pattern and shows how wonderful |
| life works. There is a lot of room for improvement in the program. You can let |
| the user enter pattern of his choice or even take input from a file. You can |
| also change rules and play with a lot of variations. Search on <A |
| HREF="http://www.google.com" |
| TARGET="_top" |
| >google</A |
| > for interesting information on game |
| of life.</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/life.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="MAGIC" |
| >20.2. Magic Square</A |
| ></H3 |
| ><P |
| >Magic Square, another wonder of math, is very simple to understand but very |
| difficult to make. In a magic square sum of the numbers in each row, each column |
| is equal. Even diagnol sum can be equal. There are many variations which have |
| special properties.</P |
| ><P |
| >This program creates a simple magic square of odd order.</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/magic.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="HANOI" |
| >20.3. Towers of Hanoi</A |
| ></H3 |
| ><P |
| >The famous towers of hanoi solver. The aim of the game is to move the disks on |
| the first peg to last peg, using middle peg as a temporary stay. The catch is |
| not to place a larger disk over a small disk at any time.</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/hanoi.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="QUEENS" |
| >20.4. Queens Puzzle</A |
| ></H3 |
| ><P |
| >The objective of the famous N-Queen puzzle is to put N queens on a N X N chess |
| board without attacking each other. </P |
| ><P |
| >This program solves it with a simple backtracking technique.</P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/queens.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="SHUFFLE" |
| >20.5. Shuffle</A |
| ></H3 |
| ><P |
| >A fun game, if you have time to kill. </P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/shuffle.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ><DIV |
| CLASS="SECT2" |
| ><HR><H3 |
| CLASS="SECT2" |
| ><A |
| NAME="TT" |
| >20.6. Typing Tutor</A |
| ></H3 |
| ><P |
| >A simple typing tutor, I created more out of need than for ease of use. If you |
| know how to put your fingers correctly on the keyboard, but lack practice, this |
| can be helpful. </P |
| ><P |
| ><SPAN |
| CLASS="emphasis" |
| ><I |
| CLASS="EMPHASIS" |
| >File Path: JustForFun/tt.c</I |
| ></SPAN |
| ></P |
| ></DIV |
| ></DIV |
| ><DIV |
| CLASS="SECT1" |
| ><HR><H2 |
| CLASS="SECT1" |
| ><A |
| NAME="REF" |
| >21. References</A |
| ></H2 |
| ><P |
| ></P |
| ><UL |
| ><LI |
| ><P |
| >NCURSES man pages </P |
| ></LI |
| ><LI |
| ><P |
| >NCURSES FAQ at <A |
| HREF="http://invisible-island.net/ncurses/ncurses.faq.html" |
| TARGET="_top" |
| >http://invisible-island.net/ncurses/ncurses.faq.html</A |
| > |
| </P |
| ></LI |
| ><LI |
| ><P |
| >Writing programs with NCURSES by Eric Raymond and Zeyd M. |
| Ben-Halim at |
| <A |
| HREF="http://invisible-island.net/ncurses/ncurses-intro.html" |
| TARGET="_top" |
| >http://invisible-island.net/ncurses/ncurses-intro.html</A |
| > - somewhat |
| obsolete. I was inspired by this document and the structure of this HOWTO |
| follows from the original document</P |
| ></LI |
| ></UL |
| ></DIV |
| ></DIV |
| ></BODY |
| ></HTML |
| > |