MULTI-LEVEL PULL-DOWN MENUS USER'S GUIDE Version 2.0 April 15, 1989 Conversion to Turbo C by Jordan Gallagher / Wisdom Research Copyright (C) 1989 Eagle Performance Software All Rights Reserved. _______ ____|__ | (tm) --| | |------------------- | ____|__ | Association of | | |_| Shareware |__| o | Professionals -----| | |--------------------- |___|___| MEMBER PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 T A B L E O F C O N T E N T S 1. INTRODUCTION . . . . . . . . . . . . . . . . . . . . . 4 Features . . . . . . . . . . . . . . . . . . . . . . 4 Using the Manuals . . . . . . . . . . . . . . . . . . 4 Licensing . . . . . . . . . . . . . . . . . . . . . . 5 Customer Service . . . . . . . . . . . . . . . . . . 5 ASP . . . . . . . . . . . . . . . . . . . . . . . . . 6 2. GETTING STARTED . . . . . . . . . . . . . . . . . . . . 7 Distribution Files . . . . . . . . . . . . . . . . . 7 Demonstration . . . . . . . . . . . . . . . . . . . . 8 3. PROGRAMMING MENUS . . . . . . . . . . . . . . . . . . . 11 Using the Shell . . . . . . . . . . . . . . . . . . . 11 Menu Modes . . . . . . . . . . . . . . . . . . . . . 11 Line Modes . . . . . . . . . . . . . . . . . . . . . 14 Hilite Control . . . . . . . . . . . . . . . . . . . 16 Adding Lines . . . . . . . . . . . . . . . . . . . . 16 Adding Menus . . . . . . . . . . . . . . . . . . . . 17 Adding Submenus . . . . . . . . . . . . . . . . . . . 18 Help Messages . . . . . . . . . . . . . . . . . . . . 20 Help Windows . . . . . . . . . . . . . . . . . . . . 21 Default Attributes . . . . . . . . . . . . . . . . . 24 Standard Borders . . . . . . . . . . . . . . . . . . 25 Control Flags . . . . . . . . . . . . . . . . . . . . 25 Summary . . . . . . . . . . . . . . . . . . . . . . . 27 4. SCREEN DESIGN . . . . . . . . . . . . . . . . . . . . . 28 Status Line . . . . . . . . . . . . . . . . . . . . . 28 Top Line Menu . . . . . . . . . . . . . . . . . . . . 28 Main Menu Row . . . . . . . . . . . . . . . . . . . . 28 Submenu Row . . . . . . . . . . . . . . . . . . . . . 29 Work Windows . . . . . . . . . . . . . . . . . . . . 29 Message Line . . . . . . . . . . . . . . . . . . . . 29 Help Windows . . . . . . . . . . . . . . . . . . . . 30 Start-Up Menu . . . . . . . . . . . . . . . . . . . . 30 Overriding Defaults . . . . . . . . . . . . . . . . . 31 Summary . . . . . . . . . . . . . . . . . . . . . . . 32 5. DATA WINDOWS . . . . . . . . . . . . . . . . . . . . . 33 Data Window Parts . . . . . . . . . . . . . . . . . . 33 Data Window Structure . . . . . . . . . . . . . . . . 33 Data Window Variable . . . . . . . . . . . . . . . . 34 Fields . . . . . . . . . . . . . . . . . . . . . . . 35 Titles . . . . . . . . . . . . . . . . . . . . . . . 37 Editing Keys . . . . . . . . . . . . . . . . . . . . 37 Key Sets . . . . . . . . . . . . . . . . . . . . . . 38 Key Translation . . . . . . . . . . . . . . . . . . . 39 Range Checking . . . . . . . . . . . . . . . . . . . 40 Help Messages . . . . . . . . . . . . . . . . . . . . 42 Help Windows . . . . . . . . . . . . . . . . . . . . 42 Default Attributes and Border . . . . . . . . . . . . 42 Default Location . . . . . . . . . . . . . . . . . . 43 2 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Summary . . . . . . . . . . . . . . . . . . . . . . . 43 6. DATA ENTRY . . . . . . . . . . . . . . . . . . . . . . 44 Data Entry vs. Data Windows . . . . . . . . . . . . . 44 Data Entry Structure . . . . . . . . . . . . . . . . 44 Adding Entries . . . . . . . . . . . . . . . . . . . 45 Displaying Fields . . . . . . . . . . . . . . . . . . 45 Sequential Entry . . . . . . . . . . . . . . . . . . 46 Edit Mode . . . . . . . . . . . . . . . . . . . . . . 48 Field Attributes . . . . . . . . . . . . . . . . . . 49 Single Entry . . . . . . . . . . . . . . . . . . . . 49 Summary . . . . . . . . . . . . . . . . . . . . . . . 50 7. WORK WINDOWS . . . . . . . . . . . . . . . . . . . . . 51 Making Steps . . . . . . . . . . . . . . . . . . . . 51 Reading the Keyboard . . . . . . . . . . . . . . . . 52 Multi-Level Windows . . . . . . . . . . . . . . . . . 54 Managing Winddows . . . . . . . . . . . . . . . . . . 55 8. USER WINDOWS . . . . . . . . . . . . . . . . . . . . . 57 Pull-Down Directory . . . . . . . . . . . . . . . . . 57 Interface . . . . . . . . . . . . . . . . . . . . . . 58 9. COMPILING THE SOURCE CODE . . . . . . . . . . . . . . . 60 Using Various Compiler Options . . . . . . . . . . . 60 Conditional Compilation . . . . . . . . . . . . . . . 60 10. TROUBLE SHOOTING . . . . . . . . . . . . . . . . . . . 62 Goof Module . . . . . . . . . . . . . . . . . . . . . 62 Pointer Addresses . . . . . . . . . . . . . . . . . . 62 Multi-tasking . . . . . . . . . . . . . . . . . . . . 63 Customer Service . . . . . . . . . . . . . . . . . . 63 APPENDIX A: Other Products . . . . . . . . . . . . . . . . 64 APPENDIX B: Credits . . . . . . . . . . . . . . . . . . . 66 APPENDIX C: Glossary . . . . . . . . . . . . . . . . . . . 67 3 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 1. I N T R O D U C T I O N FEATURES Welcome to PULLC multi-level pull-down menus! You have just obtained a copy of the highest performance pull-down menu utilities available today for Turbo C 2.0 (TC2). Both novice and professional programmers will appreciate these simple and very powerful utilities that are fully featured and fully configurable. They work on all IBM compatibles, including PS/2 and 3270 PC on any video page or any text mode. Here are some of the features you will discover: - Uses the powerful routines of QWIKC and WNDWC. - Work window(s) and complete interface for menus - Pull-down menus with 3 menu modes and 8 line modes - Pull-down file directory - Highlighted command letters - Unlimited levels of submenus - Unlimited data entry windows for 9 types of data - Data entry for the work window(s) Free field entry with either fixed column or flexible column length. Full editing capability including insert cursor mode Fields are easily selected with the cursor keys Automatic NumLock for numerical data entry Right or left justification for data entry output Error messages for invalid data entries Error messages for data entries out of range - Automatic sizes and locations for menus - Operation by cursor keys or command keys - Pull/Pop between work window and nested submenu(s) - Programmable control of pull and pop sequences - Context-sensitive help windows - Message lines for prompts and processing - Writes direct to multi-tasking video buffers (MTVB). - Full working shell for user development PULLC has been designed with a fill-in-the-blank concept. To get your application up and running, you only need to fill in the appropriate structures or variables. USING THE MANUALS Disk Based Guides - The manuals for PULLC are on disk so that you can conveniently scan for the topic you are seeking. You can do this with any list or search utility with a search function. You can also make a printed copy. If you have not already printed this manual, refer to the READ.ME file for instructions. At the present time, no bound manuals are being offered with registration. Chapter 1, Introduction Page 4 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 User's Guide - This manual, the one you are reading now, assumes that as a programmer you are already familiar with Turbo C 2.0, and that you have a working knowledge of your disk operating system (DOS). It also assumes that you are familiar with QWIKC Screen Utilities in QWIKC20D.ARC and WNDWC20A.ARC. This manual will provide the basic instructions for creating and managing multi-level pull-down menus. It also contains a tutorial to guide you step-by-step to create your own application. Reference Guide - This manual describes in detail all functions and variables used in PULLC. It is alphabetically arranged for easy access in a format similar to the TC2 manual. Use this manual when you have become familiar with the basic principles in the User's guide. LICENSING Registration - These routines and the documentation have been released for distribution as Shareware. You have been given the chance to sample the full capability of PULLC without risk! If you find that PULLC is a valuable tool, then you are expected to register. You will find a reasonable licensing schedule in LICENSE.ARC to meet private or commercial needs. Source Code - All registered users will receive source code when the signed license agreement is returned with the registration. All source code compiles under Turbo C 2.0. We do not support Turbo C 1.5. CUSTOMER SERVICE If you have questions, comments, or suggestions, the Eagle can be contacted by four means - (1) CompuServe, (2) telephone, (3) The Eagle BBS, or (4) mail. CompuServe - The most dependable way to contact the Eagle is through CompuServe. Jordan Gallagher has written the TC2 version of PULLC. He can be contacted on the Borland Forum by typing GO BPROGB from the CompuServe main menu. You will enter the Forum for Turbo C. You can contact Jordan with his PPN number of 73557,2342. Messages can also be left through EasyPlex. Telephone - Jordan can also be reached by phone at (214) 539-7855 on weekdays and Saturday from 9:00 a.m. to 8:00 p.m CST. The Eagle BBS - You can also contact us on our 24-hour BBS at (214) 539- 9878, 1200/2400 N81. Mail - For registration or problems, please write: Eagle Performance Software TP/TC products P.O. Box 292786 Lewisville, TX 75029-2786 Chapter 1, Introduction Page 5 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 In your written request for resolving problems, be sure to include: . A 5 1/4 inch diskette of compilable source code of the problem. . The Eagle product and version number. . The computer make and model. . The type of video card, video monitor and keyboard. ASP PULLC is a Shareware program conforming to the standards of the Association of Shareware Professionals (ASP). You can get more information about ASP by writing to: Association of Shareware Professionals P.O. Box 5786 Bellevue,WA 98006 This program is produced by a member of the Association of Shareware Professionals (ASP). ASP wants to make sure that the shareware principle works for you. If you are unable to resolve a shareware-related problem with an ASP member by contacting the member directly, ASP may be able to help. The ASP Ombudsman can help you resolve a dispute or problem with an ASP member, but does not provide technical support for member's products. Please write to: ASP Ombudsman P.O. Box 5786 Bellevue,WA 98006 or send a CompuServe message via EasyPlex to ASP Ombudsman 7007,3536. Chapter 1, Introduction Page 6 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 2. G E T T I N G S T A R T E D This section will acquaint you with the files on the distribution disk and show you a couple of demonstrations to quickly see what PULLC can accomplish. DISTRIBUTION FILES In this version, PULLC20.ARC contains: GOOF .C : Module to display errors. GOOFS .OBJ: Small model GOOF object code. LICENSE .ARC: ARC file containing license agreement and ordering details. MODNAME .OBJ: Contains a function used by PULLDIR to format the filenames. NOTE: The MODNAME.OBJ file that came with your ARC file was compiled for the Small model. If you change models, you must recompile it using TCC. Example: tcc -c -ml modname The "-ml" above signifies the Large model. In order to use the compiler and linker options you have set up in Turbo C, you must use TCCONFIG before compiling. See the Turbo C manuals for more details. PULLC20 .DOC: This document - a user's guide to PULLC. PULLC20 .H : This file lists all of the typedefs, macros, extern declarations, enums and prototypes for PULLC. PULLC20S.LIB: This small model library has the full power of all of its capabilities. Please note that making any changes in PULLC20.H will not affect the PULLC libraries. To recompile, complete source code is required. PULLDATA.C : Data to configure data entry windows and fields. PULLDEMO.C : Fully functional working demo. PULLDEMO.H : Header file with prototypes and other definitions for PULLDEMO. PULLDEMO.PRJ: Project file for PULLDEMO. PULLDIR .OBJ: Object code for a pull-down file directory. PULLREF .DOC: PULLC Reference Guide document covering each routine and variable in detail. PULLSHEL.ARC: Contains shell files to develop your own application. PULLSTAT.C : Stats to configure the menus including global keys. PULLWORK.C : Functions for the main work window(s). QWIKC20 .H : Header file for QWIKC. QWIKC20S.LIB: Library for quick screen writing. READ .ME : Note of printing instructions for manual. WNDPC20S.LIB: Multi-level virtual window library compiled for PULLC. WNDWC20 .H : Header file for WNDWC. Chapter 2, Getting Started Page 7 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 DEMONSTRATION To get the feeling of the speed and features of PULLC, let's run the demonstration program that comes with the utilities. Do the following steps: 1. Load the TC2 integrated environment. 2. Make sure the following compiler options are set as follows: Model size: Small Code generation/Default char type: Signed Code generation/Alignment: Word 3. Build the project file PULLDEMO.PRJ. If you get any errors, make sure QWIKC20S.LIB, WNDPC20S.LIB and PULLC20S.LIB are in Turbo C's .LIB path. 4. Follow along in the overview below. Familiar Environment - You will probably feel right at home with the environment created with PULLC as it appears very similar to the TC2 environment. It is interesting to note that PULLC was originally designed and developed with Turbo Pascal 3.0 before TP 4.0 or TC 1.0 were ever released. However, you should find the operation quite similar. While you are running the demo, let's get familiar with the format and operation of the environment and follow along with this overview: . Status Line - Row 1 just holds the title of this program. It is optional. . Top Line Menu - Row 2 has been optionally assigned to be the top line menu. Press F10 at any time to access it. . Work Window - For this demo, Rows 4 to 23 have a 20x78 window for the major part of your input/output. You can also have multi- level work windows. . Main Menu - To access a main menu, press RETURN or a command letter (LTR) while the top line menu is highlighted on any selection. Or, you can use a combination of the ALT key and a letter, such as Alt-F to get to the File menu. . Submenu - A submenu is a menu under a Main Menu. To access a submenu, press RETURN when the hilite is at a menu line with the three-bar symbol (which looks like a menu). You can see three levels of menus by pressing Alt-A/Tires/Brands. Local Keys and Letter Commands - While a window is shown, several keys operate for just that window. These keys can be listed in the message line or they can be assumed to be the command letters highlighted on each menu line. . Help Windows (F1) - While the Brands menu is still showing, press F1 to get context-sensitive help. A help window is assigned to every window and menu. Chapter 2, Getting Started Page 8 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 . Keys on Message Line - The bottom line of the CRT, being closest to the keyboard, indicates the available keys that can be used for the current context. It is also used for help or error messages. While the help window is displayed, the next key pressed will also pass through as a command. For instance, press RETURN now and see the help window removed and Firestone will also become flagged. . Pop and Pull (F2) - F2 is a pop-and-pull key that toggles between the pull-down menus and the work window. Press F2 twice and you will see that it remembers the last menu that was pulled. . Command Letters (LTR) - If you wanted to select WeatherGuard on the Brands menu, just type the letter "W" which is highlighted. These letters will work in any menu. . Cursor Keys - All of the cursor keys like the arrows, Home and End keys, have assigned functions as well as the control-shifted cursor keys. You can discover what they do by experimenting with them in a menu. . ESC Key - The ESC key always backs out of the current menu/window. Global Keys - Extended key combinations can be used to access any part of the program. In this demo, some have been assigned with the ALT key. Press down ALT for a half second and see the available global keys listed on the message line. . Directory (Alt-D) - Press Alt-D now to get a directory of your disk. If you would like to continue testing the directory, press F1 for instructions. . Exit (Alt-X) - To exit the program, one alternative is to use Alt-X, but don't do it now. . Top Line Menu (F10) - As mentioned before, to get to the top line menu, press F10 at any time. Data Entry Windows - Each menu can additionally pull down a data entry window which is indicated with a small dot symbol on the menu line. These windows are fully configurable and have full editing capability. They have a free-field entry concept where a entire string is typed and edited before entering. Let's try a few fields. . Data Entry Types - Press Alt-E to see a menu of all the data entry types available. Press RETURN when the hilite is at a menu line with a small dot symbol. Pressing RETURN again will exit the window entering the data shown. You can clear any data entered by pressing ESC which also removes the window. . Editing - Press "I" while the main menu is still shown and enter a value for the integer. The virgin entry is highlighted until you press a key. The entry has full edit capability using the cursor keys and some familiar WordStar control keys. Press F1 Chapter 2, Getting Started Page 9 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 for a list. The insert mode is indicated with a half-block cursor. . Options - All sorts of options are available for these windows including range checking, fixed and flex fields, character control and translation, automatic NumLock, justification, titles, and attributes. Work Window Data Entry - The same functions used for the data entry windows can be used for entering data in the work windows. In addition, PULLC has a smart algorithm that knows where several fields are on the screen. So moving from field to field with all the cursor keys is intuitive. . Moving the hilite - Press F2 to get back to the work window with all of the data entry fields. One field will be highlighted which means it can be moved to select another field. All of the cursor keys move the hilite including the control-shifted keys and the Tab and Shift-Tab keys. Move the hilite to the Int field. . New Values - To enter a new value for the integer, simply start typing some numbers and the hilite will change colors. You can see that only numbers and the sign can be entered. Press RETURN to enter the new value or press ESC to restore the old value. . Editing - To edit the value currently shown in a field, press RETURN or any WordStar control key. Other Features - There are many other features in the menus which will be covered later including menu and line modes, and direct menu control. You may continue to discover other features in the demo if you want. When finished, press Alt-X to quit. Multi-tasking - This demo is already set to work in multi-tasking environments compatible to DESQview, TaskView, and IBM 3270 PC. Chapter 2, Getting Started Page 10 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 3. P R O G R A M M I N G M E N U S This section will get you familiar with the basics of menu programming by starting with very basic menus and then taking you step-by-step through the full variety of options and modes available. USING THE SHELL Shell Program - Let's experiment will PULLC by developing our own application to see how powerful it can be. A shell program, which is a bare bones application, has been provided with PULLC in the file PULLSHEL.ARC. These files will help you get started for any application. But for now, it can be an excellent learning tool. To keep these files separate from the PULLDEMO files, create another working directory and unarchive the files in PULLSHEL.ARC. The files to be extracted are: PULLSHEL.C PULLDATA.C PULLSTAT.C PULLWORK.C PULLSHEL.H PULLSHEL.PRJ In addition, the following files need to be in your library path or copied into the same directory: QWIKC20S.LIB WNDPC20S.LIB PULLC20S.LIB GOOFS.OBJ Running the Shell - To make sure we have everything available, compile/link PULLSHEL.PRJ and run the program. You should be able to operate this program the same as PULLDEMO.C. There are only two menus - First and Quit. To Quit, you can either use the Quit menu or use the global key Alt-X. Features Available - The shell has every feature available. They can be added or eliminated. However, some code cannot be entirely eliminated or altered unless you are registered with the source code. But let's continue to see what the program can really do. Zero-based Lines - All values for menu line numbers you will see in this manual are zero-based. Therefore, if we mention "line 2" you must look at the third line rather than the second. This was done to improve code efficiency by not having to subtract 1 for accessing arrays. MENU MODES In this section, we will delve right into the menus and see how they function and what changes can be made. PULLC has three menu modes for every menu - EXEC_CHOICE, SINGLE_CHOICE, and MULTIPLE_CHOICE. These modes determine how all the lines in the menu can interact as a whole. Chapter 3, Programming Menus Page 11 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 SINGLE_CHOICE - You probably noticed that the First menu had one flag on it. This is because it was a SINGLE_CHOICE menu. One and only one item could be flagged by pressing return on any one line. Let's examine the data structure of the menu and see how it was made to be single choice. 1. Load the file PULLSTAT.C into the TC environment. 2. Set the project file to PULLSHEL.PRJ. 3. Search for "getmainmenu(FIRST_MENU)". 4. See the following code: getmainmenu(FIRST_MENU); topmenu.menumode = SINGLE_CHOICE; topmenu.singleflagline = 2; strcpy( topmenu.title, "First" ); strcpy( topmenu.line[0], "A line" ); strcpy( topmenu.line[1], "B line" ); strcpy( topmenu.line[2], "C line" ); strcpy( topmenu.line[3], "D line" ); topmenu.defaultline = 1; topmenu.msglinenum = MAIN_ML; topmenu.helpwndwnum = MAINMENU_HW; savemainmenu(); topmenu - All these variables are fields in the current menu structure called topmenu. Notice that menumode is set to SINGLE_CHOICE. And that's how it is done! That's all it takes to tell PULLC that all items on the menu are single choice. The program simply takes care of any selection. So how do you know which line has been selected? Just get the value of topmenu.singleflagline. But also notice that singleflagline has been initially assigned to line 2 (0-based). Now you know why the "C line" is flagged when the program first starts. Setting singleflagline is only needed for the SINGLE_CHOICE mode. So how do you make it multiple choice? MULTIPLE_CHOICE - Let's change menumode to MULTIPLE_CHOICE and run the program again. Do it now. This time you won't see any flags initially, but each line can be toggled on and off by selecting any line with RETURN. Ok, how do we know which line has been flagged now? There are several more variables in the menu structure other than those shown here. Each line has an associated flag called flagged. For example, if you want to see if line[2] has been flagged, just test and see if flagged[2] is 1. But suppose we want some of those lines to be initially flagged? Flags - All the code we have seen is within a function called getuserpullstats which initializes all the menus from initpull. The function getmainmenu simply grabs a copy of the current menu structure from the heap. It was allocated by PULLC using calloc(), so all values are cleared at this time. That means everything is a default value of zero unless we change it. So, the value of flagged for each line is 0. Let's see if we can set a couple of lines to be initially true by modifying the code to: ... topmenu.menumode = MULTIPLE_CHOICE; topmenu.singleflagline = 2; strcpy( topmenu.title, "First" ); Chapter 3, Programming Menus Page 12 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 strcpy( topmenu.line[0], "A line" ); topmenu.flagged[0]=1; strcpy( topmenu.line[1], "B line" ); topmenu.flagged[1]=1; ... Run the code again and verify that the first two lines are initially flagged. If they are, then you are already getting the hang of how to program pull-down menus! PULLC is based on a fill-in-the-blank concept where you supply the data and the program takes care of the rest. Changed Flags - If there are several flags in the menu and just one is changed, how can you know? Anytime a flag is altered in the menu, the variable "changed" in the menu structure is set to 1. This gives a quick survey for any action that may be needed. This value remains set to 1 until your application program manually sets it to 0. Executing Functions - But having a simple flag may not be enough for your application. Suppose you may also want to DO something as well. So how can we execute a function along with the selection? Again, each line has an associated execution pointer called funcptr. Simply assign any valid function address to it and it executes it! Try the following code: ... topmenu.menumode = MULTIPLE_CHOICE; topmenu.singleflagline = 2; strcpy( topmenu.title, "First" ); strcpy( topmenu.line[0], "A line" ); topmenu.flagged[0]=1; topmenu.funcptr[0]=dummyfunc; strcpy( topmenu.line[1], "B line" ); topmenu.flagged[1]=1; topmenu.funcptr[1]=dummyfunc; ... Run this code and you will find that every time line 0 or 1 is toggled, the message "Processing ..." is displayed briefly on the message line. This is all the dummy function was supposed to do. After it is processed, the line is flagged. The function dummyfunc is actually back a few lines in the code just before getuserpullstats. Having these functions in the same file makes it more convenient, but they don't have to be. These functions can be called from other modules as well, as long as those modules are linked with the program. NULL Pointer - So how come functions were not executed before the pointers were assigned? Again, all values are zero until changed. So, the pointer was set to NULL. The program simply ignores NULL pointers and therefore does not try to call them. EXEC_CHOICE - This mode only executes the functions with funcptr and just ignores all flagging. This is also the default mode. Let's try this by disabling the following code: ... #if 0 topmenu.menumode = MULTIPLE_CHOICE; topmenu.singleflagline = 2; #endif strcpy(topmenu.title,"First"); Chapter 3, Programming Menus Page 13 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 strcpy(topmenu.line[0],"A line"); topmenu.flagged[0]=1; topmenu.funcptr[0]=dummyfunc; strcpy(topmenu.line[1],"B line"); topmenu.flagged[1]=1; topmenu.funcptr[1]=dummyfunc; ... Run it again. You found that both line 1 and 2 executed the dummy process, but the flags weren't toggled. EXEC_CHOICE just ignores flagging. Flags are usually not useful with EXEC_CHOICE mode and they can remain false. Lines 3 and 4 did absolutely nothing since the pointers were NULL. Default Mode - How come menumode was not assigned to EXEC_CHOICE and instead the code was disabled? We could have if we wanted, but EXEC_CHOICE is the default mode with a value of zero. So, it saves code to leave it out. LINE MODES In this section, we will discover how each line in the menu can have one of several different modes and then test each one to see its effect. Seven Line Modes - Not only does the whole menu have a mode, but each line can have one of eight different modes: CHOICE - permits flagging with SINGLE- or MULTIPLE_CHOICE and executes the funcptr. EXEC_ONLY - executes the funcptr without flagging. NO_CHOICE - disabled by the your program. COMMENT - bypassed by the highlight. PARTITION - mid-menu horizontal border. TO_DATA_WNDW - to pull a data entry window. TO_SUB_MENU - to pull next submenu level. TO_USER_WNDW - like EXEC_ONLY, and adds a submenu symbol. These names are the actual identifiers used in linemodes. Each line has an associated line mode saved in the variable linemode. For example, to see what mode is on line 2, just check the value of linemode[2]. CHOICE - This is the default and, as you would guess, its default value is zero, so we never have to set it. With this line mode and a menu mode of SINGLE_CHOICE or MULTIPLE_CHOICE, the line can be flagged like we have seen in the previous examples. But it is rare that any menu would have only flags. So, what other alternatives are there? EXEC_ONLY - Let's suppose that just one of the lines on our First menu should never be flagged, but all the others can. How can we isolate it? Back to the current example, revise the menu to be MULTIPLE_CHOICE with the following changes: ... topmenu.menumode = MULTIPLE_CHOICE; #if 0 topmenu.singleflagline = 2; #endif Chapter 3, Programming Menus Page 14 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 strcpy(topmenu.title,"First"); strcpy(topmenu.line[0],"A line"); topmenu.flagged[0]=1; topmenu.funcptr[0]=dummyfunc; strcpy(topmenu.line[1],"B line"); topmenu.linemode[1]=EXEC_ONLY; topmenu.funcptr[1]=dummyfunc; ... Run it again and see that we can't toggle the flag on line 1 since it was assigned as EXEC_ONLY. All it does is execute dummyfunc even though the menu mode is MULTIPLE_CHOICE. COMMENT - Suppose we wanted a title or some type of comment or help message inside the menu itself. That line should not be considered as a valid choice. In fact, the line should never be highlighted. Try changing line 2 to the following: ... strcpy(topmenu.line[1],"B line"); topmenu.linemode[1]=EXEC_ONLY; topmenu.funcptr[1]=dummyfunc; strcpy(topmenu.line[2],"My comment"); topmenu.linemode[2]=COMMENT; ... Run it and move the hilite up and down. You can see that the hilite passes right over line 2 and never accesses it. By the way, notice also that the first letter of "My comment" was not highlighted as a command letter. In fact, the entire line is a different attribute. Also notice that the menu has automatically increased in width to accommodate the new longest line. PARTITION - Sometimes it is easier to understand a menu when it is divided into separate sections. This can be done with the line mode of PARTITION. Change linemode[2] as follows: ... strcpy(topmenu.line[1],"B line"); topmenu.linemode[1]=EXEC_ONLY; topmenu.funcptr[1]=dummyfunc; /*strcpy(topmenu.line[2],"My comment");*/ topmenu.linemode[2]=PARTITION; ... When you pull down the menu, you will see that line 2 has become an extension of the border with the same style and attribute. Moving the highlight up and down will also show that the partition is passed over just like a COMMENT. Since assigning a string for line[2] was unnecessary, it was commented out. NO_CHOICE - Suppose you want a line to be temporarily unavailable because it didn't apply at a given stage in your program. You can overwrite the menu structure with a line mode of NO_CHOICE. This has the same effect as COMMENT, except you can enable it again by changing the line mode back to what it was. The command letter will again become available for that line. In contrast, PARTITION and COMMENT never have a command letter. Other Line Modes - There are three remaining modes, TO_DATA_WNDW, TO_SUB_MENU, and TO_USER_WNDW, which will pull down another level of a menu or a window. These can also be assigned to linemode, and will be covered in more detail later. Chapter 3, Programming Menus Page 15 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 HILITE CONTROL Highlight Line - The menu highlight bar is tracked by a variable called hiliteline, which is also in topmenu. Any time the highlight (also called hilite) is moved, this value is updated to the current line number and saved. So, upon reentering the menu, the hilite will still be on the same line before as before. Default Line - The hilite has to start somewhere. In our current example, the variable defaultline controls the initial line for the hilite. When you run it, you can see that the first time the menu is pulled, line 1 is highlighted. Now move the hilite down to line 3. If you exit and reenter the menu, the hilite is still on line 3. So, we know that it always remembers the current line. But what is the default for defaultline? Let's find out. Comment out the following line: ... strcpy( topmenu.line[3], "D line" ); /*topmenu.defaultline = 1;*/ topmenu.msglinenum = MAIN_ML; ... Run the program and you will find that the hilite starts on line 0. Again, the memory is allocated with calloc() and thus the values are all initialized to zero. But suppose we want the hilite to start on the same line every time the menu is pulled? Back To Default Line - You can force the hilite to the same initial line by setting backtodefault to 1. Let's try it on our current example: ... strcpy( topmenu.line[3], "D line" ); topmenu.defaultline = 1; topmenu.backtodefault = 1; topmenu.msglinenum = MAIN_ML; ... When you pull-down the menu this time, the default line is 1. But move the hilite to line 3 and exit the menu. When the menu is pulled again, the hilite goes back to line 1. Looking at the code, the Quit menu structure is a few lines down from the First menu. There you can see that backtodefault is set to 1 for that menu as well. This would keep a user from inadvertently exiting the program. ADDING LINES Adding Lines - To add more lines to a menu is a snap - just add a line. Try the following modification on the First menu. ... strcpy( topmenu.line[3], "D line" ); strcpy( topmenu.line[4], "E line" ); /* add this line */ topmenu.defaultline = 1; Chapter 3, Programming Menus Page 16 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 ... The program automatically knows how many lines are in the menu so that it is sized correctly and the hilite knows how far to extend. How many lines can be added? Maximum Lines - To control the size of the menu structures, the maximum number of lines per menu is set by the macro MAX_MENU_LINES. Go to the file PULLC20.H, and you will find it near the top of the file. It has been arbitrarily set to 15 lines. This is one of several configuration macros preset in PULLC. If you change these macros, you must have the complete source code to PULLC, and recompile it with the new values. Maximum Characters - Each menu line is also limited to a maximum length. You can discover this by testing this code: ... strcpy( topmenu.line[3], "D line" ); strcpy( topmenu.line[4], "E line is longer than 20 characters" ); topmenu.defaultline = 1; ... The next line is overwritten, since each line is only allotted 20 characters. This value is also preset with the macro MAX_CHARS_PER_LINE. ADDING MENUS Now that you are familiar with the scope of a single menu, you can take the next step and learn how to include additional menus. Main Menu Name - The first thing needed is a name for a new main menu. Near the top of PULLSHEL.H, find an enum declaration called mainmenunames and insert a new name called SECOND_MENU: enum mainmenunames { FIRST_MENU, SECOND_MENU, QUIT_MENU, NO_MAIN_MENU=255 }; It is important that these names are in order so that initpull will arrange them correctly. Main Menu Structure - Now the structure for SECOND_MENU can be added. Between the FIRST_MENU and QUIT_MENU structures add the following code: getmainmenu( SECOND_MENU ); topmenu.menumode = MULTIPLE_CHOICE; strcpy( topmenu.title, "Second" ); strcpy( topmenu.line[0], "A2 line" ); strcpy( topmenu.line[1], "B2 line" ); strcpy( topmenu.line[2], "C2 line" ); strcpy( topmenu.line[3], "D2 line" ); topmenu.msglinenum = MAIN_ML; Chapter 3, Programming Menus Page 17 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 topmenu.helpwndwnum = MAINMENU_HW; savemainmenu(); Now run the program. You can see that there are now three menus that can be pulled down. They are arranged in the order of the mainmenunames. Just for fun, let's reverse the names in mainmenunames and see what happens: enum mainmenunames { SECOND_MENU, FIRST_MENU, QUIT_MENU, NO_MAIN_MENU=255 }; When you test this, you will find the two menus in different positions. This makes rearranging a snap. We didn't even have to change anything about the mainmenu structures themselves. Title - For a main menu, the title is required. By default, the first letter is used for command letter after pressing F10. Press F10 now and then press "S". This will pull down the SECOND_MENU. But what about the global key Alt-S? Press F2 to get back to the work window and try Alt-S. Nothing happens. Global Key - To assign an extended key combination to this menu, go to the bottom of the file PULLSTAT.C to the function called checkglobalkeys. In this code, an assignment can be made for this menu. Insert the following line into the code: ... case ALTF: setcmdseq ("F"); break; case ALTS: setcmdseq ("S"); break; case ALTQ: setcmdseq ("Q"); break; ... This is part of a switch statement, so the order is not important. In addition, the macro ALTS must be defined. At the top of PULLSHEL.H, add: ... #define ALTF 33 /* First menu */ #define ALTS 31 /* Second menu */ /* <--- insert this line */ #define ALTQ 16 /* Quit menu */ #define ALTX 45 /* Immediately exit program */ ... Now run the code and try Alt-S again to find that it now works. But what did we just do? Any time an extended key is pressed at the keyboard, the program always passes through the function checkglobalkeys. If the extended keycode matches one in the switch statement, the program sets a sequence of normal key codes that would be pressed just as if pressing F10 and then "S". The keycode values can be found in many reference guides. ADDING SUBMENUS Every menu can pull down another submenu. This section will show how to include one. The method is much the same as with main menus. Chapter 3, Programming Menus Page 18 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Submenu Name - Near the beginning of PULLSHEL.H is another enum declaration called submenunames which looks like this: ... enum submenunames { MY_SUB_MENU, NO_SUB_MENU=255 }; ... Let's go ahead and use the name MY_SUB_MENU. Submenu Structure - In PULLSTAT.C, just after the main menu structure for QUIT_MENU, you will find an area for submenus. There is already a structure made for MY_SUB_MENU and it looks like the following: getsubmenu( MY_SUB_MENU ); topmenu.menumode=SINGLE_CHOICE; topmenu.singleflagline=3; strcpy( topmenu.line[0], "1 line" ); strcpy( topmenu.line[1], "2 line" ); strcpy( topmenu.line[2], "3 line" ); strcpy( topmenu.line[3], "4 line" ); topmenu.msglinenum = SUB_ML; topmenu.helpwndwnum = SUBMENU_HW; savesubmenu(); It is exactly like the main menu structure, except to get and save it, getsubmenu and savesubmenu are used. A title is not required for a submenu, as initpull automatically gives it the name of the parent menu. Now let's link the submenu to a main menu. Linking Menus - Choose SECOND_MENU line 1 for the line where MY_SUB_MENU is to be linked. In the SECOND_MENU (not the submenu) structure, modify line 1 to the following code: ... strcpy( topmenu.title, "Second" ); strcpy( topmenu.line[0], "A2 line" ); strcpy( topmenu.line[1], "B2 line" ); topmenu.linemode[1]=TO_SUB_MENU; topmenu.linknum[1]=MY_SUB_MENU; ... Run the code and see that the submenu is pulled down when you press RETURN on line 1 of SECOND_MENU. This is probably simpler than you thought! You can also see a three-bar symbol on this line which indicates the line is linked to a submenu. By setting the line mode to TO_SUB_MENU, the program is prepared to pull-down another menu. linknum identifies the submenu structure to be pulled which is MY_SUB_MENU. Linking Configuration - The submenus link in a slide-up rather than a slide-under configuration. Menus grow vertically as the list expands. If the list gets too long, it can slide up when it hits the bottom of the screen. This leaves both the main menu and submenu in full view. In Chapter 3, Programming Menus Page 19 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 contrast, the data windows, which are covered later, use the slide-under configuration. Default Linking Direction - Most submenus are linked to the main menu in a right-preferred arrangement. When menus get crowded to the right, initpull will automatically reverse to the left and all dot and 3-bar symbols will also appear on the left. Each subsequent submenu will continue to link in the same direction as its parent as far as it can. Manual Linking Direction - As long as linkdir is not specified like in our example, initpull will configure it for you. However, this may not be preferred in all cases. To specify it manually, set the value of linkdir in that menu structure to LEFT or RIGHT. This will force all submenus to be linked in that direction. Global Key - Suppose this is a frequently used submenu and we want to assign an extended key, say Alt-M, to access it. To do this, it is much the same as with a main menu, except there is an additional keystroke. Add the following line to checkglobalkeys in PULLSTAT.C: case ALTM: setcmdseq ("SB"); break; and define a value for the macro ALTM in PULLSHEL.H: #define ALTM 50 /* the Alt-M extended key code */ When you run the program, pressing Alt-M will pull down both SECOND_MENU and MY_SUB_MENU just as if you had sequentially pressed F10, "S" and "B". setcmdseq - PULLC saves the sequence of keystrokes for the current menu level in a string called cmdseq. setcmdseq compares the current location of cmdseq with the new destination and sets two variables for the shortest path - morecmdseq and poplevels. This will be covered later in detail under the Control Flags section. subsubmenus - subsubmenus can be added in just the same way as submenus. In fact you could nest them as deep as you want. initpull expects the structure names in submenunames to be in a certain order so it can properly locate each one on the CRT. Just be sure that the order is all submenus first, all subsubmenus second, all subsubsubmenus third, etc. HELP MESSAGES Each menu has a help message that is displayed on the last line of the CRT where the application can indicate valid keys and status or error messages. In this section, you will discover how these messages can be linked to any menu while it is displayed. Reserved Messages - PULLC has already included some predefined messages for several contexts including those for main menus and submenus. In PULLSTAT.C, just below the last submenu structure, you can find assignments to an array called msgline. The ones reserved for main menus and submenus are MAIN_ML and SUB_ML, respectively: Chapter 3, Programming Menus Page 20 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 strcpy(msgline[MAIN_ML]," F1-help F2-pop LTR-cmd ESC-return " " \x1B\x1A menus \x18\x19-hilight " "CR-select"); strcpy(msgline[SUB_ML], " F1-help F2-pop LTR-cmd ESC-return " " \x18\x19-hilight CR-select" ); The concatenation is just so that it will fit within an 80 column width in the source code. Back in PULLSHEL.H, you can find the enum declaration msglinenames that helps identify each message. All the messages up to HELP_ML are reserved as PULLC expects them to be in that order. But new ones can be added. New Messages - To create a new message, just append a name before NO_ML in the msglinenames enum and write out your new message. Let's try it on the Quit menu by changing msglinenum in its menu structure as follows: ... strcpy( topmenu.line[1], "Quit" ); topmenu.funcptr[1] = setquit; topmenu.backtodefault = 1; topmenu.msglinenum = QUIT_ML; /* modify this line */ ... Then append the name QUIT_ML before NO_ML at the end of msglinenames: enum msglinenames { WORK_ML, TOP_ML, ALT_ML, MAIN_ML, SUB_ML, DW_ML, DE_ML, SEQ_ML, HELP_ML, FUNC_ML, QUIT_ML, NO_ML=0xFF }; And finally, let's create the message itself: strcpy( msgline[QUIT_ML], " Press \"Q\" if you are sure you want " "to quit." ); Short messages are no problem, because PULLC will clear the rest of the message line. Run the program now and see that the new message appears when the Quit menu is pulled. By now, you are seeing that pull-down menus can link other menus and messages quite easily. Alt Key Message - You may have noticed when you hold down the Alt for more than a half second, a message appears for the possible combinations. When we created a submenu with global key access, it is not likely a first-time user would know the key was available. The ALT_ML message is reserved for this purpose. Let's alter the line to: strcpy( msgline[ALT_ML], " Alt: F-First M-MY_SUB_MENU " " X-Exit" ); Run the program and hold down the Alt key to test the new message. HELP WINDOWS Chapter 3, Programming Menus Page 21 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Many times the help message is not enough to fully explain the options available for the context. In this section, you will discover how to create context-sensitive help windows and apply them to the menus. Help Window Structure - Just as each menu has its own structure, each help window also has one called helpwndw. There are only a couple of variables that need adjustment, to link in the number of lines to be included in the window. Let's try creating a help window for a main menu. Example Window - Run the current example program again and test the F1 key while the SECOND_MENU is pulled down. You should see a two-line help window with the message "Main menu help message". So, some help window is already there, but the message is just bare bones. Let's find out how the message got there and alter it. Help Lines - In PULLSTAT.C after the msgline messages are assigned, there is another section for setting helplines. You should be able to see the code: strcpy(helpline[HL_M1],"Main menu help message"); strcpy(helpline[HL_ML],""); These are the actual messages you saw in the help window. Each window can have a variable number of lines in the help window. The program will automatically size the window to fit in all the lines. Let's edit and add an extra line: strcpy(helpline[HL_M1],"Main menu help message"); strcpy(helpline[HL_M2],"Press RETURN on the highlighted line to make"); strcpy(helpline[HL_ML],"your selection."); Since a new helpline name, HL_M2, has been added, it must be inserted in the enum declaration helplinenames in PULLSHEL.H: ... HL_T1, HL_TL, /* top menu */ HL_M1, HL_M2, HL_ML, /* main menu */ /* modify this line. */ HL_S1, HL_SL, /* sub menu */ ... Now run the program and test the modified help window. This time you should see the three lines. With just a couple of changes, all help windows are still in order along with the contents because it is so easy to work with names instead of numbers. How did the program know how many lines to display even though changes were made? Number of Help Lines - In the section just below the helplines, see the following code: ... helpmsglinenum = HELP_ML; /* standard message for a help window */ sethelplines( WORKWNDW_HW, HL_W1, HL_WL ); sethelplines( TOPLINE_HW, HL_T1, HL_TL ); sethelplines( MAINMENU_HW, HL_M1, HL_ML ); Chapter 3, Programming Menus Page 22 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 ... The call to sethelplines is a trivial function listed earlier in the code that simply sets the first and last line number into the help window structure by using helplinenames. Notice that the first and last line names end with 1 and L respectively. This makes it handy so these statements never need to be altered when new lines are added or inserted. Help Window Names - The window name is MAINMENU_HW which is listed in the enumerated type helpwndwnames near the top of PULLSHEL.H: enum helpwndwnames { WORKWNDW_HW, TOPLINE_HW, MAINMENU_HW, SUBMENU_HW, DATAWNDW_HW, NO_HW=255 }; Help Window Structure - A help window structure is assigned to each one of these names. In the SECOND_MENU structure, we can find the help window structure name by examining the following: ... topmenu.msglinenum = MAIN_ML; topmenu.helpwndwnum = MAINMENU_HW; savemainmenu(); ... So now we can finally see how this help window was assigned to the main menu. Rather than having a standardized help window for each main menu as a whole, you can even create new ones just as new message lines were created: 1. Insert the name into the helpwndwnames enumeration. 2. Insert the help lines into the helplinenames enumeration. 3. Make helpline assignments. 4. Use sethelplines to identify the first and last help lines. 5. Assign the helpwndwnum to the menu structure. Help Message - Even the help window needs a help message. One has already been standardized for you called HELP_ML. initpull will initialize all help windows to be assigned the message number in helpmsglinenum. Window Width - All help windows have a standard width. It is set by a configuration macro in PULLC20.H called HELP_CHARS_PER_LINE, which is currently set at 50. The program adds 2 to this value to leave a space between the left and right borders. The value can only be changed if you have the source code, since PULLC needs to be recompiled after changing it. No Help Window - If there are selected structures where a help window is not wanted, just set: topmenu.helpwndwnum = NO_HW; Integrated Help Windows - This system will integrate the help windows into the executable file. With enumerated names, up to 32767 lines can be Chapter 3, Programming Menus Page 23 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 included. If you need more, it is suggested that you develop a disk-based help system, which is currently beyond the scope of the this utility. DEFAULT ATTRIBUTES This section will show the variables used to create default attributes for menus, windows and messages. initpull - Near the top of the function getuserpullstats, several default attribute variables can be given assignments. For instance, mainmenuwattr will be used by initpull to give all main menus the same window attribute for wattr in the menu structure. This saves you from having to make the same assignment to every menu structure. Attributes - As a personal preference, many users prefer the screen to have different attributes when possible. The following is the list of default attributes and what they affect: Struct Default variable Variable Description ---------------- --------- ------------------------------------------ toplinemenuattr n/a Full length of the top line menu. toplinemenuhattr n/a Top line menu hilite. toplinemenulattr n/a Top line menu command letter. mainmenuwattr wattr Main menu window. mainmenubattr battr Main menu border. mainmenuhattr hattr Main menu hilite. mainmenulattr lattr Main menu command letter. mainmenucattr cattr Main menu comment line. submenuwattr wattr Sub menu window. submenubattr battr Sub menu border. submenuhattr hattr Sub menu hilite. submenulattr lattr Sub menu command letter. submenucattr cattr Sub menu comment line. helpwndwwattr wattr Help window window. helpwndwbattr battr Help window border. msglineattr n/a Message line full length. errmsgattr n/a Error messages. keystatusattr n/a Key status of NumLock, Caps, and ScrollLock. Find the assignment to the variable mainmenubattr and modify it to inverse video: mainmenubattr = LIGHTGRAY_BG; When you run the program, you will find that every main menu has this new attribute. All the other variables work similarly. Mono vs. Color - You probably noticed an "if" statement in the code testing Chapter 3, Programming Menus Page 24 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 the current video mode. The results of this test allows you to configure the menus for either Monochrome or color monitors. Refer to your technical reference manual for the effects of one attribute on either monitor. DEFAULT BORDERS This section will show the variables used to create default border styles for the menus and windows just like the attributes mentioned above. Border Styles - The following is a list of default border style variables and what they affect: Struct Default variable Variable Description ---------------- --------- ----------------- mainmenubrdr border All main menus. submenubrdr border All submenus. helpwndwbrdr border All help windows. The shell program has assigned mainmenubrdr to a custom border called USER_BORDER_1. Let's modify this to SINGLE_BORDER: mainmenubrdr = SINGLE_BORDER; In addition, you can optionally comment out the assignment of USER_BORDER_1 in the previous line since it is no longer needed. When the program is run, all main menus will now have a single-line border style including the partitions. CONTROL FLAGS The pull-down menus can be programmably controlled in your program by toggling various pull-down control flags. This helps direct the users to the needed menus automatically instead of manually pressing a key sequence. In this section, you will discover these flags and what they control. Save the shell program now for later use and get back to PULLDEMO.C and its files (such as PULLSTAT.C) for this section. Control Variables and Functions - Here is the list of variables and functions that control the menus: poptoworkwndw - (Type: char) If set to 1, all menus are removed and control is returned to the work window. poptotop - (Type: char) If set to 1, control is set to the top line menu. poplevels - (Type: unsigned char) Number of levels (or menus) to pop. popped - (function) Pops all menus before execution of execptr. morecmdseq - (Type: seqstr_t (string)) series of command letters to do after popping. pulldown - (Type: char) If set to 1, the menus will be pulled down according to morecmdseq. Chapter 3, Programming Menus Page 25 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Examples - Run PULLDEMO and access Alt-I/Update. In the Update menu, there are six examples of how the menus can be controlled in combination with executing a funcptr. Execute each of those six lines to see what they do. In PULLSTAT.C, search for "getsubmenu( UPDATE_MENU )" and see that each line is executing funcptr in the menu, so let's find out what those functions are doing. Process Then Pop - Back up until you find the processthenpop function. Very simply, the program first executed dummyfunc and then set poptoworkwndw to 1. When the flow of execution passes back through the key dispatcher, PULLC will immediately pop all menus and return to the top work window. Pop Then Process - But suppose we want to do the reverse. Anytime funcptr is used for execution, a copy of it is kept in the global variable execptr. By using popped, the function sets the appropriate flags to pop the menus. The first time popped is tested, it is 0. So, dummyfunc is not executed. Once the menus are popped back to the work window, PULLC executes funcptr again via execptr. This time popped will be 1 and dummyfunc will be executed. Pop, Process, and Pull - Let's go one step further and pull the same menus back down that were popped. Looking few lines down further in the popprocessandpull function, the new line that was added is setting pulldown to true. But how does the program know what menus to pull? PULLC keeps a copy of the last command sequence in the string morecmdseq. When pulldown is 1, the menus are pulled down by morecmdseq. This is useful when there is something under the menus that needs to be changed like accessing another work window. Popping Levels - Or, if you just want to back up a number of menu levels, just specify the number of levels to pop with poplevels. In the function popnumoflevels, rather than making the menu completely disappear only to needlessly pull them back down again, this function pops back as far as it needs to go and then additionally pulls down another submenu (or data window). This sequence is useful when you know exactly where you are and where you are going. Popping to New Menus - If you need to go to a new menu, you can use the flag poptotop which will pop the menus up to the top line. By setting pulldown to 1 and morecmdseq to the desired menu, you can get to the new destination like the function poptonewmenu. Smart Pop and Pull - But suppose the menus will execute the same funcptr from three different locations in the menus, and still want it to appear smart like popnumoflevels does? PULLC has a function called setcmdseq that compares the current location, cmdseq, with your destination. It sets morecmdseq and poplevels to the shortest path. In fact, popnumoflevels could have looked like: void popnumoflevels(void) { pulldown = 1; setcmdseq( "IY" ); } Chapter 3, Programming Menus Page 26 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 The parameter for setcmdseq is the full sequence of command letters to be pressed from the top line. Sequential Data Windows - One application of these control flags may be sequential entry for data windows. Access Alt-I/Date and press RETURN a few times to see how the menus are cycled in a loop for the date. To see how it was done, follow the funcptrs in the DATE_MENU and see what flags were set. SUMMARY At this point in the tutorial, you've been able to master the configuration of the top line, main, and sub menus with all the menu modes and most of the line modes. In addition, you found how to add menus and submenus and assign help messages and help windows. You changed the appearance of the menus with attributes and borders. You could even programmably control the sequence of menus that were pulled down. These were all accomplished by just filling out the appropriate variables. Chapter 3, Programming Menus Page 27 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 4. S C R E E N D E S I G N You are now at a point where you understand most of the fundamental features in PULLC. In this section, you will discover how to control the arrangement of the major components of full screen design. You will be able to relocate the status line, top line menu, main menus, work window(s), and message line. Arrangement of the components will depend on the human factors needed for your application. STATUS LINE The Status line is a row that you can reserve for pertinent information. In the shell program, the status line is assumed to be row 1 where a program name, title, and copyright were placed. In the TC2 environment, row 2 is the status line for line, column, insert status, etc. Actually, PULLC does nothing to program this line, but PULLC was designed to work around it. Use the files for PULLSHEL in the following sections rather than those for PULLDEMO. TOP LINE MENU Showing the Top Line - The top line menu is automatically created by assembling the titles from each main menu. So, the string is already created for you in the string variable toplinestr. To write this string to the screen, simply call showtopline. But where is it placed? Top Line Placement - In PULLSTAT.C, find the function getuserpullstats again and search for the variable toplinerow. It is found in a section called Top Line Defaults. This is the variable that determines where the top line is placed: toplinerow = 2; /* top line menu to appear on row 2 */ Let's try reversing the Status line and the Top line menu in the shell program. Set toplinerow to 1 and then look in PULLSHEL.C and modify the row of the Status line to 2: wwrite( 2, 1, "PULLSHELL v2.0 Multi-level Pu" "ll-down Menus Copr 1989 J H LeMay" ); Now run the program and see both lines reversed, but when the main menus are pulled, they are still on row 3. How can they be moved? MAIN MENU ROW Main Menu Row - The row on which the main menus appear is controlled by the value of mainmenurow. The main menus do not have to be attached to top line menu and can appear on any row provided they do not interfere with the message line. Moving the Row - Let's shift the main menus back up so they will appear to be attached to the top line menu. In PULLSTAT.C, search for: Chapter 4, Screen Design Page 28 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 mainmenurow = 3; /* First row of main menus to appear on row 3 */ and change it to 2. Run the program again to see that the main menus pull- down now on row 2. But also check the submenu by pressing Alt-M. Even the submenu is correctly placed! initpull accommodates the changes. SUBMENU ROW Submenu Row - Try to arrange the lines in your menus that link submenus so that they are as high in the menu as practical. Otherwise, long menus will bottom out on the CRT and PULLC will be sliding up the menus to keep them in view. PULLC handles this well by using the slide-up configuration, but the menus will not appear to have much foresight in your design. The Alt-A/Tires/Brands menu in PULLDEMO is a good example of keeping menus high. WORK WINDOWS Window Modes - The major portion of the screen is intended to be the work window for your application. This area can have one or more windows and they can be in any window mode that you chose - even virtual. Usually one of the modes can be PERMMODE since there is no data to save under the screen, but with a TSR, you may need to save the screen as well. Window Size - The example in the shell program uses a 22x80 PERMMODE window with border. Let's get rid of the status line, make the window larger, and at the same time, take off the double border. In PULLSHEL.C, comment out the status line and modify the makewindow statement to the following: /*wwrite( 1, 1, "PULLSHELL v2.0 Multi-level Pu" "ll-down Menus Copr 1989 J H LeMay" );*/ showtopline(); setwindowmodes(PERMMODE); makewindow( 2, 1, crt_rows-2, crt_cols, WHITE+BLUE_BG, WHITE+BLUE_BG, NO_BORDER, WINDOW1 ); Running the program, you will see the work window took up all but the top line menu in row 1 and the message line on the last row. Notice also that the contents of the window also moved because they have window-relative coordinates. This shows you the flexibility of your screen design. MESSAGE LINE Message Line - This is the line on which all messages will appear and is usually in reference to crt_rows to accommodate different video modes. If the message line is intended to list key commands, for human factor reasons, it is best to keep it near the bottom row. Location - The location of the line is set by the variable msglinerow in getuserpullstats. To whatever line it is assigned, be sure that there is no possibility that it may be covered by other windows. You may need to Chapter 4, Screen Design Page 29 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 set the window margins to do this. HELP WINDOWS There are two defaults that can be set for the Help windows - the bottom row and the window modes. This section shows how these can be modified. Bottom Row - All help windows are centered column-wise on the CRT. However, the default bottom row of the window is assigned to helpbottomrow and is allowed to grow upward. This variable is used to calculate the upper left corner of the window and sets the row and col for each help window structure. Window Modes - The current example program uses a shadow with zoom effect when the help window appears. The cursor mode is also turned off. This default is assigned to helpwndwmodes which in turn assigns it to hwmodes in each help window structure. Example - In the shell program, search for helpwndwmodes and modify the code to the following: ... helpwndwmodes = CURSOROFFMODE; helpbottomrow = crt_rows-10; ... When you test any help window this time, the window will appear instantly without the zoom effect and is placed higher from the bottom. START-UP MENU Control Variables - When the program first starts, you can also have the program pull down any menu using the top line variables mpulled, pulldown, and morecmdseq. Search again for toplinerow and see this code: toplinerow = 1; mpulled = FIRST_MENU; strcpy( morecmdseq, "F" ); pulldown = 0; mpulled - This is the initial assignment for the main menu title that would be highlighted when pressing F10 for the first time. morecmdseq - This is the actual variable that is set by the global key routine setcmdseq. When F2 is pressed for the first time, it would emulate pressing F10 and "F" from the keyboard as if it remembered the last sequence of keystrokes. morecmdseq can have as many characters as needed to get down to the desired menu or window. It is a good practice to make sure the first character matches the mpulled menu. pulldown - If this is set to 1, the program will pull-down the menus just as if F2 had been pressed so that the menus pulled match the sequence in morecmdseq. Try setting pulldown to 1 and see if FIRST_MENU is indeed Chapter 4, Screen Design Page 30 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 pulled. OVERRIDING DEFAULTS PULLC configures almost everything automatically, but sometimes things don't always fit the mold. This section will show you how to override the settings before and after PULLC has configured them. getuserpullstats - row/col locations for submenus are located by default at run time with the slide-up configuration when these values remain zero, and likewise dwrow/dwcol for data windows for slide-under. You can override PULLC and locate them absolutely by giving them non-zero values in the function getuserpullstats. Submenu Location - If submenus are unusually wide and will not fit in the slide-up configuration, PULLC will terminate the program with a warning message to let you know of the problem. If you set the variable location_warning to 0 in getuserpullstats, you can go ahead and test all submenus to see the one not fitting the slide-up configuration. PULLC will attempt slide-under as a second best. If it still doesn't fit, then you need an override for the row and col setting. If you are satisfied with the menu locations, you can optionally set location_warning to 0. But be sure to check all the menus on the CRT to make sure they have been properly placed. getoverridestats - This function in PULLSTAT.C is executed after all the automatic configuration has been done. If there are exceptions in your program, you can assign them inside this function. For just a few changes, you can edit the structures right in the heap. Take a look at this function in PULLSTAT.C for PULLDEMO. What kind of things would you typically change? Menu Command Letters - Not all menus use the first letter for the command letter. What happens when two lines start with the same one? Only the first one will ever be reached. So, the command letters must be changed. The variable in the menu structure that contains these letter is cmdltrs. For instance, the command letters for FIRST_MENU are "ABCD" where the first character corresponds to line 0, etc. They are always upper case in this string, but can be lower case in the menu. So, you can edit this string to whatever your command letters need to be. In addition, that letter will also be highlighted in the menu. Inaccessible line modes like COMMENT and PARTITION replace the command letter with 255. Top Line Command Letters - You can also do the same with the top line command letters which are kept in a global variable called topcmdltrs. Attributes and Borders - Sometimes the default attributes and borders need to be changed in a place or two. getoverridestats is the place to make those changes. Chapter 4, Screen Design Page 31 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 SUMMARY If you have completed the tutorial with the shell program up to this point, you should now have enough confidence with the major components of screen design. You can now develop a concept for your application that can best be suited the needs of your users. If you would like to stop now, save the shell program so that it can to be used later in the following sections. Chapter 4, Screen Design Page 32 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 5. D A T A W I N D O W S Often when a menu line is selected from a menu, a means is needed to enter data into the application such as numbers or file names. PULLC already has powerful utilities to do this for free-field data entry including flex fields, set checking, key translation, and range checking for ten types of data. DATA WINDOW PARTS There are two parts to a data entry window - the data entry field and the window. Data Entry Field - As the name implies, this is the place where characters are allowed to be entered for the data. This is also called entry or field for short. Data Window - The window is the border and spacing surrounding the field. Using the term data window loosely refers to both the field and the window together. DATA WINDOW STRUCTURE The arrangement of the structures for data windows is very similar to the way menu structures have been done. So, the same concept of fill-in-the- blank is used. In this section, you will find how these structures can be filled out in the program. Top Data Window - Let's find the data window structure and see what it contains: 1. Load the PULLDATA.C file for PULLSHEL into the TC environment. 2. Set the Project file to PULLSHEL.PRJ. 3. Search for "getdatawndw(AUBYTE_DW)" 4. See the following code: getdatawndw(AUBYTE_DW); /* just gets cleared topdatawndw */ topdatawndw.entry.varaddr = &aubyte; topdatawndw.entry.typeofdata = UNSIGNED_BYTES; topdatawndw.entry.field = 3; topdatawndw.entry.setname = NO_SET; /*topdatawndw.entry.justify_output = RIGHT;*/ /* this is the default */ /*topdatawndw.entry.msglinenum = DW_ML;*/ /* this is the default */ topdatawndw.entry.helpwndwnum = DATAWNDW_HW; savedatawndw(); /* saves it in the heap */ In the tutorial program PULLSHEL, the program does not have any data windows linked to the menus yet, but this structure has already been written to speed things along. As you can see, only four lines were needed to be set in the data window structure which is named AUBYTE_DW. Let's go ahead and link this structure into one of the menus. Chapter 5, Data Windows Page 33 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Linking Data Windows - Go back to PULLSTAT.C to find the FIRST_MENU structure and revise it to add this data window to line 2: getmainmenu(FIRST_MENU); ... strcpy( topmenu.line[0], "A line" ); strcpy( topmenu.line[1], "B line" ); strcpy(topmenu.line[2],"Enter Byte"); topmenu.linemode[2]=TO_DATA_WNDW; topmenu.linknum[2]=AUBYTE_DW; strcpy( topmenu.line[3], "D line" ); ... When the program is run, pressing RETURN on line 2 will pull-down the data entry window. The variable is an unsigned byte type which it was assigned in the data window structure. Go ahead and experiment with the entry by making mistakes such as a number over 255. When you get an error message, just press ESC. For the full list of editing keys, press F1 for the help window. Window Location - Notice that the location of the window is immediately underneath the hilite and shifted slightly to the right. This is called a slide-under configuration. Since there is only one row in a data window, they tend to grow horizontally. Should the window be too long for the right side of the screen, the window would then slide under the hilite to the left until it fit. This location is determined at run time. Justification - The entry is always left justified since it is an input- only window. Line Mode - If you remember about line modes, we did not get a chance to test TO_DATA_WNDW. So, now you can see that this instructs PULLC that a data window is linked to the menu and it then uses the named data window structure for the window. Link Number - The number of the Data Window structure linked is of course the value of the name AUBYTE_DW. Again, names are used to easily arrange and identify the contents of the structure. So, how is the name assigned? Data Window Names - In the file PULLSHEL.H, the enum declaration datawndwnames has the following list: enum datawndwnames { AUBYTE_DW, NO_DW=255 }; Only one window name has been assigned and that is the one being tested. There are no reserved names except NO_DW. Ok, the data is plugged into the window, but where is it being stored? DATA WINDOW VARIABLE Variable Address - To know where the variable is located, the structure uses a pointer to the variable location. The variable used in this window Chapter 5, Data Windows Page 34 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 is aubyte which is a variable declared at the beginning of PULLDATA.C and given a value of 100. To get the address of the variable, you just place the "&" operator in front of it. Type of Data - Because a pointer is being used instead of the variable itself, it is not known how many bytes the variable uses in memory and neither is the type of data. So, PULLC must be instructed what type of data is being accessed. PULLC can handle ten types of data. The enumeration for this is in PULLC20.H: enum typesofdata { BYTES, /* actually chars, but numerical */ UNSIGNED_BYTES, /* " " " " */ INTS, UNSIGNED_INTS, LONGS, UNSIGNED_LONGS, #ifdef USE_FLOATEMU_CODE FLOATS, DOUBLES, #endif USERNUMS, CHARS, STRINGS }; The associated data types should be intuitive. A special case is USERNUMS which are really strings, but are meant to be displayed as numbers like hex for example. Remembering that all zero values in the structure are the default, now you can see why typeofdata defaults to BYTES when nothing was assigned - good ol' fill-in-the-blank again. Matching the Type - This arrangement of splitting the variable into a type and a pointer is most convenient for the data window. However, it is up to your skills as a programmer to ensure that the type truly matches the data at that destination as PULLC both reads and writes to it. Validity Check - All numeric types are given a validity check. If the data entry can successfully be converted to the specified numeric type and without overflow, the data is considered valid. If not, the error message "Invalid entry." is displayed. For now, here is a hint about error messages. You can see the string for this message earlier in the file: strcpy( errmsgline[INVALID_EM], " Invalid entry. ESC-" "acknowledge" ); FIELDS This section gives instructions on how to adjust the field sizes for the data entry window. Field - Let's see how easy it to add a new data window on our own for strings and adjust the field sizes. Right after the AUBYTE_DW structure, add the following new structure: Chapter 5, Data Windows Page 35 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 getdatawndw(MYSTRING_DW); topdatawndw.entry.varaddr = mystring; topdatawndw.entry.typeofdata = STRINGS; topdatawndw.entry.setname = NO_SET; topdatawndw.entry.field = 25; savedatawndw(); At the top of the file, declare the variable mystring: unsigned char aubyte = 100; int aint = 200; char mystring[25] = "This is my string."; Now let's link it into line 3 of FIRST_MENU. So, in PULLSTAT, modify the FIRST_MENU structure to: getmainmenu(FIRST_MENU); ... strcpy( topmenu.line[0], "A line" ); strcpy( topmenu.line[1], "B line" ); strcpy(topmenu.line[2],"Enter Byte"); topmenu.linemode[2]=TO_DATA_WNDW; topmenu.linknum[2]=AUBYTE_DW; strcpy( topmenu.line[3], "My string" ); topmenu.linemode[3]=TO_DATA_WNDW; topmenu.linknum[3]=MYSTRING_DW; ... And let's not forget to add MYSTRING_DW to the datawndwnames enum: enum datawndwnames { AUBYTE_DW, MYSTRING_DW, NO_DW=255 }; Run the program and test the data window. Since field was set to 25, the window was expanded to allow a 25 character entry with a space on either side. So, whatever the field width is, that is how many characters can be entered in the string. Notice that we were careful not to make field larger than the string size so it would not overwrite any characters at mystring's address. But what if the string can have up to 100 characters? How can that be made to fit? Maximum Field - There are actually two variables to adjust the size of the entry field. One is field and the other is maxfield. Rather than explaining it, let's see what it can do. First change the string size to: char mystring[101] = "This is my string."; and then add the variable setting of maxfield in the data window structure: ... topdatawndw.entry.field = 25; topdatawndw.entry.maxfield = sizeof(mystring)-1; /* 100 */ savedatawndw(); Chapter 5, Data Windows Page 36 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Now when you run it, the field is flexible. You can now enter up to 100 characters even though the field only displays 25. These fields are called flex fields. Default Field - By default, the program sets maxfield equal to field. Anytime maxfield has been set and is not equal to field, the flex field becomes active. So, this can be used with any type of data and not just strings. TITLES If your field is wide enough, you can easily add a title. Try adding the following code to the MYSTRING_DW structure: getdatawndw(MYSTRING_DW); strcpy( topdatawndw.title, "Enter File Name" ); topdatawndw.entry.varaddr = mystring; topdatawndw.entry.typeofdata = STRINGS; topdatawndw.entry.field = 25; ... Pulling down this data window, the title will be centered on the top row of the window. Titles are optional and will be truncated if necessary to fit within the window. EDITING KEYS If you didn't get a chance to try out the full editing keys, here's the list and their function: Keys Movement ---------------------------- ----------------------------------------- Left Arrow / ^S Character Left/Right Right Arrow / ^D Character Left/Right Home / Ctrl Left Arrow / ^A First character End / Ctrl Right Arrow / ^F Last character Del / ^G Deletes character under cursor Backspace / ^H Deletes character to left of cursor Ins / ^V Toggles between insert and overwrite mode ^R/^U Restores original contents ^Y Deletes entire contents Insert Toggle - PULLC allows you to use both insert and overwrite mode. You can tell the status by the shape of the cursor. The half-block cursor indicates the insert mode which appears correctly in any video mode. Cursor Handling - In flex fields, some special cursor handling has also been included that you will appreciate when the cursor is at either end. When adding characters to the far right there is always one space open until the maximum character is reached. It helps identify the last character and shows the character when the Del key is used. The same principle is used when backspacing - at least one character is always shown Chapter 5, Data Windows Page 37 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 until the last one is deleted. The cursor always remains confined inside the field width on a flex field. One-Column Field - A special case is considered when the field is 1 column in width. The cursor does not move and any valid character typed in will overwrite the contents. Del or Backspace also deletes the contents. Please do not consider using flex fields for this case. autonumlock - On machines with enhanced keyboards, it may be an advantage to automatically turn on the NumLock when in edit mode. If so, by setting autonumlock to 1, the numeric keypad will have NumLock turned on when you start editing. After the edit, PULLC will restore it to its original mode, on or off. Please note that even though the "NUM" message appears on the message line to show the true internal status of NumLock, many machines do not have a smart enough BIOS to also toggle the NumLock LED on the keyboard itself. Pressing the key can put it back in sync, but the "NUM" message is the thing to watch. KEY SETS This section gives instructions on how to use strings ("sets") of characters to screen for valid keystrokes into the data entry. Entry Sets - As a preventative measure, each keystroke is checked against a string to see if it is valid before it is allowed into the field. The check is done using strchr() to determine whether the character typed is in the string. If it is not, the keystroke is simply ignored. Several strings have been included, and a default is given to each of the ten types of data: enum setnames { UNSIGNED_SET, SIGNED_SET, #ifdef USE_FLOATEMU_CODE FLOAT_SET, #endif CHAR_SET, HEX_SET, FILENAME_SET, PATH_SET, MASK_SET, NO_SET=255 }; where the following types are given the following sets: Type Set --------- ------------ BYTES SIGNED_SET UNSIGNED_BYTES UNSIGNED_SET INTS SIGNED_SET UNSIGNED_INTS UNSIGNED_SET LONGS SIGNED_SET Chapter 5, Data Windows Page 38 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 UNSIGNED_LONGS UNSIGNED_SET FLOATS FLOAT_SET DOUBLES FLOAT_SET USERNUMS CHAR_SET CHARS CHAR_SET STRINGS CHAR_SET Four more practical sets have also been included for your use. To examine the contents of the sets, they are located in PC20VAR.C in an array called entryset. Assigning Sets - setname will always default to UNSIGNED_SET if you do not set it. The best thing to do is set it to NO_SET. This will do the work for you by default. However, once in a while a custom set will be required. Let's change mystring to be a file name with only valid DOS characters by changing the MYSTRING_DW structure to: ... topdatawndw.entry.field = 25; topdatawndw.entry.maxfield = sizeof(mystring)-1; /* 100 */ topdatawndw.entry.setname = FILENAME_SET; savedatawndw(); Try entering a string into the window and find that invalid characters like the space, "\", and "*" can not be entered. This gives the user immediate cues about the validity of the entry before it is completely entered. KEY TRANSLATION As each key is entered, it is possible to intercept the keystroke before it reaches the data entry editor. This section will show how it is done. Translation Pointer - Each data window structure has a translation pointer called translatefunc. You probably know by now that the default is NULL. If a valid function is assigned to this pointer, it will be executed. Suppose you prefer to have the file name entries in upper case. Modify MYSTRING_DW to: ... topdatawndw.entry.field = 25; topdatawndw.entry.maxfield = sizeof(mystring)-1; /* 100 */ topdatawndw.entry.setname = FILENAME_SET; topdatawndw.entry.translatefunc = translatecase; savedatawndw(); Back up a bit to find this function and you will see: void translatecase(void) { if(!extkey) key = toupper(key); /* simple upper case translation */ } Chapter 5, Data Windows Page 39 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 In PULLC, key is the character returned from readkey, and extkey is set to 1 if it is an extended key. Run the program again and find that all keys typed in this data window are forced to upper case. Every Key - All keys can be translated with this function. Even global or editing keys can be intercepted and translated to whatever is desired. It is also useful for foreign language translation. RANGE CHECKING After the entry is entered and checked as valid, there is one more option of checking the entry to make sure it is in the range or form that is acceptable to the program. This section will show how to create range checking functions and out of range error messages. Three Parts - Before setting up a range check, you need to understand how the data is transferred between the entry and the variable. There are three parts to performing the data entry transfer - the data entry string, data pad, and destination variable. Data Entry String - The data entry string is the string that is seen and edited on the CRT. This string is held in datastr and is declared in PC20VAR.C. Reading - The data pad, called datapad, is a two-way messenger that transfers data between the variable and datastr. When the data window is initially displayed, the data pad reads the contents of the variable and makes an exact copy of the value onto itself. Then the program converts this value over to datastr into a string form which the user can easily read and edit. Storing - As mentioned before, pressing RETURN after editing is completed, the program attempts to store the entry to the variable. First, numeric data must pass a validity check by converting datastr to a value. The check differs between types of data (see PC20DATA.C for details). If it passes the check, a copy of the converted data is now on the data pad. Now is the time to do a data range check. By default, there is no check and the data would continue to be stored from the data pad to the destination variable as is. But to do the range check, you need to know the identifiers that are used on the data pad. Data Pad Identifiers - The data pad is completely generic. Any type of data occupies the exact same address. So all you need to do is select the right macro to match the type of data for your variable. The macros simply typecast the address of datapad.dataspace to a pointer to a different type of data. They are used by coding, for instance, "bytedata(datapad)". This is not a function call, we're simply tapping the power of macros. You can find the list of these "shortcut" macros in PULLC20.H. Chapter 5, Data Windows Page 40 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Type Macro -------------- ---------- BYTES bytedata UNSIGNED_BYTES ubytedata INTS intdata UNSIGNED_INTS uintdata LONGS longdata UNSIGNED_LONGS ulongdata FLOATS floatdata DOUBLES doubledata USERNUMS userdata CHARS chardata STRINGS strdata Example - Let's set up a range check for AUBYTE_DW. Modify its data window structure to: getdatawndw(AUBYTE_DW); /* just gets cleared topdatawndw */ ... topdatawndw.entry.field = 3; topdatawndw.entry.checkrangefunc= checkaubyte; /* add this line. */ ... savedatawndw(); /* saves it in the heap */ Arbitrarily, let the range for aubyte be between 20 and 50 inclusive. Now back up a few lines before the translatecase function and the following function has already been added for you: void checkaubyte(void) { if(ubytedata(datapad)<20 || ubytedata(datapad)>50) makeerrmsg(20,50); } Since aubyte is an unsigned char, ubytedata is used for the comparison. For the range check, any values under 20 or over 50 will run the makeerrmsg function. Try it and see if you can produce the error message. Error Messages - How does PULLC know if an error is found? It tests the value of errmsg in datapad. If the value is NO_EM, then the range check is passed. Otherwise, it has failed. And, it uses this same value as the error message name. The errmsglines are much like the msglines using names for indexes. Right after the implementation you can see the error message names: enum errmsgnames { USER_EM, INVALID_EM, MY_EM, NO_EM=255 }; The names USER_EM, INVALID_EM, and NO_EM are reserved. The name USER_EM is of most interest. errmsgline[USER_EM] is meant to be customized by creating a message at run-time. This saves you from making several hard- Chapter 5, Data Windows Page 41 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 coded messages. The example used makeerrmsg, located a few lines back. It is a good simple example how to set both errmsg and create a custom message on the USER_EM. But the important value is the change of errmsg. Message Length - Error messages tend to be short, so there is no need to allocate a full screen width for the message. The maximum length of a message is set by ERR_STR_SIZE in PULLC20.H. The program will automatically clear the remaining part of the line and takes special care not to do a needless full-line clear. This prevents a flicker effect. Maximum Message - Similarly, the maximum number of error message lines is set by NUM_OF_ERR_MSG_LINES in PULLC20.H. Summary - The outcome of the range check is determined by the value of datapad.errmsg. PULLC sets it to NO_EM. Compare your range with the datapad identifiers in any fashion. Only if it is out of range, set errmsg to the errmsgline you want to show. HELP MESSAGES A help message can be assigned to the data window structure exactly the same as the menus. One name has already been reserved for the data window called DW_ML and it is the default. But you can also assign new ones. The msglines and names are in PULLSTAT.C. HELP WINDOWS Again, just like the menus, help windows can be assigned to data windows. The name reserved for data window structures is called DATAWNDW_HW. If you have not seen this window, run the program with a data window pulled and press F1. The help window structures and help lines are in PULLSHEL.H. DEFAULT ATTRIBUTES AND BORDER This section will show the variables used to create default attributes and border for the data window. Initialization - PULLDATA is initialized by the function initpulldata, which is called by initpull. There are two functions that set up the colors and border: setdefaultcolors - assigns colors and border to the default variables. initdatacolors - assigns the defaults to the data window structure. setdefaultcolors - In PULLDATA, search for setdefaultcolors and see the following code: if(qvideo_mode==MONO) { dataentryiattr = LIGHTGRAY; /* input attribute */ dataentryoattr = WHITE; /* output attribute */ datawndwiattr = WHITE; /* input attribute */ Chapter 5, Data Windows Page 42 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 datawndwoattr = LIGHTGRAY_BG; /* output attribute */ } else { dataentryiattr = YELLOW+MAGENTA_BG; /* input attribute */ dataentryoattr = BLACK+LIGHTGRAY_BG; /* output attribute */ datawndwiattr = BLACK+BROWN_BG; /* input attribute */ datawndwoattr = YELLOW+BLACK_BG; /* output attribute */ } datawndwbattr = BLACK+BROWN_BG; /* border attribute */ datawndwbrdr = HDOUBLE_BORDER; initdatacolors - These variables in turn are assigned to the following structure variables in datawndw: Struct Default variable Variable Description ---------------- --------- ---------------------------- datawndwiattr iattr Attribute for input datawndwoattr oattr Attribute for output datawndwbattr battr Border attribute datawndwbrdr border border of the window Just like the menus, this saves you from having to make the same assignment to every menu structure. Try experimenting with the colors and border in the shell program and see the results. Although NO_BORDER is permissible, it is not suggested. DEFAULT LOCATION The location of a data window is placed by PULLC at run time. With the slide-under configuration, the menu is placed underneath the hilite and shifted to the right 2 columns. If necessary, it is shifted to the left to prevent wraparound. To alter this position manually, set row and col in the menu structure to your desired location. SUMMARY In addition to mastering the menus, you can now link data windows into the menus by adding and linking data window structures in PULLDATA.C. You have been able to address the entry variable and create a window suitable for the data type including the field type and size, title, key set and translation, range checking, error and help messages, and help windows. With this environment in your application along with the power and speed of QWIKC and WNDWC, your programs can match and even better professional programs. Chapter 5, Data Windows Page 43 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 6. D A T A E N T R Y Data Windows in the pull-down menus are not the only place where data is needed to be entered. The work windows themselves may need several data entry fields as well. PULLC simply uses a subset of data windows to handle the job very easily. In addition, a smart hilite automatically knows the location of each field in the window. DATA ENTRY vs. DATA WINDOWS Data Windows have both a data entry field and a window surrounding it. In work windows, all that is needed is the data entry field. So, to distinguish between data entry in the pull-down menus and the work windows, the data windows are only in the menus, while data entry will be considered in this document to be in the work windows. DATA ENTRY STRUCTURE One data entry field has already been included in the work window. Let's take a look at its structure. In PULLDATA, find getdataentrystats and search for "getdataentry(AINT_DE)" to see the following code: getdataentry(AINT_DE); topentry.varaddr = &aint; topentry.typeofdata = INTS; topentry.row = 2; topentry.col = 11; topentry.field = 4; topentry.maxfield = 3; topentry.checkrangefunc = verifyaint; topentry.msglinenum = DE_ML; topentry.helpwndwnum = DATAWNDW_HW; savedataentry(); The structure identifiers should look quite familiar because they are the same ones use in a data window structure. The only differences are the get and save functions, because the structure is defined of type dataentry_t rather than datawndw_t. If you examine these typedef declarations in PULLC20.H, you will find that there is a dataentry_t inside of datawndw_t. So, data entry is truly a subset of data windows. Variable - The variable is aint, which is declared early in the file. row/col - This time, a row and column is specified where the left column of the field is to appear in the work window. These coordinates are window relative. Default Helps - Both the help message and help window are set to a default so your program can be up and running without being concerned about details. Being in a different part of the pull-down menu environment, the DE_ML is different from DW_ML because the function of F2 is the opposite to either case. Chapter 6, Data Entry Page 44 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Other Variables - All the other variables in the structure should be familiar to you from the explanations in the previous section on data windows. So, there is no need to repeat them here. ADDING ENTRIES Entry Record - We have already seen how to add entries with the fill-in- the-blank concept, so let's try adding another entry to the work window. Just after the AINT_DE structure, add the following code: getdataentry(ADOUBLE_DE); topentry.varaddr = &adouble; topentry.typeofdata = DOUBLES; topentry.row = 3; topentry.col = 11; topentry.field = 12; topentry.precision = 10; savedataentry(); Precision - This variable is just for floats and doubles to specify the precision to be used for the output display. gcvt() is used for the conversion. adouble - You can set up a default value for adouble. At the beginning of PULLDATA, add this declaration: ... int aint = 200; double adouble = 4.56e7; ADOUBLE_DE - Now append the data entry structure name to the dataentrynames list in PULLSHEL.H: enum dataentrynames { AINT_DE, ADOUBLE_DE, NO_DE=255 }; Now run the code and see if it appears in the work window. When it runs, the field does not appear. Why? DISPLAYING FIELDS Work window - The work window controls what is to appear inside the window. This code is in the file PULLWORK.C. Search for "displayfields" and see: ... wwrite( 2, 6, "Int:" ); displayfields( AINT_DE, AINT_DE ); ... Chapter 6, Data Entry Page 45 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 displayfields - This is the code that displayed the Int field. The displayfields function displays a sequence of fields in order of the list of dataentrynames. It is prototyped in the header file of PULLC as: void displayfields( unsigned char first, unsigned char last ); So, what it does is display each field starting with first and ending with last. Looking back in PULLWORK again, it displayed structures both beginning and ending with AINT_DE. So, it never reached ADOUBLE_DE. Let's modify the function call to include ADOUBLE_DE as follows: displayfields( AINT_DE, ADOUBLE_DE ); Field Label - Running it, the ADOUBLE_DE will be displayed this time. But it doesn't have a label, so add one to the window with: ... wwrite( 2, 3, "Int:" ); wwrite( 3, 3, "Double:" ); displayfields( AINT_DE, AINT_DE ); ... Justification - adouble now appears on the screen with the field right justified. By default, numbers are justified to the right and strings to the left. To alter this, just include your setting in the data entry structure assignments. Let's try left justifying adouble by adding the following code in PULLDATA.C: getdataentry(ADOUBLE_DE); ... topentry.precision = 10; topentry.justify_output = LEFT; /* add this line */ savedataentry(); Everything appears correctly on the screen. But when we try to move the hilite, it just stays on the int field. How can we make it select the other field? Real easy - keep reading. SEQUENTIAL ENTRY With sequential entries of several fields, PULLC gives you the advantage of using not just one movement with the hilite, but both relative and sequential movement. enterseq - PULLC makes sequential entry as natural as can be. In PULLWORK.C, search for enterseq and see: enterseq( AINT_DE, AINT_DE, &start1 ); This function's syntax, found in PULLC20.H, is as follows: void enterseq( int first, int last, int *start ); Chapter 6, Data Entry Page 46 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 enterseq allows you to enter a whole block of entries in the dataentry array of structures where "first" and "last" are the first and last structures in the block. ADOUBLE_DE was not included in this block. So let's modify the code to: enterseq( AINT_DE, ADOUBLE_DE, &start1 ); Now run the code and find that the highlight can now move freely between the two fields. That's all there is to do! PULLC handles the rest. start - start is the structure index where the highlight is to begin. At the top of the file, start1 is initialized to AINT_DE. This lets enterseq remember where the hilite is after using the menus. Smart Hilite Algorithms - Take a break from PULLSHEL and go back to PULLDEMO. Run the program and take a look at the work window with several entries. PULLC has smart algorithms that know where all the fields are located on the screen. The hilite can be moved freely to select any field with the following keys: Keys Movement Type of Movement ----------------------- ------------------------- ---------------- Left/Right Arrow Left/Right Relative Up/Down Arrow Up/Down nearest cursor Relative Home / Ctrl Left Arrow First one on the row Relative End / Ctrl Right Arrow Last one on the row Relative PgUp / Ctrl Home First in sequence Sequential PgDn / Ctrl End Last in sequence Sequential Tab / Shift Tab Next/Previous in sequence Sequential Relative Movement - PULLC checks the block of entries to see where to move the hilite relative to the current field. If the field is already at its limit, say to the far left with the left arrow key, it does not wrap around to the far right. When the hilite is moved up or down, PULLC looks for the field nearest the cursor, not the full width of the field, and also does not wrap. Sequential Movement - Primarily, the Tab key is used for sequential movement. If you reach the end of the sequence, the next Tab will wrap back to the first. What determines sequential movement? It's the order of the dataentrynames. So, changing the sequence is as simple as reorganizing the names - no interlinks between fields are necessary! Now you can insert a new field without fretting over links. Auto Tab - The value of sequential entry is the order in which you want to enter fields. After entering one field, the program should be smart enough to go to the next one. With autotab set to 1, PULLC will jump to the next field in sequence automatically after each entry. Let's try it again on the PULLDEMO program. 1. Get into the work window. 2. Press PgUp to get to the "Byte" entry field. 3. Enter any value in range and press RETURN. 4. See that the hilite is now on "Unsigned byte". Chapter 6, Data Entry Page 47 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 To prove the point of relative and sequential movement, let's move one of the fields to a different location. In PULLDATA, search for the data entry structure for ABYTE_DE, and change the location to: getdataentry(ACHAR2_DE); topentry.varaddr = &achar2; topentry.typeofdata = CHARS; topentry.row = 5; /* change the row (was 15) */ topentry.col = 5; /* change the column (was 50) */ ... Run PULLDEMO and see the field moved to (5,5). Moving the up arrow as far as it will go, the hilite will end up on ACHAR2_DE. But when you press Tab, the next field is "String". Now, suppose we want ACHAR2_DE to be first in sequence. How easy is it? Look for dataentrynames in PULLDEMO.H and see: enum dataentrynames { ABYTE2_DE, AUBYTE2_DE, AINT2_DE, AUINT2_DE, ALONG2_DE, ADOUBLE2_DE, AHEX2_DE, ACHAR2_DE, ASTRING2_DE, FILENAME_DE, NO_DE=255 }; There is a bunch of names here, but all we are interested in is moving ACHAR2_DE. Move the name to the top of the list. But now we have changed the beginning name of the block from ABYTE2_DE to ACHAR2_DE. So, go into PULLWORK and do a global search and replace of ABYTE2_DE with ACHAR2_DE. Three familiar lines will be changed. Run the program now and see that PgUp will now move the hilite to the top and a subsequent tab will move it to Byte. This makes organizing your screen very easy. Setting Autotab - In PULLDATA, search for autotab which is right at the beginning of the data entry structures. The current value is 1. If this value is set to 0, then the hilite would stay in the same field after entry. EDIT MODE When the hilite moves from field to field, it is in Select Mode. But when you are editing a field, it is in Edit Mode. What exactly makes it change from Select to Edit mode? Overwrite vs. Edit - To overwrite the contents, just start typing the new entry. To edit the contents, the suggested key to use is the Return key. When pressed, the hilite changes color and the cursor is set past the last Chapter 6, Data Entry Page 48 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 character. Non-Extended Keys - In addition to the Return key, any non-extended key will work as well and will also apply that key to the field. For example, while in select mode, if ^A is pressed, the hilite would change into Edit mode and, in addition, the cursor would be moved to the first character. Invalid Characters - If the key pressed is not a member of the entry key set, it acts the same as return on the first key, but is otherwise ignored. Escape - To escape Edit mode and restore the original contents, just press ESC and the hilite will return to Select Mode. If ESC is pressed again, the program would escape the entire sequence of entry and would return control to the workwindow function. FIELD ATTRIBUTES There are three attributes that can be modified for the data entry fields - the display, the edit, and the hilite attributes. Default Attributes - Data entry fields have default attributes for each structure just like the data windows. These variables are also set in setdefaultcolors and initdatacolors. setdefaultcolors - In PULLDATA, search for setdefaultcolors and see the following code: dataentryiattr = YELLOW+MAGENTA_BG; /* input attribute */ dataentryoattr = BLACK+LIGHTGRAY_BG; /* output attribute */ initdatacolors - These variables in turn are assigned to the following structure variables in dataentry: Struct Default variable Variable Description ---------------- --------- -------------------- dataentryiattr iattr Attribute for input dataentryoattr oattr Attribute for output Hilite Bar - The hilite attribute is set by the color on the data pad called datapad.hattr. This variable is right next to autotab in PULLDATA. Rather than having both the cursor and the hilite, the hilite can be turned off by assigning hattr the value of SAMEATTR. Then each field will appear in its own display attribute. SINGLE ENTRY If you prefer to customize your own functions, you can use the function enter in lieu of enterseq. The function is prototyped in PULLC20.H as: void enter( int recnum ); Chapter 6, Data Entry Page 49 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 This accesses the same powerful editing features as enterseq, but only works on the one field indicated in the parameter. It does not display the fields before or after editing. This must be done with displayfields. SUMMARY You have just covered enough features to master data entry in the work windows or user windows. You can assign the structures, display and locate the fields, control the sequence of entry, and adjust the appearance with the justification and attributes. You also learned how to direct PULLC's built in hilite bar for field selection. Chapter 6, Data Entry Page 50 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 7. W O R K W I N D O W S The bulk of your application needs to be displayed somewhere, and the Work Windows are used for this purpose. In this section, you will discover how to integrate your work window functions in a pull-down menu environment so the program can randomly access any function. In addition, you can create a sophisticated multi-level window environment. MAKING STEPS In this section, you will find how to break down functions into individual steps and still allow the flow of execution to cycle through the key dispatcher. Requirements - When functions are broken down into steps, they can do most anything. But each step must exit with two settings: 1. Assignment made for the next workwndwstep. 2. Assignment made for key and extkey. Example - Let's do an example to understand the flow of execution. Get the original files for PULLDEMO.C - PULLWORK and PULLDATA. Now take a look at the last of PULLWORK and see: void workwndw(void) { ... switch(workwndwstep) { case 0: initworkwndws(); break; case 1: editfields(); break; } } With this construct, your work can be separated into different steps. To add a new step, just insert one. To demonstrate how to create a new step, let's separate the left and right columns of the data entry fields into two separate steps. So, let's add step 2: ... case 0: initworkwndws(); break; case 1: editfields(); break; case 2: editfields2(); break; } and change editfields to: void editfields(void) { displayfields( FILENAME_DE, FILENAME_DE ); enterseq( ABYTE2_DE, ADOUBLE2_DE, &start1 ); /* change this line. */ if(key==ESCKEY) workwndwstep=2; /* add this line. */ } Chapter 7, Work Windows Page 51 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 This will isolate the sequence to the left column of fields. And now add editfields2: void editfields2(void) { displayfields( FILENAME_DE, FILENAME_DE ); enterseq( AHEX2_DE, FILENAME_DE, &start2 ); if(key==ESCKEY) workwndwstep=1; } start2 has already been added for you. Set the TC project file to PULLDEMO.PRJ. Now compile, link and run the program and see that you can only access the left or right sequence of fields. If you want to swap between the left and right columns, just press ESC. Flow of Execution - ESC has been assigned inside enterseq as the key that is used to exit the function. This is called a gated exit. Once it exits, it also exits workwndw and goes back into PULLC to analyze the keystroke in the key dispatcher. If no key combinations are used to access the menus, execution will return right back to workwndw and into the right step. You can catch the execution flow by testing for the Esc key right after enterseq. If so, then change the step as we did in the example. Menu Access - Why even bother to test the key if ESC exits enterseq? The Esc key is not the only key that would exit this function. Any menu key like F10 will also exit the same way. So, the "if" statement is needed to confirm the correct key before changing the step. Updating the Window - Did you notice that displayfields was used twice for just one field? Any time you exit a menu and reenter the window, there is a possibility that something may need to be updated. And this time the File name field is a possibility because it was linked to the output of the file directory. If you haven't had a chance, press Alt-D to get the directory and pick a file by pressing RETURN. The selected file name will appear in the File name field. The other link is that the file name data entry field also preselects the initial file name when the directory is pulled again. But there are other alternatives than using displayfields twice. Changing Steps - This example was quite simple. When step 1 was complete, workwndwstep was incremented to step 2. When step 2 was done, it was cycled back to step 1. It continues to stay in this loop until the program is terminated, or until some other function changes the step number. In step 0, if we had failed to assign a new step at the end of the step, the program would have been locked in an infinite loop, because it never would access any keyboard input. READING THE KEYBOARD In some steps, the program may need keyboard input while others do not. In this section you will find out how to read the keyboard within each step. Output-Only Step - Step 0 in the example, is an output-only step that displays the fields in the window. No keyboard input was required. So, at Chapter 7, Work Windows Page 52 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 the end of the step, we needed to emulate a keystroke of some kind to meet the step requirements. By setting key to NULLKEY (0), the key dispatcher will bypass a call to the menus and ignore the key. This guarantees that the flow of execution will return to back workwndw. extkey should also be set properly if a valid combination is needed. Some keys do not require it such as NULLKEY, ESCKEY, and RETKEY. See your reference book for more details. Input and Output Step - Most steps will require both input and output. Step 1 called enterseq which has a keyboard reading routine built in. So, no keystroke emulation is needed. However, the last line in the step still needs to check for a change of step. Custom Input - There will be several occasions where you will want to have your own functions that read the keyboard. If you have the source code, look at PC20DATA.C for the construct of enterseq which looks like: do { ... checkforpulldown(SEQ_ML); ... checkforpop(); } while(key!=ESCKEY && !pop && !pulldown); checkforpulldown is a function available to you to read the keyboard. Its parameter, msglinenum, will show this message while the program pauses for entry. A function called by checkforpulldown is readkbd, which can be used instead. It just reads the keyboard and does not show a message. checkforpop - This function is also available to you to check if any menu control flags have been set and regulates them. If the flags indicate a pop is needed, then pop will be set to 1. Gated Exit - Now you can see two more possible reasons that would cause the program to exit enterseq - pop and pulldown. These are the very same flags used for controlling the menus. By gating the exit, the flow of execution is confined with the do/while construct until a control flag permits the exit. Using these two flags allows enterseq to be used in either the work window or a user window in the menus. If the function is only going to be used in the work window, then pop is not necessary. Non-Gated Exit - The step doesn't have to be gated at all. You can use checkforpulldown by itself if the flow of execution can continue through the dispatcher every time a key is pressed. For enterseq, this would not work, because the hilite would flash with each keystroke. The next section shows an example where a non-gated exit works perfectly fine. Idle Keyboard - Rather than just letting the program just sit there and wait for keyboard input, you could be letting the program do other background processing. A call from PULLC inside readkbd continually runs the function kbdidle. This function can be in any module, but it must be somewhere, even if the function contains nothing, or you will get linker errors. Chapter 7, Work Windows Page 53 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 MULTI-LEVEL WINDOWS One work window may not be enough for your application and others may be needed. The following example shows how to add a simple hidden work window. Making the Step - Add step 3 to workwindow: ... case 2: editfields2(); break; case 3: editworkwndw2(); break; /* add this line. */ } Now, create this editing step to just put ASCII keys into the window. A non-gated function will work fine: void editworkwndw2(void) { checkforpulldown( WORK_ML ); if(!extkey) switch(key) { case RETKEY: putch( '\n' ); putch( '\r' ); break; case ESCKEY: hideworkwndw(); break; default: if(key>=32 && key<=126) putch(key); break; } } /* non-gated exit */ The key chosen to hide the window is ESC although any key could be assigned to do this. But we also need to initialize the second work window to be available to the program. So, add the following line to initworkwndws: void initworkwndws(void) { showfields(); makeworkwndw2(); /* add this line. */ ... } and then code the function to make a hidden window and place it just after the showfields function. (The code is already there, but is disabled using "#if 0" and "#endif". Just remove these directives for this example.) void makeworkwndw2(void) { setwindowmodes( HIDDENMODE ); makewindow( 8, 21, 10, 40, LIGHTBLUE+LIGHTGRAY_BG, LIGHTBLUE+LIGHTGRAY_BG, DOUBLE_BORDER, WINDOW2 ); setwindowmodes(0); writetohidden( WINDOW2 ); titlewindow( TOP, LEFT ,SAMEATTR, "2" ); titlewindow( TOP, CENTER,SAMEATTR, " Work Window 2 " ); titlewindow( BOTTOM,CENTER,SAMEATTR, " Press ESC to Hide " ); wwritec( 1, "Type in any input" ); Chapter 7, Work Windows Page 54 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 wgotorc( 2, 1 ); writetocrt(); } At the beginning of PULLDEMO.H, let's also activate the multi-level work window code by changing the "#undef" to "#define" to look like: #define MULTIWORKWNDWS This activates the accessworkwndw function which simply accesses the selected window set by topworkwndwname. When topworkwndwname has changed, workwndwstep also needs to be reset. In the function resetworkwndwstep, confirm that WINDOW2 starts on the correct step: case WINDOW2: workwndwstep = 3; break; Now we need some keys to get access to these windows. Let WINDOW1 and WINDOW2 be assigned to the Alt-1 and Alt-2 keys. To do this, go back to PULLSTAT.C and edit checkglobalkeys to the following: ... case ALTX: setquit(); break; case ALT1: setworkwndw(WINDOW1); break; case ALT2: setworkwndw(WINDOW2); break; default: pulldown=0; break; ... The macros ALT1 and ALT2 have already been defined for you. setworkwndw is located just before checkglobalkeys and looks like: void setworkwndw( int wn ) { pulldown = 0; poptoworkwndw = 1; topworkwndwname = wn; } This assigns a new work window name for the accessworkwndw function. Now it is all set. Give the program a run. You will see that anytime you press Alt-1 or Alt-2, it immediately accesses that window even if you are in the menus! And, when you get WINDOW2, the contents are preserved. If you were able to program this successfully, you have attained a highly sophisticated environment with little code. MANAGING WINDOWS We have just covered all the code necessary to have several work windows on the screen at once. To randomly access any window, the Alt keys were assigned to a particular window number. To hide a window, the Esc key was used and the contents were saved. Chapter 7, Work Windows Page 55 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Should you decide to use temporary windows so that ESC would remove the window, just replace the two occurrences of hidewindow with removewindow. Of course, you could make a combination of both. You now have the tools for complete window management. Chapter 7, Work Windows Page 56 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 8. U S E R W I N D O W S While in the menus, you may have to create a window or a menu that differs from the ones in PULLC. This section will show you the pull-down directory included with the package and how to integrate any user window into the pull-down environment. PULL-DOWN DIRECTORY The pull-down directory module has already been linked into the demo. By now, you can probably figure out how the directory is accessed. Looking in the FILES_MENU, find the following code: strcpy( topmenu.line[2], "Directory" ); topmenu.linemode[2]=TO_USER_WNDW; topmenu.funcptr [2]=dodir; To pull down a user window, the line mode is set to TO_USER_WNDW which places a 3-bar symbol on that line. Then, dodir is the function that will access the directory and looks like: void dodir(void) { /* Use (filename,filename) to initially hilite a close match. */ /* Use (filename,'') to start at default. */ pulldirectory( filename, filename ); } pulldirectory handles everything once inside the function. A particular emphasis was made on end-user human factors in its development. . Single column - A single column, alphabetically sorted list is the easiest to visually scan quickly. Multi-column lists such as the one provided in the TC2 environment require difficult zig-zag scanning. . Cursor key scanning - Cursor keys are the expected way to scan through the directory. In addition, Home, ^Home, End, and ^End keys only move the hilite while PgUp, ^PgUp, PgDn, and ^PgDn move only the page. . Letter key scanning - Any alpha-numeric key will scan for the first file name starting with the same first letter. The page is moved and the cursor is centered as much as possible. . Lower-case text - Lower case text is more legible than the standard upper-case text provided by DOS. . Right justified extension - Visual searches for extensions are easier to read when aligned. In addition, this also facilitates proper alphabetic sorting. . High speed sort - The sorting routine uses is a secondary-index quick sort which is the fastest kind for this application. It is currently set at a limit of 250 file names are permitted, but it can be set to grow as much as your heap allows. Chapter 8, User Windows Page 57 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 . High speed scroll - The display is expected to be fast when scrolling and is. Neither the hilite nor the screen ever flicker. . Default hilite - If a value file name is passed to the directory for the default other than '', the directory searches for a close match to hilite. In either case, the hilite is centered as much as possible. . Picked file name - Pressing CR will replace the referenced file name passed to the directory. INTERFACE If you have the source code to PULLDIR.C, take a look at pulldirectory and see how the code interfaces the pull-down menus. This is an excellent example of incorporating all the features to integrate with the menus: void pulldirectory( char *nametochange, char *nametohilite ) { if(heapok( FNAMESIZE * MAXFILES)) { filenames=calloc( MAXFILES, FNAMESIZE ); if(heapok(sizeof(unsigned)*MAXFILES)) { sortindex=calloc( MAXFILES, sizeof(unsigned) ); turnarrows(ON); /* 1 */ strapp( cmdseq, topmenu.cmdltrs[topmenu.hiliteline] ); /* 2 */ showdirmenu( nametohilite ); /* 3 */ do { checkforpulldown( dirmenu.msglinenum ); /* 4 */ if(extkey) { if(helpkeypressed()) #ifdef USE_HELPWNDW_CODE pullhelpwndw( dirmenu.helpwndwnum ) /* 5 */ #endif ; else scandirbycursor(); } else if(key!=RETKEY) scandirbyletter(); if(key==RETKEY && totalfiles>0) { poptoworkwndw = 1; /* 6 */ i = filelistindex+(dirmenu.hiliteline); strcpy( nametochange, filenames[sortindex[i]] ); removespaces( nametochange ); } checkforpop(); /* 7 */ } while(key!=ESCKEY && key!=RETKEY && !pop); /* 8 */ key = NULLKEY; /* 9 */ removewindow(); /* 10 */ cmdseq[strlen(cmdseq)-1]=0; /* 11 */ turnarrows(OFF); /* 12 */ free(sortindex); } free(filenames); } } Chapter 8, User Windows Page 58 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Comments - Comments for the numbered lines follow. Line 1 - turnarrows is a function that places arrow symbols on the top menu to direct the user's attention to the new menu. Line 2 - Append cmdseq so that PULLC can know how it got to this menu. Line 3 - This function actually produces the window on the CRT and is custom designed. Line 4 - checkforpulldown monitors keyboard entry and displays a message. Line 5 - You can include your custom help window here. Line 6 - Once the item was selected on the menu, this option commands PULLC to return to the top work window. This is optional. Line 7 - checkforpop analyzes all the menu controls flags, including poptoworkwndw, to see if a request has been made to pop out of the menu. If so, pop is set to true. Line 8 - The do/while construct provides a gated exit. Line 9 - Alter the value of key so the next menu will ignore an esckey value. ESC is meant to pop only one menu. If the key was not changed, the menus would continue to pop until is was back into the work window. Line 10 - The exit has been confirmed and the window/menu needs to be removed from the CRT. Line 11 - Adjust cmdseq for one pop. Line 12 - Turn the arrows back off of the top menu. This same construct can be used for any user window/menu to be included with the pull-down menus. Chapter 8, User Windows Page 59 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 9. C O M P I L I N G T H E S O U R C E C O D E PULLC can be compiled under any model size except Tiny, and parts of the code can be excluded from compilation using a set of macros. This section will show you easy we have made these tasks. USING VARIOUS COMPILER OPTIONS If you have registered for the source code, it's likely that you will want to compile it using various model sizes and other compiler options. We have made this process easy. Use the MAKEPULL.BAT file provided to compile PULLC. When running it, you must specify the letter for the model you wish to use. For instance, to compile the source using the Compact Model, type: makewndw c Note: Turbo Assembler, MASM 4.0, or a compatible assembler must be accessible via the DOS path for MAKEPULL.BAT to be able to compile the modules where inline assembly is used. The batch file will compile the PULLC modules, then add them to the library (.LIB) file for the model you requested. For instance, if you specified the Medium Model, the modules would be placed in PULLC20M.LIB. The following settings are required for compiling PULLC: Model size: Small-Huge (Tiny not supported) Default char type: Signed Alignment: Word These options are specified in the MAKEPULL.BAT files as command-line options to TCC. They may be set otherwise in TURBOC.CFG, but the batch file will override those settings. Any other options you wish to use must be in your TURBOC.CFG file. Other settings besides those listed above will be required as well, such as generating underbars. Rather than listing such options, we are leaving this to your common sense as a C programmer. If you have problems with the options you are using, set them to the default options provided with Turbo C. For help on the other options, see the Turbo C User's Guide. Note: After recompiling PULLC, it is also suggested that you recompile the WNDWC source code, using the WNDWC20.H file provided in PC20-SRC.ARC (registered users only). This is only needed for the PULLDEMO found in PDEM-SRC.ARC, but it may be necessary for your application as well. See the WNDWC documentation for details. CONDITIONAL COMPILATION Many times your application may not need to use all of the features built into PULLC. We have included a way to eliminate some of the code and data using macro definitions. Chapter 9, Compiling the Source Code Page 60 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 Here is a complete list of the macros used in PULLC: USE_SUBMENU_CODE - to include submenus. USE_HELPWNDW_CODE - to include help windows. USE_DATAENTRY_CODE - to include data entry or data windows. USE_MSGLINE_CODE - to include normal and error messages. USE_FLOATEMU_CODE - to include floating point and FLOATS/DOUBLES. MULTIWORKWNDWS - to include multi-level work windows in PULLWORK.C. Location - You can find the first five directives defined in the header file PULLC20.H. If you want to change it, you may choose to make a copy of it in another subdirectory so as not to lose its previous contents. The last macro listed, MULTIWORKWNDWS, is in PULLDEMO.H or PULLSHEL.H. Its only use is eliminating multi-level work window code from PULLWORK.C. Definition - When you see the #define statement, it will look like: #define USE_SUBMENU_CODE With the "#define" in place, USE_SUBMENU_CODE would be defined and would include all of the submenu code. If you do not want the code, then undefine it by just changing it to: #undef USE_SUBMENU_CODE Once you have changed all the needed definitions, you must recompile PULLC as well as your program. The code will then be ready for use. Chapter 9, Compiling the Source Code Page 61 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 10. T R O U B L E S H O O T I N G PULLC is a well-thought-out library. If you find that the program has not done what you expected, there may be something that you overlooked. This section will try to help jog your memory as to what the problems may be. GOOF MODULE All programmers make mistakes, right? So, what happens when you try to make more windows than there are indices available? Since WNDWC and PULLC are powerful tools and can even write in RAM, there is a good possibility that your mistake may not even show up on the screen. How do you know if anything has gone wrong? The GOOF module was made especially for handling errors. Displaying Errors - When an error is found in your program, the showgoof function is called and the program is terminated. The CRT will display an error message in a flashing window. There are eight fatal errors that are listed in APPENDIX B in WNDWREF.DOC to identify problems before they happen. Please refer to it for the error messages and their solutions. If you do not have a copy of WNDWC20A.ARC, you can get one direct from the Eagle. Flexibility - You can freely edit the GOOF module without needing to recompile WNDWC or PULLC. You can even edit it for use in your own applications. The error message numbers 1-50 are reserved. So, for your own applications, it is suggested that you start with number 51. If you have thoroughly tested your program, some of the messages can be eliminated. Linking GOOF - It is very important that GOOF be included when linking. When in a project file, list it as GOOFx.OBJ where x is the model size you are using. MAKEPULL.BAT renames GOOF.OBJ to the model size after compiling. POINTER ADDRESSES Because PULLC uses pointers for functions and variables, it is quite possible to lock up your computer if these are not properly addressed. A debugger is most helpful in these circumstances. But if you do not have one, here is a checklist of possible causes: Function Pointer Calls - PULLC makes indirect calls to functions using function pointer variables. The addresses for these functions must be either NULL or assigned to a function. If an invalid address is assigned to these pointers, your computer will probably lock up. Data - The variable address and type of data in each data structure must exactly match or risk possible data loss. String lengths must not be longer than maxfield. Chapter 10, Trouble Shooting Page 62 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 MULTI-TASKING This demo has already been set to work in multi-tasking environments compatible to DESQview, TopView, and IBM 3270 PC Workstation. It uses the multi-tasking video buffer (MTVB) whenever possible. To always disable MTVB use, set prefer_multitask to 0 in PULLSHEL.C. CUSTOMER SERVICE If you are still having problems, leave us a message or give us a call. Chapter 10, Trouble Shooting Page 63 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 A P P E N D I X A : O T H E R P R O D U C T S Eagle Performance Software has developed identical products for both Turbo C and Turbo Pascal. Our pledge is to provide you quality products with unparalleled performance and ease of use. QWIK QWIK - For direct screen video, QWIK is the highest performance screen writing tools available today for all text modes in any video configuration. QWIK provides capabilities far beyond in the unit/library that comes with your compiler. Here are some of the features: . Writes on all IBM compatible computers, displays and adapters including MDA, CGA, EGA, MCGA, VGA, 8514/A, Hercules and 3270 PC. . Superior video detection routine. . Eliminates snow and flicker. . Writes directly to the screen in absolute rather than relative coordinates. . Writes in all text modes and column modes. . Writes on all video pages. . Writes on virtual screens in RAM. . Writes text and attribute, text only, or attribute only. . Reads strings, characters and attributes. . Uses End-Of-String (EOS) marker for quick string chaining. . Provides standardized cursor shapes for all adapters. . Enhanced cursor movement. . Compatible with DESQview and similar multitasking environments. . Over 650% faster than standard direct screen writing. . Only 2.7k bytes of code if all 43 utilities are used. . Writes direct to multi-tasking video buffers (MTVB). . Optimized by the compiler and drops unused code. . Used in all other Eagle products. Here are the product versions: File name CIS name Compiler Release date ----------- ---------- -------- ------------ QWIK42C.ARC QWIK42.ARC TP4(TP5) 12-03-88 QWIK5XB.ARC QWIK5X.ARC TP5(TP4) 03-04-89 QWIKC20D.ARC QWKC20.ARC TC2 03-22-89 WNDW WNDW - For multi-level virtual windows, WNDW is the highest performance window utilities available today. It offers very powerful utilities for full window control and management you probably never thought possible. They are simple and yet very powerful with high speed and tight code. With WNDW, you can choose the absolute writing routines of QWIK, the window- relative writing routines of WNDW, and even customize your own. Here are some of the features you will discover: Appendix A: Other Products Page 64 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 . Uses the powerful direct screen writing routines of QWIK. . Up to 254 fixed or virtual windows can be on the screen at one time. . Extremely high-speed virtual screens in RAM (up to 40 times faster). . Virtual windows are fully updated on screen, even if covered! . Virtual windows have virtual titles. . Fully supported hidden windows saved in RAM. . Fully supports all video pages. . Adjustable-rate moving, resizing, and scrolling. . All windows can be randomly accessed, not just stacked or tiled. . 28 window-relative writing routines. . 15 different border styles with shadow and zoom effects. . Full line drawing routines. . Full cursor mode control for each window. . Writes in all text modes and column modes. . Writes direct to multi-tasking video buffers (MTVB). . Only 13k bytes of code if all 69 utilities are used. . Used in all other Eagle products. Here are the product versions: File name CIS name Compiler Release date ----------- ---------- -------- ------------ WNDW42B.ARC WNDW42.ARC TP4(TP5) 12-03-89 WNDW5XB.ARC WNDW5X.ARC TP5(TP4) 03-22-89 WNDWC20.ARC WNDC20.ARC TC2 02-01-89 PULL Here are the product versions: File name CIS name Compiler Release date ----------- ---------- -------- ------------ PULL42B.ARC PULL42.ARC TP4(TP5) 01-03-89 PULL5XB.ARC PULL5X.ARC TP5(TP4) 03-22-89 PULLC20.ARC PULC20.ARC TC2 04-01-89 ON-LINE SERVICES CompuServe - All updated files and later versions can be found on the CompuServe Borland Forums (GO BPROGA for TP and GO BPROGB for TC) or the IBM Programming Forum (GO IBMPRO). RELEASE DATES Please note that the release dates are only estimates. Appendix A: Other Products Page 65 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 A P P E N D I X B : C R E D I T S Art Hill started some initial ideas on this in Turbo Pascal with PullDown.arc. Art Hill 936 S. Kensington Ave. La Grange, IL 60525 CIS 72307,3570 Copyright (c) 1986-1989 by James H. LeMay for Eagle Performance Software. All Rights Reserved. Protected by the United States Copyright Laws. Conversion to Turbo C by Jordan Gallagher / Wisdom Research Turbo Pascal and Turbo C are trademarks of Borland International. WordStar is a trademark of MicroPro International. Appendix B: Credits Page 66 PULLC Multi-level Pull-Down Menus User's Guide, Version 2.0 A P P E N D I X C : G L O S S A R Y Command letter - A letter highlighted in a menu that executes that line. Command sequence - The sequence of command letters pressed to arrive at a window or menu. Data entry - Field for entering data into the application program in the work windows or user windows. Data window - Window for entering data into the application program from the menus. Error message - A short message for data out of range. Field - A highlighted area reserved for a data entry string to be displayed. Flex field - A field that allows more characters into the data entry than what the field displays. Free field - A field that allows a continuous string of characters to expand in the field in any position. This is in contrast to formatted fields where each column is designated an entry. Local key - A key that only works within the menu or window. Gated exit - A function that lets the flow of execution pass through back to PULLC's key dispatcher only on specific keystrokes. Global key - A key that accesses a different part of the program at any time. Help message - A message appear on the message line for keyboard instructions. Help window - A window displayed by pressing F1 that provides context- sensitive help. Hilited - A highlighted bar pointed at in a menu. Level - A window or menu where the program is operating. Line - A row of text. Link - A menu line showing a symbol (three-bar or dot) that pulls another menu or window. The symbol is also on the same side where it is pulled. Main menu - The first menu pulled from the Top Line menu. Menu - A list of selectable lines. Message line - The bottom row to display key helps or processing status. MTVB - Multi-Tasking Video Buffer used in multi-tasking environments. Pop - removes menu and returns to the previous menu. Pulldown - pulls menus down to the previous level. Selection - A line selected in a menu with a CR. Shell - A bare bones program of pull-down menus to get you started in your own application. Slide-under - The configuration of data windows them to slide under the hilite if the field grows wider. Slide-up - The configuration of submenus that allows them to slide upward as the menu list grows. Status line - A optional line reserved for status information pertinent to your program. Submenu - All subsequent menus pulled after a main menu. Top Line menu - The menu always shown (usually in row 1 or 2). Window - Not a menu. Work window - The window where the bulk of the application is shown. Appendix C: Glossary Page 67