NcDialog API User’s Guide

Table of Contents

Next: , Up: (top)   [Contents][Index]

NcDialog Library API

Copyright © 2005 - 2015
            Mahlon R. Smith, The Software Samurai


This manual describes version 0.0.28 of the NcDialog-class API library.


  Permission is granted to copy, distribute and/or modify this document
  under the terms of the GNU Free Documentation License, Version 1.3
  or any later version published by the Free Software Foundation;
  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
  Texts.  A copy of the license is included in the section entitled 
  "GNU Free Documentation License".





Next: , Previous: , Up: Top   [Contents][Index]

Introduction

┌─────┤ NcDialog API ├─────┐ Hello World! Enter Here └────────────────────────────┘

What NcDialog Is

What NcDialog Does






Next: , Previous: , Up: Top   [Contents][Index]

Operational Overview







Next: , Up: Operational Overview   [Contents][Index]

Description of a Dialog

The Concept of a Dialog

An NcDialog API Dialog




Next: , Previous: , Up: Operational Overview   [Contents][Index]

Application Layer Methods

  1. Startup Code: All NcDialog-based applications have a standard sequence of startup-up activities. This sequence is demonstrated in the ’main’ method of each of the test applications (see Test Applications).
    For more information on the startup sequence, please see NCurses Engine, or see Your First Application, for a step-by-step explanation of the startup sequence.
  2. Define the NcDialog object: An instance of an NcDialog window object has the following characteristics:
    Parameter   Description
    .........   ....................................................
    Size        The number of rows and columns for the window
    Position    The offset of the dialog from terminal window origin
    Color       Color attributes for window border and interior
    Style       Border style
    Title       Optional window title to be displayed in border
    

    Please refer to the discussion of the InitNcDialog class,
    see Your First Application, for more information.

  3. Define and configure the controls the NcDialog window will contain.
    For each control contained in an NcDialog window, there is a control definition which specifies size, position, color, contents and other parameters.

    In addition, many optional configuration methods are provided which can be called after instantiation to further refine each control object for your particular needs. Please see Dialog Control Objects for details on the definition and optional configuration parameters for each control type.

  4. Instantiate and open the dialog window. Once the NcDialog window object and its controls have been defined, open the dialog using the following steps.
    1. Instantiate the dialog and its controls.
    2. Call any optional configuration methods which must be completed before the window is opened for the first time.
    3. Open the dialog window.
    4. Call any additional configuration methods for the controls.
    5. Write any desired static text to the window.
    6. Make the window visible to the user.
    7. Enter the user-interface loop.

    For an example sequence, please refer to the discussion in see Your First Application.
    Please refer to see Dialog Control Objects for details on optional configuration parameters for each control type.

  5. User Interface loop: The basic application layer consists of a simple user-interface loop. This loop organizes user input and decides what to do with that input. It tracks which control object currently has the input focus and calls the appropriate ’Edit’ method for that control type.

    There is exactly one (1) publically-accessible Edit method for each control type:

    Edit Method          Control Class        Control Type
    ...........          .............        ..............
    EditPushbutton       DialogPushbutton     dctPUSHBUTTON
    EditTextbox          DialogTextbox        dctTEXTBOX
    EditBillboard        DialogBillboard      dctBILLBOARD
    EditRadiobutton      DialogRadiobutton    dctRADIOBUTTON
    EditScrollbox        DialogScrollbox      dctSCROLLBOX
    EditDropdown         DialogDropdown       dctDROPDOWN
    EditMenuwin          DialogMenuwin        dctMENUWIN
    EditScrollext        DialogScrollext      dctSCROLLEXT
    EditSpinner          DialogSpinner        dctSPINNER
    
  6. Keyboard and Mouse Input: All, or nearly all user input is handled within the Edit methods listed above. However, there are certain instances where it is useful to obtain information directly from the user. The most common of these would be the nckPause() macro which simply waits for the user to press a key.

    All keyboard and mouse input is channeled through the method
                    GetKeyInput( wkeyCode& wk )
    Please note that for reasons of thread safety, this method is designed in layers. The NcDialog::GetKeyInput() method should be used in nearly all cases because it incorporates not only thread safety, but also translates mouse input into keycodes and dialog coordinates associated with the mouse pointer wherever possible.
    Please see Mouse Configuration for a detailed description of the mouse interface.
    Please see Thread Safety for a more detailed discussion of thread safety.

    The NcWindow::GetKeyInput() method is a thread-safe pass-through to the underlying NCurses::GetKeyInput() method, which IS NOT thread safe, and neither of these layers should be called directly from within an NcDialog application.

  7. Output to the Display: There are three (3) primary methods for writing data to the NcDialog window:
    Output Method              Description
    .............              .....................................
    NcDialog::WriteChar()      Writes a single character
    NcDialog::WriteString()    Writes a text string to current line
    NcDialog::WriteParagraph() Writes a paragraph (with line breaks)
    

    These methods are actually member methods of the parent class, the NcWindow class, and are fully thread-safe, that is: they will not interfere with each other if multiple threads are simultaneously writing to the dialog.

    For a detailed description of how these methods are used, please refer to the section, see NcDialog Display Output.
    For a more detailed discussion of thread safety, please refer to the section, see Thread Safety.

    Note that the NCurses class also has WriteChar() and WriteString() methods for writing directly to the terminal window; however, these methods ARE NOT thread safe, and should not be used when an NcDialog window is open (except possibly for writing debugging messages to the terminal during application development).

  8. Shutdown Code: An orderly shutdown of the NcDialog API is necessary in order to prevent memory leaks, pointers to unallocated data space and other application bête noire. Fortunately, the shutdown sequence is very simple:
    1. delete the NcDialog-class object,
    2. then stop the NCurses Engine.

    Please refer to the section, see NCurses Engine,
    or the section,see Your First Application, for more information.




Next: , Previous: , Up: Operational Overview   [Contents][Index]

Library Internals

This document is intended as a reference for the application designer. As such, the private, support methods which implement the low-level NcDialog API functionality are not discussed in detail here.

However, your author has spent most of his career documenting code, his own code and everyone else’s code; therefore, the NcDialog API library source code is as close to being self-documenting as we can make it. In addition, copious programming notes are included in every source module and in the header of every non-trivial method, so the next poor bastard who updates the code will have an easier time of it.

If, however, you are having trouble with any section of the source code, just remember that your author has a massive ego (as all software designers do), and is happy to discuss his work with anyone who has a serious question.
See Technical Support.




Next: , Previous: , Up: Operational Overview   [Contents][Index]

Inheritance Layers

A Short History Lesson

The C language is powerful, concise, infinitely adaptable, and in the right hands, imbued with the seeds of artistry.

However, C is a terrible language for generational software such as the Linux kernel and the ncurses function library because one designer’s artistry is the next generation’s frustration and misery.

In a past life, this author partnered with a true software genius to start a little company in the Silicon Valley. His code was pristine—it did exactly what he intended it to do, smooth and fast—a true work of art. The problem was his total recall: he never wrote a word of comment because he remembered in great detail every line of code he ever wrote. So when he went off the rails, as genius always does, it became our job as his number two to come along behind, documenting what he had done and porting the code for next-generation projects. Frustration and misery, indeed!

Layering to the Rescue

Just as parents work all their lives to create a stable environment from which their children can launch themselves into the future; exactly so, the strength of C++ is the ability to inherit robust, reliable, self-documenting code from parent classes, building layer-by-layer upon the successes of the past.

The NCurses / NcWindow / NcDialog class progression starts simple and becomes progressively more sophisticated. We make no wild claim, however, that all bugs have been banished. Indeed, sophistication breeds fragility, and each layer requires an order of magnitude greater planning, care and field testing.

  1. The NCurses Layer
    The function of the NCurses class is to define and configure input and output streams, and to interact directly with the underlying ncursesw C-language library.

    The application’s only direct access points to the NCurses class are:

    1. the start-up sequence
    2. the defined constants for keycodes and color attributes
    3. the shut-down sequence

    The application needs NO direct contact with the underlying C library, and in fact, doing so would very likely trash the application’s working environment. DON’T DO IT!

    Please study the ’Dialogw’ test application, tests 1 and 2 for a demonstration of the NCurses class methods.
    See Dialogw App.

    For a detailed explanation of all public methods and data of the NCurses class, please see NCurses Engine.

  2. The NcWindow Layer
    The true value of the ncurses(w) C library is its definition of a text-mode "window." This ’window’ is a block of memory which contains the text and color attribute data, along with positioning and configuration information representing a section of the terminal’s display area. It’s primitive C-style naming conventions, and ad-hoc design are rather difficult to work with, but it is also quite a powerful construct.

    However, in order to harness this power for non-trivial projects, it is necessary to encapsulate the ncurses(w) window primitives, simplifying its application interface and compensating for its weaknesses.

    The NcWindow class leverages the low-level power of the NCurses class for input and output while creating a cushion between the application programmer and the difficult process of creating and maintaining window objects. In short, the NcWindow class converts the ncurses(w) ’window’ construct from a sequence of C function calls into a true window ’object’.

    Simultaneously, the NcWindow class creates a thread-safe application environment upon which its derived classes can rely.

    Like the NCurses class, the NcWindow class makes no attempt to encapsulate the entire ncurses(w) windowing interface. Instead, it uses the absolute minimum number of ncurses(w) primitive functions necessary to get the job done. This is not a matter of laziness (or of arrogance), but is an exercise in defensive programming: we want our API to be robust and reliable, and much of the ncurses(w) C library is neither.

    Please study the ’Dialog1’ test application, tests 3 and 4, and the ’Dialogw’ test application, test 3 for a demonstration of the NcWindow class methods:
    See Dialog1 App.
    See Dialogw App.

    For a detailed explanation of all public methods of the NcWindow class, please see NcWindow Meta-layer.

  3. The NcDialog Layer

    While it is possible to write a decently-robust application using the NcWindow class directly, the NcWindow class lacks any kind of sophisticated user interface or visual polish.

    The NcDialog class is a ’derived class’ whose parent is the NcWindow class. Essentially everything inside the NcDialog class is a window object or a group of window objects. The NcDialog layer makes no direct calls to the ncurses(w) C library, and is thus free from the problems that arise when changes are made to underlying third-party libraries. See Description of a Dialog, for more information on dialog objects.

    The NcDialog class is the application programmer’s working environment.

    • The application knows nothing at all about the ncurses(w) C library.
    • The application knows nothing about the NcWindow class.
    • Apart from the start-up and shut-down sequences, and the constant definitions (keycodes and color attributes), the application knows nothing about the NCurses class.

    Of course a good programmer can find ways to violate this layering of functionality, but a great programmer will not do it.

  4. The Dialog Control-Object Layer
    Within a dialog object, interaction with the user is the primary focus. To interface smoothly with the user, several dialog control objects are defined. Each control object has a specific interface function so the application programmer does not have to create all new interface functionality.

    Each control object is itself a dialog object which is in turn, created from one or more NcWindow objects. For instance, the DialogMenuwin class is actually a linked set of three(3) NcWindow objects: a title, a border and a scrolling display area which constitute a collapsible menu object. See Application Layer Methods, for an overview of the user interface controls.

    For a detailed explanation of all public methods, data and control objects of the NcDialog class, please see NcDialog Class Definition.




Next: , Previous: , Up: Operational Overview   [Contents][Index]

Thread Safety

Introduction

In any non-trivial application, thread safety can become an important issue. In the world of multi-core CPUs, built-in language support for multi-threading, and an application programmer’s burning need for speed, multi-threading is a natural choice.

Multiple threads may be launched from any active process, up to the limits of system resources. This means that threads using shared code and resources must play nicely with each other to avoid all kinds of potentially nasty consequences.

The underlying ’ncursesw’ library upon which the NcDialog class relies, was designed to run under single-threaded processes. This is understandable, since at the time, the concept of multi-threading was still in its infancy. The ’ncursesw’ library is therefore unsuited and inherently unsafe for multi-threaded environments—but fear not!

Implementation

The NcDialog class library manages the input and output resources so that application threads need not worry about I/O resource conflicts.

The actual thread-safety mechanism is implemented in the NcWindow class
(see NcWindow Meta-layer,) and this mechanism carries through to all of the NcWindow-class descendants, including the NcDialog class as well as the individual user interface control objects with which an NcDialog window may be populated. (See NcDialog Class Definition.)

The implementation is conceptually simple. There are two data streams, the keyboard/mouse data input stream and the display data output stream. Any executing thread within the application may request exclusive access to to one or both of these streams simply by calling the needed input or output method(s). There is no need for application-level thread management of I/O resources; simply launch the threads to do their assigned tasks, and terminate them when they are no longer needed.

Thread-safe Operation

  1. As mentioned, the ’ncurses’ and ’ncursesw’ libraries included as part of Linux/UNIX systems ARE NOT thread safe.
  2. The NCurses class methods (see NCurses Engine,) provide direct access to the ’ncursesw’ input and output streams, and are therefore also not thread safe.
  3. The NcWindow class (and its derived classes) implement thread safety through two exclusive-access-lock <recursive_mutex> objects:
    1. ’InputStream_Lock’ for keyboard/mouse input, and
    2. ’OutputStream_Lock’ for data output to the display.

    These mutexes are not data members of the NcWindow class. They are a single, static, shared resource for all NcWindow-derived objects.

    All input/output member methods, as well as I/O configuration methods must obtain exclusive access to the target stream before each operation. In practice this means that after requesting a currently-unavailable resource, the requesting thread will sleep until the resource becomes available, thus ensuring that resource conflicts will not occur.

  4. Keyboard/Mouse Input: The ’ncursesw’ library primitives which comprise the low-level keyboard/mouse interface are implemented as a single resource shared among all window objects. It operates as a FIFO queue, and for this reason, input is naturally robust, though still potentially vulnerable to resource conflicts, especially during application start-up and configuration.
    1. It is recommended that all application start-up tasks be completed by the primary thread before secondary threads are launched.
    2. Similarly, when temporarily putting the NCurses Engine to sleep (’Hibernate’ method) or re-awakening it (’Wake’ method), it is strongly recommended that all other program threads should be either ’join’ed (terminated), OR blocked. It would be quite embarrassing for a thread to run amok, calling NCurses/NcDialog methods which are not currently available.
  5. Cursor positioning: Because the terminal window in which the application operates (GNOME, Konsole, etc.) is a single display area, the cursor (output insertion point) is is also a single resource, shared among all objects within that terminal window, so at no time, should a thread rely upon the cursor being where it was previously positioned. Another thread may have moved it. This is why all methods which write to the screen are designed to EXPLICITlY set the cursor position at the start of the output sequence.
    1. Note that if one thread makes the cursor visible, then the user may be surprised to see the cursor jumping around as other threads write data to the display. There is no elegant way to prevent this without inter-thread communications regarding what each thread is doing with the cursor, so, it is recommended that the cursor remain invisible as much as possible in order to reduce user confusion.
    2. Under extreme duress, multi-threaded output will occasionally show screen artifacts - this is unavoidable due to inherent weaknesses in the underlying ’ncursesw’ library, as well as hardware/kernel task switching issues. However, in a ’typical’ application, where events move at a user-oriented pace, such artifacts will be rare. For an over-the-top stress test of multi-threaded output, please refer to test seven (7) of the Dialog4, test application. See Dialog4.
  6. Please note that multi-threading IS NOT USED within the NcWindow class nor any of its descendants, and thus, the ’lpthread’ library need not be linked to the target application unless the application itself uses multi-threading.
  7. For convenience, the thread safety functionality is controlled during the library build by the NCD_THREAD_SAFETY flag in NcWindow.hpp, so the mutexes can be temporarily disabled during testing; however, this flag should be enabled for all production builds, even if the target system does not support multi-threading.



Previous: , Up: Operational Overview   [Contents][Index]

Newbie Corner

Introduction

Software Sam may be a caffine-swilling, hard-core, old-school assembly-language programmer, but he is also a classroom teacher, so he understands that for a programmer who is just starting out on his or her career, even a very small misunderstanding over terminology or some minor technical issue which needs to be researched can stall a project for hours—or even weeks. For this reason, we offer some simple, introductory information for a few of the problems which beginning programmers and those new to GNU/Linux are likely to encounter.





Next: , Up: Newbie Corner   [Contents][Index]

Vocabulary

Here are a few useful definitions for terms that students often find confusing.





Next: , Previous: , Up: Newbie Corner   [Contents][Index]

What Is a Class?

Why do I need to have Class?

In the early days of computing, a computer program was a sequence of instructions which performed a sequence of tasks in a particular order. This style is sometimes referred to as ’structured programming’ because the structure of a program was task oriented, like a Rube Goldberg Machine:

a) the marble rolls down the incline, hitting the bottle
b) the bottle falls over, causing the water to spill
c) the water drains into the cup, making it heavy
d) the heavy cup pushes down the lever which strikes a bell

While such a device is a fantastic toy, it is a terrible model on which to build a software program because every time you need to perform a new task, you need to create a complex new sequence of sub-tasks to support it. That’s why all modern programmers need to have class.

Object Oriented Programming

Work in the real world is based on using tools which are designed for a specific purpose. You drive a nail with with a hammer, not a corkscrew; and unless you are a drunken engineering student, you open your bottle of wine with a corkscrew, not a hammer.

Each tool has been tested under real-world conditions and has proven itself to be reliable. The tool is able to do its job independently, without depending on help from any other tool.

Each tool is reusable for a variety of related tasks; thus, if you need to perform a given task, all you need to do is select the right tool for the job.

In the world of modern programming, we emulate the real world. We use existing, well-behaved tools when available; we modify existing tools when necessary to meet our current needs; and create new tools to meet new challenges. In C++, Java and other object-oriented languages, these tools are called ’classes’.

Now that I have Class, what do I do with it?

A ’class’ may be thought of as a specific set of data (variables) AND the code needed to access and modify that data. This is a huge step forward in programming because it protects your data from outside influences (forces of evil?) which might otherwise randomly trash your application without your being able to trace the source of the problem.

Working in C++ means leveraging class definitions to handle most of the details in an execution sequence. Not only does this allow for robust and modular code which can be easily modified or re-used, it ALSO means that the code is human-readable i.e. maintainable.
These classes fall into four categories:

  1. Primary classes
    These classes define major operations and control the program flow.
    The Primary classes of the NcDialog API are the NCurses class, the NcWindow class and the NcDialog class.
  2. Derived classes
    These are classes based on one of the Primary classes, but modified to accomodate special cases of the task the Primary class was created to perform. For instance, the NcDialog class is actually ’derived’ from the NcWindow class. That is, the basic, reliable and well-behaved functionality of the NcWindow class is leveraged to create a special case of the parent class. Specifically, the primitive ’window’ object of the NcWindow class is enhanced to create the ’dialog’ object of the NcDialog class.

    Some additional examples of Derived classes in the NcDialog API are the user interface control objects, which are themselves based on the NcWindow and/or the NcDialog classes, to perform specific types of user interaction.

  3. Secondary classes
    These classes are needed to pass information between the application code and the Primary class methods or internally among methods. A few of the often used Secondary classes of the NcDialog API are:
    see winPos class
    see wkeyCode class
    see InitCtrl class
    see InitNcDialog class
    see uiInfo class
    All of these classes are used to transport data between the application layer and the NcDialog API methods.
  4. Support classes
    This group of classes is used internally by the Primary, Derived and Secondary classes. Because they are not used directly by the application code, these classes are not discussed in this document. Support classes are defined and described in the the associated header files: NCurses.hpp, NcWindow.hpp and NcDialog.hpp.

Don’t worry, be happy!

In summary, don’t let your professors or or other programmers frighten you with technical jargon about objects, classes, inheritance, overloading and other such nonsense. The jargon is not the thing itself, so just get familiar with the tools by studying example code and through personal experimentation. In a very short time, you will start to wonder how anyone ever wrote software before there were classes.





Next: , Previous: , Up: Newbie Corner   [Contents][Index]

Reading Texinfo documentation

Texinfo is the official documentation engine for the entire GNU project, which includes GNU/Linux and all of its component applications. The Texinfo engine is not very sophisticated, but it is surprisingly powerful.

On the command line, type the ’info’ command to view the main page of the info document tree. Use the arrow keys, PgUp, PgDn, Home, End to navigate through the page. With the cursor on a line which contains the ’*’ (hyperlink) character, press the ENTER (RET) key to access the specified node on the tree.

The ’info’ reader has a large number of options which can be seen by pressing the ’h’ key for Help. (Press the ’x’ key to close the Help window.)

The ’n’ and ’p’ keys move to the Next and Previous chapters respectively, and the ’u’ (up) key takes you to the parent node of the current position in the info tree.

Probably the most useful command is the ’s’ (search) key or the equivalent ’/’ (forward slash) key which invokes a search. After pressing ’s’, type in a string for which to search and press ENTER (RET). To search for the same string again, either press the ’}’ key (search forward) or ’{’ key (search backward), or just press ’/’ and then ENTER (RET).
Case sensitivity: Whether the search will be case sensitive depends on the search string. If you enter all lower-case characters, then the search is not case sensitive; however, if you enter mixed upper-case and lower-case, then the search is case sensitive. (Who thinks this stuff up? :)
The search tool is not perfect, and you may end up in a document which is completely unrelated to your needs, but it is still a useful tool.

The 'l' key move to the ’last’ (most-recently-visited) chapter, so if you find that your search has gone astray, you can walk backward through the list of visited pages.

Press the ’q’ (quit) key to exit the info reader.


There are two(2) general ways to access the documentation for a specific application package.





Next: , Previous: , Up: Newbie Corner   [Contents][Index]

Configuring Linux

GNU/Linux Installation and Configuration

The GNU/Linux distribution packages available for download or purchase are generally robust and easy to install if you accept all the default options. However, we have never met a Linux warrior who could accept all of these default options. These defaults were dreamed up by some tool in the Marketing department, based on their idea of an imaginary lowest-common-denominator user.

We all need more than that, especially those of us who write software for fun and profit. Using the NcDialog API requires very few non-default Linux installation and configuration options, but be sure that your development system is properly set up for the following tools.

  1. GNU G++ compiler, headers and libraries
    The compiler may be properly installed by default on your system because it is needed for a number of other installation and update tasks.

    However, it is important that your compiler is up-to-date and has the correct paths set for all components, including the linker.

    When you invoke the compiler, at the command line, a great number of complex activities are performed, and if even one of them goes wrong, the compile will not be successful.

    Setting up the compiler is beyond the scope of this simple document, but you can find additional information here:

    • Read the official documentation:
         info gcc
    • See the Free Software Foundation website’s installation pages:
         https://gcc.gnu.org/install/
    • Building the compiler from source:
         https://gcc.gnu.org/install/configure.html
    • Reference the compiler’s help:
         g++ --help | less

    Also, please see Building the API Library for additional information on updating and using the G++ compiler.



  2. ncurses (ncursesw) development system
    It is quite possible that the ’ncurses’ development system including needed headers and the ’ncursesw’ link library will not be installed by default on your system.

    Please see Libraries for ncurses for details on installing the ncurses development system.



  3. Texinfo documentation (makeinfo) development system

    Documentation written for GNU/Linux, its utilities and applications is written using the Texinfo documentation system. Source documents are initially written as plain text files, similar to HTML source documents, but with Texinfo formatting commands. Source documents are identified by the ’.texinfo’ filename extension (often shortened to just ’.texi’).

    Documentation is produced using the Texinfo ’makeinfo’ utility. ’makeinfo’ operates on the Texinfo source files to produce output in a variety of file formats. While the NcDialog API documentation is generated in ’.info’ and ’.html’ output formats only, several other formats are available through ’makeinfo’ options.

    The Texinfo (makeinfo) utility is often not installed by default, so if you want to create or modify documentation, you will need to install it. Please see Updating Texinfo Docs for additional information.

    Using the Texinfo documentation engine is too big a subject to cover here, but moderately good, and sometimes excellent information is readily available to help you get started with Texinfo (makeinfo).

    • The Texinfo documentation (if installed on your system):
         info texinfo
         info makeinfo
    • The GNU Texinfo official page:
         http://www.gnu.org/software/texinfo/

      Shameless plug: The GNU Texinfo page also contains a link to Software Sam’s contribution to the Texinfo project.

    • The Texinfo users’ mailing list:
         help-texinfo@gnu.org




Next: , Previous: , Up: Newbie Corner   [Contents][Index]

Selected command-line utilities

The Linux Terminal and the Command Line

While it is not our intention to frighten those who are new to Linux, be aware that it will take several years and many tears to fully master the system command line, and most of us never do fully understand the hundreds of available commands.

Fortunately, a very modest general knowledge of command-line utilities is sufficient to produce highest-quality applications based on the NcDialog API.

What follows is a short list of command-line utilities you will probably find useful, and very simple descriptions of how to use them.

Full information on each of these utilities is available, through the ’info’ documentation system. Note that some command-line utilities have the same name as equivalent C-language functions. If for instance, you type  'info mkdir',  you will get the ’mkdir’ C function. If this happens for a command you are researching, invoke the info system like this:  'info coreutils mkdir to access information on the command-line utility.






We don’t provide direct hyperlinks to the documents referenced in the above list because this document may not be integrated into the ’info’ document tree, and would therefore cause broken links.

Please see Development Tools for additional information.

Also, there are numerous tutorial websites that explain how to use Linux command-line utilities and the dreaded ’regular expressions’ syntax. We like Dr. Bob’s Lowfat Linux (http://lowfatlinux.com), but please search the online world or your nearest bookstore to find a tutorial that is right for you.

If you are interested in Alvin Alexander’s Teleport (’tp’) utility, please visit
http://alvinalexander.com/linux/linux-teleport-command-cd-improved





Previous: , Up: Newbie Corner   [Contents][Index]

Friendly Advice for New Programmers

Starting to Design an Application

Classroom instruction in programming is much like playing with a pile of Leggos. It introduces you to the concepts of writing software and produces some cute output, but it does nothing to prepare you for the realities of staring at a blank screen hoping for an idea on how to begin. Here is how to write an application: WRITE THE COMMENTS FIRST!

This is not optional. Human beings do not think in C, C++ or Java. We think in the language our mother taught us. Use it!

First, answer a couple of questions:

1) Who am I doing this for?
2) What do I want the application to do?
3) How much time and energy am I willing to invest in this project?

Answer each question with a short paragraph. Then open a blank source document and expand your answer to question #2, answering this:

Question 2a) How do I get the application to do this?

Here is an example of how to start an application. Your ideas will begin to flow more smoothly and the goals will become clearer for each comment line that you write.

// read the user's arguments

// has user asked for something stupid?
   // if yes, call user a dumb guy and exit

// perform the startup sequence

// query the system's capabilities

// query the user for any additional information needed

// grant the user's request

// report the results

// ask if user needs anything else
   // if yes, then loop

// perform the shuddown sequence and exit

Keep inserting comments, layer upon detailed layer. At some point, you will be tempted to start inserting bits of actual code into your comments. Resist this temptation for as long as possible because organized thought leads to organized code. Divide the functionality into sections and create one or more source modules for each section.

Question 2b) How do the sections interact with each other?

Different sections of the code should interact with each other in a very controlled and specific way. The reason for this is that a change in one section should never cause another section to break. This is the essense of object-oriented design.

Question 2c) Which section should I do first, second and third?

If you need to do research on some aspect of the project, it is usually best to start there. The actual process of doing the research will lead to a better understanding of the work required.

Second, do error checking on the input and error reporting. This will help you to focus on what information the application needs and how to obtain and verify it.

Next, experiment a little bit. There is no need to have finished code at this point, just make something happen. Make comments about what happens, and what fails to happen as expected. This builds confidence and provides a clearer picture of the overall project.

Clean up your mess, and then begin the actual development of the application. Procrastination about cleaning up the comments and experimental code inevitably leads to product delays, ridicule from your peers, and eventually failure.

Question 2d) Is the user interface as intuitive as I think it is?

Just because things make sense to you, doesn’t mean that anyone else will see it that way. Invest your money wisely: buy your roommate a pizza, and get him or her to test the thing for you. An enthusiastic and knowledgeable beta-tester is a treasure beyond golden-crust pizza, so put your massive, fracking ego on hold for five minutes:

   DO NOT call your tester an idiot!
   DO NOT interrupt,
   DO NOT make rude noises, and 
   DO NOT try to explain your design.
   DO LISTEN to what your tester has to say, and take careful notes.

Why Do I Need To Write All These Comments?

Code tells the story, so why do I need to write so many comments?
We have heard this from everyone from brilliant students, to frustrated burn-out cases, to grizzled, clear-eyed code warriors.

It is true that really great code is always and naturally self-documenting code. And it is true that over time we develop an ability to read code almost as well as we read human language. The problem is that the code only tells WHAT is being done. We need comments to communicate the thought process behind the code: WHY it is being done in this particular way, and WHY it is not being done in some other, more obvious way.

Source code has a lot in common with a good novel. It is created by an author with a burning need to tell his or her story in the hope that someone will read it and fully understand the passion and the pain that went into creating it. Source code is two intelligent human beings, the designer and the maintainer, having a conversation with each other. More often than not, the designer and the maintainer are the same person, separated by time and experience—so do your future self a favor.

A couple of examples:

Here is a simple, but typical comment written to explain why we made a seemingly redundant and unnecessary system call inside a loop. We learned the original lesson the hard way, and didn’t want to re-learn it in a future revision, NOR did we want to force someone who borrowed from the FileMangler code to go through the same pain we did.

// Programmer's Note: We could get the file type from 
// dirstat.d_type, (type DT_DIR=4) except that not all 
// filesystems have this field; thus, we ALSO lstat the target.

Here is a straightforward note to future generations about initializing the members of a date/time structure:

// Programmer's Note: If we are beyond the dreaded year 2038 
// overflow point, the assignment to member time_t sysepoch 
// may be truncated here.

If it was important today, it will still be important twenty years from today. Write it down!


Style Counts

Programmers are highly-individualized personalities. Each of us has a unique style and approach to problem solving. Be sure that your vision and the style you see in your head translate accurately into the source code itself. Whatever your style may be, and however your style develops over time, be consistent, be clear and be honest with yourself about your strengths and limitations.

There is a worthwhile book by Herb Sutter and Andrei Alexandrescu:
C++ Coding Standards - 101 Rules, Guidelines and Best Practices.
This is the best book of its kind, filled with no-nonsense guidelines for producing successful software. If you remember nothing else from the book, remember this:

   "Correct is better than fast. Simple is better than complex.  
    Clear is better than cute. Safe is better than insecure."    

                                      Sutter and Alexandrescu

Others Will Follow, Make It Easy For Them

The code you write today, is just the first step in a legacy that will last for years, or decades to come. You are the first contributor to the code, but you will not be the last (nor even the best).

Version 1.0 may seem like a distant dream today, but the person who writes Version 7.0, whether it is your future self, or some kid who hasn’t been born yet, will appreciate the effort you have put into making the base code easy to read and and yet robust and flexible enough to endure through generations.

The efforts of the the entire "free-as-in-freedom" software movement are based on the idea of generational software. Will the kid who takes over the project in the the year 2035, worship and admire you, or will she think you were a worthless douche-bag who should never have been allowed near a computer? Today, your legacy as a software designer is in your own hands; tomorrow is too late. Plan for the future!


Documentation Is Just As Important As Code

Users really want to trust that your application does what they need for it to do. Even more importantly, they want to trust that your application does what you say it will do.

It really doesn’t matter how good your application is if potential users don’t know what it does or can’t figure out how to use it. The quality of your application must be reflected in the quality of the documentation.

Maybe you are comfortable expressing yourself in human speech, and maybe you are only comfortable talking to your computer. Either way, do your very best to explain in writing what your application does and how it does it. If necessary, hire a technical writer to help you reach the necessary level of clarity.

Tell the truth. Marketing department goons are the Organized Crime of the corporate world. They are willing do anything and say anything to get a new customer. This is fine for trade show brochures, but is a huge mistake for technical documentation.

Don’t lie — and don’t exaggerate. Tell the truth and speak it clearly. Your customers will be loyal to you for a lifetime.



Next: , Previous: , Up: Top   [Contents][Index]

Your First Application

The structure of an NcDialog application is a simple and easy-to-understand, even for newbie programmers, and only basic tools are needed to create sophisticated applications.





Next: , Up: Your First Application   [Contents][Index]

Development Tools

Developing terminal (text-based) applications is not like working in a GUI environment. It is not necessary to learn how to use a "development environment" in order to get useful work done. In fact, working in an "environment" feels to us like having to drag around a bag of rocks wherever we go. For a lovely visual of what it feels like to work within a development environment, we recommend the opening scenes of the film, "Johnny English—Reborn."

To create a complete and sophisticated NcDialog application, all you will need is a simple text editor, a terminal program and the compiler.

Text Editor

If you are an experienced programmer, you probably already have a favorite text editor, one with which you feel comfortable. If you are new to bit-herding, we do recommend that you select a text editor that has direct support for C++ formatting, i.e. a "programmer’s text editor," but the most important qualities for any tool are that it doesn’t get in the way of your productivity and that it feels right for you.

Terminal Program

We assume that you are at least generally familiar with how to use a terminal program, and that you understand the basic command-line utilities; but even if you are not familiar with 'ls', 'grep', 'chmod' and a few of the other basic commands, don’t worry. You WILL succeed.

The default terminal program for most GNU/Linux installations is either the GNOME Terminal or the KDE Konsole. Both of these are excellent choices for command-line work. Konsole is a bit more configurable (as are all things KDE), but for our purposes there is little difference between them. If you already have a favorite terminal program, you are definitely nerd enough to succeed at any task.

GCC (g++) Compiler

The use of an up-to-date compiler and its associated header files and libraries are critical to any software project. In our not-too-humble opinion the GNU GCC compiler collection is the finest piece of software ever written for public use.

The NcDialog API relies on full compiler support for the GNU++11 (C++11) standard. According to the GCC compiler’s own documentation, full support for C++11 was achieved in GCC version 4.8.1. However, several earlier versions actually had full (if poorly documented) support for C++11 functionality.

(If you are a member of that annoyingly enthusiastic group who uses LLVM/Clang as your C++ compiler, be sure that you are using version 3.3 or higher.)

Additional Tools

'make':
-------

The ’make’ utility is an integral part of software development, and you should know the basics of writing a ’Makefile’; however, the NcDialog package provides several Makefiles which you can copy and use with very little modification to build your own projects. Note that we refer to the ’make’ utility as ’gmake’ throughout this document. The reason is that we grew up using UNIX make and that worthless steaming pile of cow dung known as Microsoft make(tm), and we want to distinguish between those and GNU ’make’ which actually performs as documented.

The good news is that all you need to know for now is that you type the word ’gmake’ (or ’make’) and press ENTER.


'grep':
-------

If you are like us, you probably use ’grep’ at least three times before you’ve even had your morning coffee. This is especially true if you are working on a large project. For instance, the NcDialog project, including the test applications, is now over 45,000 lines of code.

Here’s a quick example. If you want to find the debugging method, Dump_uiInfo() in the NcDialog source code, type:
  grep -n ’NcDialog::Dump_uiInfo’ *.cpp
This will yield your answer:
  NcdControl.cpp:904:void NcDialog::Dump_uiInfo(winPos wPos, uiInfo info)




Next: , Previous: , Up: Your First Application   [Contents][Index]

Application Framework

IMPORTANT NOTE: If you have not installed the ’ncursesw’ development package, then do it now (see Libraries for ncurses).

IMPORTANT NOTE: If you have not yet compiled the ’NcDialog.a’ link library, then do it now (see Building the API Library).

Create a Directory for Your New Application

Your new project deserves a new, empty subdirectory in which to live.
If you are new to programming, new to C++ or new to Linux, please go to Newbie Corner for general information about finding your way through the wonderful world of GNU/Linux programming.
If you’re not a programming virgin, then feel free to move ahead.



Add the Start-up and Shut-down Sequences

Although the start-up sequence can include several additional configuration options, this is just our first try, so we will keep it simple.

Modify main() and and the ’MyDialog’ method to look like this:

int main ( int argc, char* argv[], char* argenv[] )
{
   // Start-up sequence
   if( (nc.StartNCursesEngine ()) == OK )
   {
      nc.ClearScreen () ;           // clear the terminal window

      MyDialog () ;

      // Shut-down sequence
      nc.StopNCursesEngine () ;     // Deactivate the NCurses engine
   }
   else
   {
      wcout << "Unable to start the NCurses Engine!" << endl ;
   }
   exit ( 0 ) ;
}

void MyDialog ( void )
{
   nc.WriteString ( 0, 0, "  Hello World!  ", nc.grR ) ;
   nc.RefreshScreen () ;   // make our output visible
   nckPause();             // wait for a key press
}

Try another compile. Your application should build without errors and without warnings. However, this time your application can show a bit of life, so after a successful build, invoke your application, admire it, invite your friends to admire it — and then press any key to exit.

gmake

./FirstProg

Congratulations!

With 22 lines of code, you have mastered almost everything you need to know about using ’ncurses’. (It really is that easy.)

The next chapter will show you how to create your first NcDialog-based application. See Create a Dialog Window.




Next: , Previous: , Up: Your First Application   [Contents][Index]

Create a Dialog Window

Now that you have created your application template (see Application Framework) you can create a useful application based on a dialog window. Then in the next chapter, see Create Control Objects, we will add control objects.

Define What Your Dialog Will Look Like

Discard the current ’MyDialog’ method.

Insert the following ’MyDialog’ method.
Yes, this is a big step, but each item will be carefully explained.

void MyDialog ( void )
{
   const short dialogROWS = 24 ;    // display lines
   const short dialogCOLS = 80 ;    // display columns
   
   attr_t dColor = nc.blR,          // dialog background color
          bColor = nc.brbl,         // dialog border color
          mColor = nc.brbl ;        // dialog message color
   short ulY = 2,                   // upper left corner in Y
         ulX = 2 ;                  // upper left corner in X
   const char* 
     msg = "      Hello World!       \n\nPress any key to exit... " ;

   //* Initial parameters for dialog window *
   InitNcDialog dInit( 
      dialogROWS,     // number of display lines
      dialogCOLS,     // number of display columns
      ulY,            // Y offset from upper-left of terminal 
      ulX,            // X offset from upper-left of terminal 
      "  NcDialog - Our First Effort  ", // dialog title
      ncltDUAL,       // border line-style
      bColor,         // border color attribute
      dColor,         // interior color attribute
      NULL            // pointer to list of control definitions
      ) ;
   
   //* Instantiate the dialog window *
   NcDialog* dp = new NcDialog ( dInit ) ;
   
   //* Open the dialog window *
   if ( (dp->OpenWindow()) == OK )
   {
      dp->WriteParagraph ( 5, 28, msg, mColor ) ;
      dp->RefreshWin () ;           // make everything visible
      nckPause();                   // wait for a keypress
   }
   else
   {  // The most likely cause of error is 
      // that the terminal window is too small.
      nc.WriteString ( ulY, ulX, 
                       "Unable to open dialog window!", nc.re, true ) ;
      nckPause();    // wait for a keypress
   }

   if ( dp != NULL )                // close the window
      delete ( dp ) ;
}

A Closer Look At Our Dialog

Re-compile and run your application again.

gmake

./FirstProg

Congratulations!

You have successfully created your first dialog window. Yes, we admit that our dialog still doesn’t do anything useful, but we are getting close — and with only 54 lines of code.

The next chapter will show you how to design and position some control objects which we will use to interact with the user.
See Create Control Objects.




Next: , Previous: , Up: Your First Application   [Contents][Index]

Create Control Objects

We now have a simple dialog object (see Create a Dialog Window), and we are ready to create some control objects.

We will define a list of four (4) control objects, 1 Pushbutton, 2 Radio buttons and 1 Textbox. Then in the next chapter will provide the user with access to these controls. See Simple Interface Loop.

Give names to our controls

Although we could use magic numbers to refer to our various controls, good programming practice dictates that we should name them in a consistent and meaningful way.

Copy this name list into the ’MyDialog’ method just above the InitNcDialog initialization.

enum mdControls : short
{
   DonePB = ZERO, // PushButton (item ZERO)
   lfTB,          // TextBox
   grRB,          // Radio Button for green messages
   yeRB,          // Radio Button for yellow messages
   mdCONTROLS     // number of controls defined
} ;

Define the list of control objects

Control objects are defined by creating an initialized array of ’InitCtrl’ class objects.

The ’InitCtrl’ class is fairly complex, but for now, we will just use it, and later we will come to understand it. A detailed description of the ’InitCtrl’ class can be found at See InitCtrl class.

Copy the following array into the ’MyDialog’ method just below our name list.

InitCtrl ic[mdCONTROLS]  // array of dialog control initialization objects
{
   {  //* 'DONE' Pushbutton  - - DonePB *
      dctPUSHBUTTON,             // control type
      rbtTYPES,                  // rbSubtype: (n/a)
      false,                     // rbSelect:  (n/a)
      short(dialogROWS - 3),     // ulY:       upper-left Y
      short(dialogCOLS / 2 - 4), // ulX:       upper-left X
      1,                         // lines:     height
      8,                         // cols:      width
      "  ^DONE  ",               // dispText:  
      nc.gyR,                    // nColor:    non-focus color
      nc.reG,                    // fColor:    focus color
      tbPrint,                   // filter:    (n/a)
      NULL,                      // label:     (n/a)
      ZERO,                      // labY:      (n/a)
      ZERO,                      // labX       (n/a)
      ddBoxTYPES,                // exType:    (n/a)
      1,                         // scrItems:  (n/a)
      1,                         // scrSel:    (n/a)
      NULL,                      // scrColor:  (n/a)
      NULL,                      // spinData:  (n/a)
      true,                      // active:    can receive focus
      &ic[lfTB]                  // nextCtrl:  next control
   },
   {  //* 'Linux Flavor' Textbox  - - lfTB *
      dctTEXTBOX,               // control type
      rbtTYPES,                 // rbSubtype: (n/a)
      false,                    // rbSelect:  (n/a)
      7,                        // ulY:       upper-left Y
      26,                       // ulX:       upper-left X
      1,                        // lines:     height
      26,                       // cols:      width
      NULL,                     // dispText:
      nc.bw,                    // nColor:    non-focus color
      nc.grR,                   // fColor:    focus color
      tbPrint,                  // filter:    any printing character
      "Favorite Linux flavor:", // label:     
      ZERO,                     // labY:      
      -23,                      // labX       
      ddBoxTYPES,               // exType:    (n/a)
      1,                        // scrItems:  (n/a)
      1,                        // scrSel:    (n/a)
      NULL,                     // scrColor:  (n/a)
      NULL,                     // spinData:  (n/a)
      true,                     // active:    can receive focus
      &ic[grRB]                 // nextCtrl:  next control
   },
   {  //* 'Green Message' Radio Button  - - grRB *
      dctRADIOBUTTON,           // type:      
      rbtS3s,                   // rbSubtype: standard3
      false,                    // rbSelect:  dflt sel
      short(ic[lfTB].ulY + 2),  // ulY:       upper-left Y
      ic[lfTB].ulX,             // ulX:       upper-left X
      1,                        // lines:     (n/a)
      0,                        // cols:      (n/a)
      NULL,                     // dispText:  (n/a)
      nc.bw,                    // nColor:    non-focus color
      nc.grR,                   // fColor:    focus color
      tbPrint,                  // filter:    (n/a)
      "Green Messages:",        // label:
      ZERO,                     // labY:      
      -16,                      // labX       
      ddBoxTYPES,               // exType:    (n/a)
      1,                        // scrItems:  (n/a)
      1,                        // scrSel:    (n/a)
      NULL,                     // scrColor:  (n/a)
      NULL,                     // spinData:  (n/a)
      true,                     // active:    can receive focus
      &ic[yeRB]                 // nextCtrl:  next control
   },
   {  //* 'Yellow Message' Radio Button  - - yeRB *
      dctRADIOBUTTON,           // type:      
      rbtS3s,                   // rbSubtype: standard3
      true,                     // rbSelect:  dflt sel
      short(ic[grRB].ulY + 1),  // ulY:       upper-left Y
      ic[grRB].ulX,             // ulX:       upper-left X
      1,                        // lines:     (n/a)
      0,                        // cols:      (n/a)
      NULL,                     // dispText:  (n/a)
      nc.bw,                    // nColor:    non-focus color
      nc.grR,                   // fColor:    focus color
      tbPrint,                  // filter:    (n/a)
      "Yellow Messages:",       // label:
      ZERO,                     // labY:      
      -17,                      // labX       
      ddBoxTYPES,               // exType:    (n/a)
      1,                        // scrItems:  (n/a)
      1,                        // scrSel:    (n/a)
      NULL,                     // scrColor:  (n/a)
      NULL,                     // spinData:  (n/a)
      true,                     // active:    can receive focus
      NULL                      // nextCtrl:  end of list
   },
} ;

Group the Radio Buttons

We have defined two Radio Buttons, and each button selects a color attribute for our displayed message. Clearly, the message cannot be BOTH colors at the same time, so we must allow the user to select only one of the Radio Buttons at a time.

To do this, we create an Exclusive-OR grouping for the buttons. An XOR group means that exactly one member of the group may be selected at any given time. (The ’yellow’ button is initially selected.)

Copy the following into the ’MyDialog’ method just below ’dp->OpenWindow()’, and just above ’dp->WriteParagraph()’.

//* Create an Exclusive-OR Radio Button group *
short XorGroup[] = { grRB, yeRB, -1 } ; 
dp->GroupRadiobuttons ( XorGroup ) ;

Update our message

Make one more small adjustment to our existing code:
Replace the existing message text with the following message.

Replace:
 const char* msg = "      Hello World!       \n\nPress any key to exit... " ;

With:
 const char* msg = "      Hello World!      " ;

Rebuild and Run the Application

 gmake

 ./FirstProg

Congratulations!

You now have a fully-populated dialog application, although the user doesn’t yet have access to the controls. Let’s do that next.
See Simple Interface Loop.




Previous: , Up: Your First Application   [Contents][Index]

Simple Interface Loop

An application based on an NcDialog dialog window uses an extremely simple user interface loop. The loop simply transfers the input focus among the defined controls.

All actual keyboard (and mouse) input is handled by the ’Edit_Xxx’ method for the control object which currently has the input focus.

Note: If you’re having trouble understanding the concept of input 
focus, just think of it as aiming a paintball gun. Whatever the 
gun is aimed at, gets hit by the paintball (receives the keycode).

For our example application, we defined three (3) types of controls, so our user-interface loop must handle those three types.

Edit MethodControl ClassControl Type
 EditPushbutton DialogPushbutton dctPUSHBUTTON
 EditTextbox DialogTextbox dctTEXTBOX
 EditRadiobutton DialogRadiobutton dctRADIOBUTTON

Copy the following code into the ’MyDialog’ method just above ’nckPause();’, and then delete the ’nckPause();’ line because we don’t need it anymore.

Again, this is a big step, but each item will be carefully explained.

//* Interact with user *
gString lfMsg ;               // message buffer
uiInfo  Info ;                // user interface data returned here
short   icIndex = ZERO ;      // index of control with input focus
bool    done = false ;        // loop control
while ( ! done )
{
   if ( ic[icIndex].type == dctPUSHBUTTON )
   {
      if ( !Info.viaHotkey )
         icIndex = dp->EditPushbutton ( Info ) ;
      else
         Info.HotData2Primary () ;

      if ( Info.dataMod != false )
      {
         if ( Info.ctrlIndex == DonePB )
            done = true ;
      }
   }
   else if ( ic[icIndex].type == dctTEXTBOX )
   {
      icIndex = dp->EditTextbox ( Info ) ;

      if ( Info.dataMod != false )
      {  // get contents of Textbox
         dp->GetTextboxText ( lfTB, lfMsg ) ;
         // write it to the dialog display area
         winPos wp( 14, 14 ) ;
         dp->ClearLine ( wp.ypos ) ;
         if ( lfMsg.gschars() > 1 )
         {
            lfMsg.insert( "  My favorite Linux flavor is: " ) ;
            lfMsg.append( "  " ) ;
            dp->WriteString ( wp, lfMsg, mColor, true ) ;
         }
      }
   }
   else if ( ic[icIndex].type == dctRADIOBUTTON )
   {
      icIndex = dp->EditRadiobutton ( Info ) ;

      if ( Info.dataMod != false )
      {  // re-draw message(s) in specified color
         mColor = Info.selMember == grRB ? nc.grR : bColor ;
         dp->WriteString ( 5, 28, msg, mColor ) ;
         if ( lfMsg.gschars() > 1 )
            dp->WriteString ( 14, 14, lfMsg, mColor ) ;
         dp->RefreshWin () ;
      }
   }

   //* Move input focus to next/previous control.*
   if ( done == false && Info.viaHotkey == false )
   {
      if ( Info.keyIn == nckSTAB )
         icIndex = dp->PrevControl () ; 
      else
         icIndex = dp->NextControl () ;
   }
}  // while()

REMEMBER to delete the ’nckPause()’ call !!


A Closer Look At the Loop Elements

Note that no direct input is collected from the user!
All direct contact with the user is through the Edit_Xxx methods.


Rebuild and Run the Application

 gmake

 ./FirstProg

It’s Playtime!

For a screenshot of this dialog, please see eye candy.


Congratulations!

You have successfully created your first NcDialog application!

It may be simple, but it can already do real things. How long did it take? If you are an experienced programmer, it probably took less than 30 minutes. If you are just beginning your career as an author of world-changing software, then perhaps an hour. Well done!!

  Note that if you ran into problems, we have left you an Easter Egg.
  The source module, ’FirstProg.cpp’ and ’Makefile’ may be found at  
                     NcDialog/Misc/fp.tar
  It is our belief and our hope that you have learned more through 
  your step-by-step construction of the application than you would 
  have learned by simply copying the files.
  This is something teachers do——sorry for the trick.  :-)

Many more and different examples can be found in the test applications included in the NcDialog API package.
See Application Examples. Enjoy!





Next: , Previous: , Up: Top   [Contents][Index]

NCurses Engine

This chapter covers the NCurses class definition and the NCurses Engine which forms the connection between an NcDialog-based application and the ’ncurses’ C library primitives.







Next: , Up: NCurses Engine   [Contents][Index]

Introduction to ncurses

The ’curses’ interface was originally created for BSD UNIX to allow for formatted text output. A.T.&T. created a much-enhanced version of curses for use in its versions of UNIX.

The GNU ’ncurses’ (New Curses) C-language library is ’free software’ created under Linux to avoid copyright issues with SystemV(tm) UNIX. This is the version of curses included in all major Linux distributions. The ’ncurses’ C-language library comes in two flavors:
    ’ncurses’     This is the narrow (8-bit) character interface.
    ’ncursesw’    This is the wide (32-bit) character interface.
The functionality of these two versions is nearly identical; however, the ’wide’ version is recommended for all modern applications, because it fully supports all languages and character sets for internationalization of user interfaces.

Several people have been involved in the developmet of ’ncurses’ including Pavel Curtis, Zeyd Ben-Halim, Eric Raymond and Juergen Pfeifer. However, most of the heavy lifting in recent times has been done by the inestimable Thomas E. Dickey <http://invisible-island.net/> who, as he once posted on his website, took it up as a hobby. (Some hobby!)

Many terminal-based Linux applications use the ’ncurses’ C library, either directly or through one of the toolkits developed to make ’ncurses’ programming easier. ’ncurses’ is extremely portable and terminal-independent, and is reasonably-well documented. It suffers, however, from many years of questionable extensions (code bloat) and a general seediness that is revealed in a large number of small, but annoying bugs which most developers simply work around.

The only major drawback to the ’ncurses’ library is that it is oriented toward C-language programming and all that entails: lax prototyping, lack of type-checking, ’structs’, random casts, maintenance nightmares, and the constant threat of buffer overruns. This is not the fault of ’ncurses’, but of Kernighan and Ritchie, darn their lazy and unimaginative hides. :-)

Overall, ’ncurses’ is a tremendous accomplishment that brings a GUI-like interface and a quick application-development process to the obscure world of Linux command-line utilities.




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Class Overview

The NCurses Engine is based on the ’ncurses’ C-language library functions. It is written entirely in C++ and encapsulates the most vulnerable constructs of C-language programming with the ’ncurses’ library for a nearly-indestructable (albeit, single-threaded) user interface.

The NCurses Engine uses the ’ncursesw’ (wide-character) version of the ’ncurses’ C library exclusively. The 8-bit character library, ’ncurses’ is not used.

NCurses is implemented as a C++ class definition with approximately sixty (60) public methods for initialization and use of the underlying ’ncurses’ library for keyboard and mouse input and formatted text output to the terminal window.

A comprehensive array of color-attribute constants and keycode constants are also available as public data members.
See Keycode Constants.
See Color Attributes.

The NCurses-class methods follow the ’ncurses’ C library convention regarding defined return values:
   (short int) ’OK’  indicates a successful execution
   (short int) ’ERR’ indicates a general error condition
(See note below on the choice of ’short int’ over the ’int’ integral type.)

The NCurses class makes absolutely no effort to be an inclusive wrapper for the ’ncurses’ library. In the author’s view, most of the capabilities provided by ’ncurses’ are seldom, if ever used — certainly not in a simple terminal application, so there is no need to provide a high-level interface to them. Note however, that if you find a need for ’ncurses’ functionality that is not supported within the NCurses class, please send the author a note, and we’ll see what can be done.
See Technical Support.

IMPORTANT NOTE

There is a single instance of the NCurses class in each application program. Because the NCurses-class object is instantiated globally (see GlobalDef.hpp), access to the NCurses methods and public data members is through the global handle, ’nc’. Example:
              nc.WriteString ( 5, 2, "What’s up doc?", nc.bl ) ;
This call will write the message using the NCurses::WriteString() method, AND the message will be written using the color attribute defined in the public data member, NCurses::bl (blue on default background).

Notes

  1. The NCurses-class methods ARE NOT thread safe!
    The NCurses-class provides direct, or nearly direct access to the underlying ’ncurses’ C library functions. Be sure, therefore, to complete all initialization and configuration which uses NCurses methods BEFORE launching any secondary threads and BEFORE performing any Input/Output operations.
  2. Beyond the start-up and shut-down sequences described, direct use of the NCurses-class methods in applications is strongly discouraged.

    Instead, use the NcDialog API for all non-trivial applications, so that you are working in a controlled dialog window rather that writing haphazardly to the base terminal window.

    The exception would be if you are writing what appears to the user to be an application that is writing to ’stdout’ (’wcout’, ’wprintf’) BUT is actually capable of formatted, multi-color output. In that case, by all means, amaze your friends with your command-line skills! In particular, please refer to the OutputStreamScrolling method.

  3. The NCurses class, and in fact, the entire NcDialog API uses the ’short int’ integral type wherever practical. The author learned programming on an IBM360(tm), DEC PDP8(tm), PDP11(tm), S/100-bus hacks, the PET(tm), the Apple2(tm), Amiga(tm), and on and on: all in assembly language. The declaration of variables with 2, 4, 6, 12 or more extra bytes that do nothing at all is an offense to the author’s sensibilities.
    (We will admit, however, that memory-access time on certain platforms is more efficient in larger chunks.)



Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Public Methods

What follows is a list of all public methods of the NCurses class.
Methods are arranged in functional groups.

        NCurses Method Name            Chapter Reference    
 NCurses [constructor] see NCurses Startup-Shutdown
 ~NCurses [destructor] 
 StartNCursesEngine 
 StartColorEngine 
 ColorText_Available 
 ColorText_Initialized 
 TerminalColorSupport 
 StopNCursesEngine 
  
 ScreenDimensions see NCurses Configuration
 GetColorMap 
 SetColorMap 
 SetLocale 
 GetLocale 
 VerifyLocale 
 SetDefaultBackground 
 KeyEcho 
 GetKeyProcState 
 SetKeyProcState 
 GetKeyDelay 
 SetKeyDelay 
 SetKeycodeWidth 
  
 GetKeyInput see NCurses Keyboard Input
 UngetKeyInput 
 KeyPeek 
 GetEscapeSequence 
 FlushKeyInputStream 
  
 WriteString see NCurses Display Output
 WriteChar 
 ClearScreen 
 RefreshScreen 
 ClearLine 
 GetCursor 
 SetCursor 
 GetCursorState 
 SetCursorState 
 RestoreCursorState 
  
 EnableMouseEvents see NCurses Mouse Access
 DisableMouseEvents 
 MouseEventsEnabled 
 GetMouseEventTypes 
 SetMouseEventTypes 
 GetMouseClickInterval 
 SetMouseClickInterval 
 GetMouseEvent 
 UngetMouseEvent 
 FlushMouseEventQueue 
 GetMousePosition 
 FilterSpuriousMouseEvents 
  
 Hibernate see NCurses Utility Methods
 Wake 
 UserAlert 
 Get_NCurses_Version 
 Get_nclibrary_Version 
  
 WriteStream see NCurses Debugging Methods
 OutputStreamScrolling 
 DisplayColorOptions 
 DisplayDefinedColors 
 DisplayAlternateFont 
 GetTransTable1 
 GetTransTable2 
 GetTransTable3 
 Get_stdscr_Address 



Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Startup-Shutdown




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Configuration




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Keyboard Input



Please see NCurses Configuration for information on configuring the key input stream.




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Display Output

The following methods write text and color-attribute data directly to the main terminal window. As with all NCurses-class methods, access is through the global handle: 'nc'.
Example: nc.WriteString ( 4, 1, "Hello World!", nc.bl );

Note that if NcWindow-class or NcDialog-class objects also exist in the terminal window, then those objects will need to be refreshed AFTER you have finished writing data to the terminal window.

IMPORTANT NOTE:
NCurses-class output methods are not thread safe; thread safety is implemented at a higher level (see thread-safe operation).
When using NCurses-class methods, be very sure that only one execution thread in your application is writing to the display.






Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Mouse Access

The NCurses-class mouse implementation is simply a primitive
interface to the underlying ncursesw C-language library mouse
interface. For the application layer mouse interface, please
see the NcDialog API mouse interface.
                See Mouse Configuration.

Introduction to the Text-based Mouse Interface

Please see the test application, Dialog4, Test03 for an example of the NCurses-class mouse interface.

Please see the test application, Dialog4, Test04 for an example of the NcDialog-class mouse interface.

In general, using the mouse for a text-based application is unnecessary, and actually makes the user interface more difficult to use. This is especially true at the level of the NCurses (terminal window) abstraction layer. For this reason, please consider the following method descriptions as reference information only, and not as an invitation to use them directly. However, if you must have mouse input for your application, use the NcDialog mouse interface instead: see Mouse Configuration, which transparently converts most mouse events into the equivalent keystroke data.




NCurses Mouse Methods




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Utility Methods




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

NCurses Debugging Methods




Next: , Previous: , Up: NCurses Engine   [Contents][Index]

Color Attributes




Next: , Up: Color Attributes   [Contents][Index]

ncurses Color Support

Color Attributes In ncurses World

In order to provide the same support for all hardware platforms, all operating systems and terminal types, the ncurses C-language library provides only very basic color mapping for the text displayed in a terminal window.

However, the ncurses library is aware of the (virtual) RGB color registers and the foreground/background color pairs which are part of the terminal definition and this awareness allows for expanding color support to match the capabilities of each platform.

The actual resources available for a particular installation are determined by the hardware platform and terminal-emulator software version, as well as the terminal type being emulated and the color palette selected during terminal configuration.

Because direct color support in the underlying library is so rudimentary, the NcDialog API provides comprehensive color support matching the capabilities of the target terminal. Please see the following chapters for additional information.




Next: , Previous: , Up: Color Attributes   [Contents][Index]

Platform Color Support

A Note on Dumb Terminals

In the early days of computing, before personal computers were available and before our screens were filled with pretty screen-saver slide shows, a "terminal" was a physical piece of hardware consisting of a cathode-ray tube driven by a minimal bit of electronics which provided a simple, text-based interface to the host computer. Each hardware manufacturer had one or more hardware designs with varying capabilities. You may have heard about some of these designs, the VT-100 and so on. Nearly all of these are now in some landfill, leaking lead and arsinic into our water supply—so our discussion will focus on "terminal emulators" which are software programs that "emulate" the physical terminals they replace and provide us with a text-based interface to the guts of our operating system.


How Color Is Implemented In a Modern Terminal Emulator

Except for monochrome dumb terminals, most modern terminals support color text output. Terminal configuration is a complex subject, but for color there are two mechanisms in play:

  1. RGB Registers
    The number of RGB (Red - Green - Blue) register sets determines the number of unique colors which can be simultaneously displayed by the terminal. These are not physical registers, but are software-emulated registers which provide a subset of the colors available to the parent (GUI) environment.

    In most systems, there will be either eight(8) or sixteen(16) RGB register sets. For some systems the register settings are fixed, while on other systems you may change the individual color bits of these registers to customize the terminal’s color palette.

    The ncurses library provides access to these registers and determines whether they are read-write or read-only. If the RGB values are read-write, then each of the three base colors may be set to any value between 0 and 1000 with the default being 680 (decimal). For a typical Gnome terminal, set to the ’Linux Console’ color scheme, the 16 RGB registers would look like this.

              R    G    B                       R    G    B
    Black    0000 0000 0000    Bright Black    0000 0000 0000
    Red      0680 0000 0000    Bright Red      1000 0000 0000
    Green    0000 0680 0000    Bright Green    0000 1000 0000
    Brown    0680 0680 0000    Bright Brown    1000 1000 0000
    Blue     0000 0000 0680    Bright Blue     0000 0000 1000
    Magenta  0680 0000 0680    Bright Magenta  1000 0000 1000
    Cyan     0000 0680 0680    Bright Cyan     0000 1000 1000
    Grey     0680 0680 0680    Bright Grey     1000 1000 1000
    
  2. Color Pairs
    A ’color pair’ is a pair of index values that indicate which RGB register will be used for the foreground (text) color and which RGB register will be used for the background color.

    The ncurses C-language library initializes only the first eight(8) color pairs (pairs 0 through 7). These eight pairs are all set to use the terminal’s default background color, and the foreground colors are set to Black, Red, Green, Brown, Blue, Magenta, Cyan and Grey, respectively.

    Note that color pair zero(0) is a special case. Strictly speaking, color pair zero should be set as white text on a black background; however, research has shown that this is hard on the eyes. For this reason, the terminal definition often exchanges the foreground and background colors of color pair zero (black on near-white) to provide better visual contrast.

The ncurses library and the NcDialog API handle all the low-level details of color mapping, so in general, it is not necessary for the application to deal directly with the terminal’s color configuration unless you want to create a custom configuration. For details on how to capture and modify the terminal’s color map, please see Adjusting the Color Map in test application ’Dialog2’.




Next: , Previous: , Up: Color Attributes   [Contents][Index]

NcDialog API Color Support

The NcDialog API takes full advantage of the ncurses library’s support for color in the terminal window using up to sixteen (16) RGB register sets and initializing all available foreground/background color pairs up to 256 pairs.

Note however, that except for the start-up sequence, direct uses of the low-level registers and color pairs is strongly discouraged. All application-level access to color attributes should be through the NcDialog API color variables see NcDialog Color Variables.

NcDialog API Color Support

Although the ncurses library initializes only color pairs 0 through 7, most terminals support many more color pairs. The NcDialog API initializes all available color pairs up to 256 pairs so that your application will encounter no surprises when using them to write text data in the terminal window.

The following discussion may be unsatisfying in ’info’ format because the Texinfo documentation engine is unable display color data, however, the NcDialog test applications display the information in full color.

See Dialog2 App,
      Test #4: see Adjusting the Color Map
      Test #6: see Display Text Color Mapping
      Test #7: see Capture Display Data to Text or HTML

The most common color terminal configurations are:


RGB Registers

The number of RGB registers available determines the number of colors which can be simultaneously displayed in the terminal window. The NcDialog API assigns each of these foreground colors to a foreground/background color pair using the terminal’s default background color.
The basic eight(8) RGB registers are:

COLOR           COLOR PAIR
00 Black        00h   (fg/bg are often swapped by the terminal)
01 Red          01h
02 Green        02h
03 Brown        03h
04 Blue         04h
05 Magenta      05h
06 Cyan         06h
07 Grey         07h

If the terminal supports additional RGB registers they are:

COLOR                      COLOR PAIR
08 Bright Black            40h (64 decimal)
09 Bright Red              41h
10 Bright Green            42h
11 Bright Brown (yellow)   43h
12 Bright Blue             44h
13 Bright Magenta          45h
14 Bright Cyan             46h
15 Bright Grey             47h (often indistinguishable from white)

Color Pairs

In addition to mapping the 16 RGB registers to individual color pairs, if additional color pairs are available, the NcDialog API initializes 56 high-contrast foreground/background color combinations and assigns them to color pairs 08h through 3Fh as follows.

COLOR PAIR  FOREGROUND/BACKGROUND   COLOR PAIR  FOREGROUND/BACKGROUND
08h         Black on Red            24h         Blue on Black
09h         Black on Green          25h         Blue on Red
0Ah         Black on Brown          26h         Blue on Green
0Bh         Black on Blue           27h         Blue on Brown
0Ch         Black on Magenta        28h         Blue on Magenta
0Dh         Black on Cyan           29h         Blue on Cyan
0Eh         Black on Grey           2Ah         Blue on Grey
0Fh         Red on Black            2Bh         Magenta on Black
10h         Red on Green            2Ch         Magenta on Red
11h         Red on Brown            2Dh         Magenta on Green
12h         Red on Blue             2Eh         Magenta on Brown
13h         Red on Magenta          2Fh         Magenta on Blue
14h         Red on Cyan             30h         Magenta on Cyan
15h         Red on Grey             31h         Magenta on Grey
16h         Green on Black          32h         Cyan on Black
17h         Green on Red            33h         Cyan on Red
18h         Green on Brown          34h         Cyan on Green
19h         Green on Blue           35h         Cyan on Brown
1Ah         Green on Magenta        36h         Cyan on Blue
1Bh         Green on Cyan           37h         Cyan on Magenta
1Ch         Green on Grey           38h         Cyan on Grey
1Dh         Brown on Black          39h         Grey on Black
1Eh         Brown on Red            3Ah         Grey on Red
1Fh         Brown on Green          3Bh         Grey on Green
20h         Brown on Blue           3Ch         Grey on Brown
21h         Brown on Magenta        3Dh         Grey on Blue
22h         Brown on Cyan           3Eh         Grey on Magenta
23h         Brown on Grey           3Fh         Grey on Cyan

If the terminal supports RGB registers 8-15, and if a sufficient number of color pairs are available, the NcDialog API assigns an additional 56 color combinations to color pairs 48h through 7Fh using the ’Bright’ versions of the color combinations shown in the table above.

COLOR PAIR  FOREGROUND/BACKGROUND
48h         Bright Black on Bright Red.
 .  .  .
7Fh         Bright Grey on Bright Cyan

Color Pairs 128 and Above

Note that although the NcDialog API directly uses only color pairs 00h-7Fh; if the system supports color pairs beyond 7Fh (127 decimal), then color pairs 80h-FFh (128 through 255 decimal) are initialized to the terminal’s default foreground/background color. This is done because unitialized color pairs result in black-on-black which is a rather unfortunate display color.


Color Attribute Bits

Each foreground/background color may be modified using one or more of the color-attribute bits.

Note that no hardware-based terminals support all of these attribute bits and that most terminal emulators support only the most useful subset. Note that specifying an unsupported attribute will have no effect on the output.

'attr_t' type   Bit Number   Attribute
  00000000h        n/a       NORMAL     (no attribute)
  00010000h         16       STANDOUT   (fgnd/bkgnd reversed and both Bold)
  00020000h         17       UNDERLINE  (underlined text)
  00040000h         18       REVERSE    (foreground/background reversed)
  00080000h         19       BLINK      (blinking text)
  00100000h         20       DIM        (faded text)
  00200000h         21       BOLD       (bold foreground text)
  00400000h         22       ALT_CHAR_SET (interpret character as value in
                                           'alternate character set')
  00800000h         23       INVISIBLE  (invisible text bkgnd==fgnd)
  01000000h         24       PROTECT    ('protected' i.e. read-only)
  02000000h         25       HORIZONTAL (unsupported)
  04000000h         26       LEFT       (unsupported)
  08000000h         27       LOW        (unsupported)
  10000000h         28       RIGHT      (unsupported)
  20000000h         29       TOP        (unsupported)
  40000000h         30       VERTICAL   (unsupported)

Please see Specifying Color Attributes for more information.




Previous: , Up: Color Attributes   [Contents][Index]

NcDialog Color Variables

The previous chapter described the low-level color mapping mechanism; however, the application accesses all these color data through the defined color-attribute variables.

These variables are defined as public data members of the NCurses class.

IMPORTANT NOTE: In order to simplify and speed up the initialization process of the NCurses Engine, and to avoid the need for macros for accessing color variables, we have made the design decision to declare these color variables as ordinary public ’attr_t’ variables rather than ’const attr_t’ as they should be.

However, it is YOUR responsibility as the application programmer to ALWAYS consider these variables as being READ-ONLY! Of course, we trust you completely, but be aware that re-defining ’red’ as ’blue’ (for instance) would be somewhat counter-productive, so remember: READ-ONLY!

Naming Convention

As described elsewhere (see Accessing NCurses Class Members) all NCurses-class public methods and data are accessed through the global handle ’nc’. Thus, color data are accessed as shown in the example:

attr_t MsgColor = nc.bl ;

Color variables use a short, but consistent and memorable naming convention. Each base color is specified using a two-character code:

CODE  COLOR       VARIABLE
bk    BlacK       nc.bk
re    REd         nc.re
gr    GReen       nc.gr
br    BRown       nc.br
bl    BLue        nc.bl
ma    MAgenta     nc.ma
cy    CYan        nc.ma
gy    GreY        nc.gy
bw                nc.bw    Specifies the terminal's default foreground 
                           which will usually be either black-on-white 
                           OR white-on-black.

For often-used ’color + attribute’ combinations, the following variables are defined:

CODE  COLOR                VARIABLE
bkB   BlacK (Bold)         nc.bkB
bkU   BlacK (Underlined)   nc.bkU
bkR   BlacK (Reversed)     nc.bkR
bkG   BlacK (Bold+Reverse) nc.bkG
bkS   BlacK (Standout)     nc.bkS
bkD   BlacK (Dim)          nc.bkD

Variable names for the remaining base colors follow this 
naming pattern for color+attribute combinations:

BOLD    UNDERLINED  REVERSED  BOLD+REV  STANDOUT  DIM
nc.reB   nc.reU      nc.reR    nc.reG    nc.reS   nc.reD
nc.grB   nc.grU      nc.grR    nc.grG    nc.grS   nc.grD
nc.brB   nc.brU      nc.brR    nc.brG    nc.brS   nc.brD
nc.blB   nc.blU      nc.blR    nc.blG    nc.blS   nc.blD
nc.maB   nc.maU      nc.maR    nc.maG    nc.maS   nc.maD
nc.cyB   nc.cyU      nc.cyR    nc.cyG    nc.cyS   nc.cyD
nc.gyB   nc.gyU      nc.gyR    nc.gyG    nc.gyS   nc.gyD

For 16-color systems, add ’b’ (’Bright’) to specify the color variable name.

Note that for 8-color systems, these color variables are duplicates of the variables for color pairs 00h-07h.

CODE  COLOR             VARIABLE
bbk   Bright BlacK      nc.bbk
bre   Bright REd        nc.bre
bgr   Bright GReen      nc.bgr
bbr   Bright BRown      nc.bbr
bbl   Bright BLue       nc.bbl
bma   Bright MAgenta    nc.bma
bcy   Bright CYan       nc.bma
bgy   Bright GreY       nc.bgy

Variable names for often-used ’color + attribute’ combinations follow the established naming pattern:

BOLD    UNDERLINED  REVERSED  BOLD+REV  STANDOUT  DIM
nc.bbkB  nc.bbkU    nc.bbkR   nc.bbkG   nc.bbkS   nc.bbkD
nc.breB  nc.breU    nc.breR   nc.breG   nc.breS   nc.breD
nc.bgrB  nc.bgrU    nc.bgrR   nc.bgrG   nc.bgrS   nc.bgrD
nc.bbrB  nc.bbrU    nc.bbrR   nc.bbrG   nc.bbrS   nc.bbrD
nc.bblB  nc.bblU    nc.bblR   nc.bblG   nc.bblS   nc.bblD
nc.bmaB  nc.bmaU    nc.bmaR   nc.bmaG   nc.bmaS   nc.bmaD
nc.bcyB  nc.bcyU    nc.bcyR   nc.bcyG   nc.bcyS   nc.bcyD
nc.bgyB  nc.bgyU    nc.bgyR   nc.bgyG   nc.bgyS   nc.bgyD

Additional Color Variables

Color variables are also established for the remaining initialized color pairs 08h-3Fh (8 through 63 decimal), and if available, color pairs 48h-7Fh (72 through 127 decimal).

Note that if color pairs 48h-7Fh are not available, then the corresponding color variables are duplicates of the variables for color pairs 08h-3Fh.

 NORMAL                            BRIGHT
VARIABLE  FOREGROUND/BACKGROUND   VARIABLE
nc.rebk      Red on Black         nc.brebk
nc.grbk      Green on Black       nc.bgrbk
nc.brbk      Brown on Black       nc.bbrbk
nc.blbk      Blue on Black        nc.bblbk
nc.mabk      Magenta on Black     nc.bmabk
nc.cybk      Cyan on Black        nc.bcybk
nc.gybk      Grey on Black        nc.bgybk

nc.bkre      Black on Red         nc.bbkre
nc.grre      Green on Red         nc.bgrre
nc.brre      Brown on Red         nc.bbrre
nc.blre      Blue on Red          nc.bblre
nc.mare      Magenta on Red       nc.bmare
nc.cyre      Cyan on Red          nc.bcyre
nc.gyre      Grey on Red          nc.bgyre

nc.bkgr      Black on Green       nc.bbkgr
nc.regr      Red on Green         nc.bregr
nc.brgr      Brown on Green       nc.bbrgr
nc.blgr      Blue on Green        nc.bblgr
nc.magr      Magenta on Green     nc.bmagr
nc.cygr      Cyan on Green        nc.bcygr
nc.gygr      Grey on Green        nc.bgygr

nc.bkbr      Black on Brown       nc.bbkbr
nc.rebr      Red on Brown         nc.brebr
nc.grbr      Green on Brown       nc.bgrbr
nc.blbr      Blue on Brown        nc.bblbr
nc.mabr      Magenta on Brown     nc.bmabr
nc.cybr      Cyan on Brown        nc.bcybr
nc.gybr      Grey on Brown        nc.bgybr

nc.bkbl      Black on Blue        nc.bbkbl
nc.rebl      Red on Blue          nc.brebl
nc.grbl      Green on Blue        nc.bgrbl
nc.brbl      Brown on Blue        nc.bbrbl
nc.mabl      Magenta on Blue      nc.bmabl
nc.cybl      Cyan on Blue         nc.bcybl
nc.gybl      Grey on Blue         nc.bgybl

nc.bkma      Black on Magenta     nc.bbkma
nc.rema      Red on Magenta       nc.brema
nc.grma      Green on Magenta     nc.bgrma
nc.brma      Brown on Magenta     nc.bbrma
nc.blma      Blue on Magenta      nc.bblma
nc.cyma      Cyan on Magenta      nc.bcyma
nc.gyma      Grey on Magenta      nc.bgyma
                                  
nc.bkcy      Black on Cyan        nc.bbkcy
nc.recy      Red on Cyan          nc.brecy
nc.grcy      Green on Cyan        nc.bgrcy
nc.brcy      Brown on Cyan        nc.bbrcy
nc.blcy      Blue on Cyan         nc.bblcy
nc.macy      Magenta on Cyan      nc.bmacy
nc.gycy      Grey on Cyan         nc.bgycy
                                  
nc.bkgy      Black on Grey        nc.bbkgy
nc.regy      Red on Grey          nc.bregy
nc.grgy      Green on Grey        nc.bgrgy
nc.brgy      Brown on Grey        nc.bbrgy
nc.blgy      Blue on Grey         nc.bblgy
nc.magy      Magenta on Grey      nc.bmagy
nc.cygy      Cyan on Grey         nc.bcygy

Note that some of the above color-combination variables include the ’Bold’ (ncbATTR) color attribute bit in order to produce acceptable contrast between foreground and background. Even so, there are a few combinations which do not produce easily-readable text, so experiment a bit to determine which combinations look best in your applications.


Specifying Color Attributes

The NcDialog API defines the following color attribute bit masks.
These bit masks are defined in NCurses.hpp.

As noted in a previous chapter, not all systems support all of these color attribute bits, and terminal emulators support only a common subset, usually ’Bold’, ’Underline’, ’Reverse’, and ’AltCharSet’. Use of an unsupported attribute bit will have no effect on the output.

const attr_t ncnATTR ; // NORMAL     (no attribute)
const attr_t ncsATTR ; // STANDOUT   (fgnd/bkgnd reversed and both Bold)
const attr_t ncuATTR ; // UNDERLINE  (underlined text)
const attr_t ncrATTR ; // REVERSE    (foreground/background reversed)
const attr_t nckATTR ; // BLINK      (blinking text)
const attr_t ncdATTR ; // DIM        (faded text)
const attr_t ncbATTR ; // BOLD       (bold foreground text)
const attr_t ncaATTR ; // ALT_CHAR_SET (interpret character as alternate-
                       // character-set value - use is not recommended)
const attr_t ncgATTR ; // REVERSE+BOLD (combines ncbATTR and ncrATTR)
const attr_t nciATTR ; // INVISIBLE  (invisible text)
const attr_t ncpATTR ; // PROTECT    (unsupported)
const attr_t nchATTR ; // HORIZONTAL (unsupported)
const attr_t nclATTR ; // LEFT       (unsupported)
const attr_t ncwATTR ; // LOW        (unsupported)
const attr_t ncqATTR ; // RIGHT      (unsupported)
const attr_t nctATTR ; // TOP        (unsupported)

Adding a color attribute bit is done through a logical-OR of that bit with the base color.

Combine Blue text with the Underline attribute:
attr_t msgColor = nc.bl | ncuATTR ;

Write a message using Magenta text with Bold and Reverse attributes:
dp->WriteString ( wp, "Hello World!", nc.ma | ncbATTR | ncrATTR ) ;



Next: , Previous: , Up: NCurses Engine   [Contents][Index]

Keycode Constants

What Is a Character?

The native language of Linux is UTF-8. UTF-8 encoding is a byte-oriented way of representing all characters of all human languages.

’Curses’ was developed long before UTF-8 encoding, so character encoding and processing in curses world is not as simple as one would hope. The original 7-bit and 8-bit curses C-language library was completely inadequate for international communication, so the ’wide character’ library (ncursesw) was developed. The ’wide’ library is based on a ’wchar_t’ (32-bit integer) character type. At the lowest level, some tricks are played to enable ’wide’ character support in the ’ncursesw’ library. Fortunately, an NcDialog API application doesn’t need to worry about these details because the printing characters are equivalent to ’Unicode code points’. However, a few things must be kept in mind.

The first thing to remember is that character codes fall into two general categories.

  1) printing character codes and meta-characters
  2) command (function) codes

Because the ’ncursesw’ library has only a very limited number of codes available, the numerical values for characters and for commands overlap; therefore, a character is always handled as a pair of codes.

  a) the character CODE, and
  b) the charater TYPE.

In general, it is not possible to know whether a given code is a character or a command code unless we look at both of these elements (see below for examples).

The ’ncursesw’ C-language library provides basic support for all printing characters (and meta-characters) as well as a useful selection of function-key definitions; however, the way the ’ncursesw’ library handles the code pairs is inconvenient, to say the least. For this reason, the NcDialog API encapsulates all the underlying messiness into the wkeyCode class.


The wkeyCode Class

The wkeyCode class, defined in NCurses.hpp, is the workhorse of character processing. The wkeyCode class has three public data members:

  wchar_t    key ;
  wkeyType   type
  MouseEvent mevent ;

First, let’s look at the ’type’ member. The type is specified by the enumeration list, ’wkeyType’:

  enum wkeyType : short
  {               // == INDICATES ==
     wktPRINT,    // a printing character or metacharacter
     wktFUNKEY,   // a command code (function-key code)
     wktMOUSE,    // a mouse event code
     wktEXTEND,   // a member of the 'extended' command-key group
     wktESCSEQ,   // an untranslated escape sequence
     wktERR       // an invalid code
  } ;

wktPRINT and wktFUNKEY correspond roughly to the designations in the underlying ncurses(w) library.

wktMOUSE indicates that a mouse event has been captured and that the mouse data have been stored in the ’mevent’ data member. For more information on the mouse interface, please see NCurses Mouse Access.

wktEXTEND indicates that the code is one of the extended command-keys defined by the NcDialog API. These command keys are mostly 'ALT+x' key combinations. For instance, if the user presses and holds the ’ALT’ key and then presses the ’A’ key, that combination, 'ALT+A' is a non-printing command key (defined as 'nckA_A').

wktESCSEQ indicates that an unknown sequence of bytes has been received from the underlying keyboard interface. Because the ncurses(w) library has only a limited number of keycodes available, and because the ncurses(w) library must support a very wide variety of modern and legacy keyboards, not all escape sequences can have a keycode assigned to them. As an example, the ncurses(w) library does not translate the following sequence of hexadecimal bytes received from the keyboard. An escape sequence like this is generally meaningless at the application level, and so should be ignored.

  1B 5B 31 7E     // represents numeric keypad 'Home' key

wktERR indicates than the attempt to retrieve keyboard input has failed or that the data received do not represent a valid code.


Testing Key Data

Next, The wchar_t value, ’key’ defines the keycode. Before discussing the values that ’key’ can contain, it is important to understand that a given value of ’key’ can only be properly interpreted within its ’type’ context.

Here are some practical examples of testing the key input:

// define some wkeyCode-class objects
wkeyCode wk, wkesc( nckESC, wktFUNKEY );

// get key input from user
// (note that key type is also the return value)
nc.GetKeyInput ( wk ) ;

// testing for the return of the LeftArrow function key
if ( wk.type == wktFUNKEY && wk.key == nckLEFT )
{
   /* DO STUFF */ 
}

// testing for return of the extended-definition key, ALT+SHIFT+B
else if ( wk.type == wktEXTEND && wk.key == nckAS_B )
{
   /* DO STUFF */
}

// testing for equality with another wkeyCode object
else if ( wk == wkesc )
{
   /* DO STUFF */
}

// You may also test for inequality with another wkeyCode object:
if ( wk != wkesc )
{
   /* DO STUFF */
}


Printing Characters and Metacharacters

A printing character is one which can be written to the display and we can see it. Most human language characters fall into this category.

There are a few character which are known as control codes, the 'CTRL+C' (break) and the ’ENTER’ (RETURN) key are common examples. These characters are not visible, but they control the flow of the output.

However, some languages and character sets, while quite complex and beautiful when written by hand, are not so simple to render electronically. A base character may require one or more additional marks or glyphs in order to be complete. These additional glyphs are not characters in themselves, and so are never printed alone, but only in combination with a base character. These special glyphs are known as metacharacters. The Arabic language is a good example of a language which uses metacharacters.

For additional information on character encoding please
see Multi-language Support.


Defined Constants

While printing characters and metacharacters are well-defined and standardized, function keys and their associated control codes vary from system to system.

The ncurses(w) C-language library defines certain keycodes based on information in the terminal definition database (termcap, terminfo, environment variables and so on). These definitions may refer to a single key, or may refer to a key combination. Because the ncurses(w) library must support a wide variety of systems (many of which no longer exist) many of these keycodes are implemented as macros. However, macros are difficult to work with and tend to be fat and slow when converted to CPU instructions.

For this reason, the NcDialog API, and specifically the NCurses class define a large number of useful control code constants as well as some common character codes.


ASCII Control Codes

The ASCII (American Standard Code for Information Interchange) control codes are represented in various human readable forms ’^A’, ’Ctrl+A’, 0x01 and so on. In the NcDialog API, these control codes are referenced by their named constant values, e.g. ’nckC_A’ means ’NCursesKey Control+A’.

 Constant  Code Constant  Code Constant  Code
 nckC_A Ctrl+A nckC_K Ctrl+K nckC_V Ctrl+V
 nckC_B Ctrl+B nckC_L Ctrl+L nckC_W Ctrl+W
 nckC_C Ctrl+C nckC_N Ctrl+N nckC_X Ctrl+X
 nckC_D Ctrl+D nckC_O Ctrl+O nckC_Y Ctrl+Y
 nckC_E Ctrl+E nckC_P Ctrl+P nckC_Z Ctrl+Z
 nckC_F Ctrl+F nckC_Q Ctrl+Q nckESC Ctrl+[
 nckC_G Ctrl+G nckC_R Ctrl+R nckC_4 Ctrl+4
 nckC_H Ctrl+H nckC_S Ctrl+S nckC_5 Ctrl+5
 nckTAB Ctrl+I nckC_T Ctrl+T nckC_6 Ctrl+6
 nckENTER Ctrl+J nckC_U Ctrl+U nckC_7 Ctrl+7

Note that the zero (0, \0, 0x00) character is designated as nckNULLCHAR.

Note that the ’Ctrl+M’ (carriage return) keycode is not used in Linux/UNIX systems. The ’Ctrl+J’ (linefeed) keycode is used instead.


Navigation Keys

The navigation keys, Up, Down, Right, Left, Tab, PageUp, PageDown, Home and End are familiar to most computer users. These keys when used alone or in combination with the ’Shift’, ’Ctrl’ and ’Alt’ (’Meta’) keys move the text cursor and/or the input focus around the window.

Some of these key combinations are not generated by the keyboard itself, some are captured by the system before they reach the terminal window, some are captured by the terminal window itself, and some cannot be handled by the underlying ncurses(w) C-language library. The remainder are available to an application running in a terminal window. The exact list of navigation keys which will be available is system dependent. For instance, this list shows an example of the possible key combinations for the DownArrow key; however, it is likely that some of these key combinations will not be available on your system.


The following table shows the navigation-key combinations which are actually seen by an NcDialog application running in a terminal window on typical Wintel hardware.

 Name  Base +Shift+Alt+Shift
 Up Arrow nckUP nckSUP nckASUP
 Down Arrow nckDOWN nckSDOWN nckASDOWN
 Right Arrow nckRIGHT nckSRIGHT nckASRIGHT
 Left Arrow nckLEFT nckSLEFT nckASLEFT
 Page Up nckPGUP . . . . . .
 Page Down nckPGDOWN . . . . . .
 Home nckHOME . . . . . .
 End nckEND . . . . . .
 Tab nckTAB nckSTAB . . .
    
 Name +Alt+Ctrl+Alt+Ctrl
 Up Arrow nckAUP nckCUP . . .
 Down Arrow nckADOWN nckCDOWN . . .
 Right Arrow nckARIGHT nckCRIGHT . . .
 Left Arrow nckALEFT nckCLEFT . . .
 Page Up nckAPGUP . . . nckACPGUP
 Page Down nckAPGDN . . . nckACPGDN
 Home . . . . . . . . .
 End . . . . . . . . .
 Tab . . . nckTAB . . .

Numeric Keypad

For keyboards which have built-in numeric keypads or for externally-mounted numeric keypads, some additional navigation keys and other keys may be available. Some of these extra keys will yield the same keycodes as the equivalent key on the main keyboard, so they cannot be individually identified. The keycodes which can be identified as belonging to the numeric keypad are indicated with a ’nckpXXX’ named constant, but within this group, note that many of the key combinations yield the same keycode as the base key, so this table contains some duplicates.

IMPORTANT NOTE: In order to see these keycodes, the ’Numlock’ key must be ON and the ’Shift’ key MUST NOT be pressed.

 Name  Base +Alt+Ctrl +Alt+Ctrl
 Up Arrow . . . . . . . . . nckpACUP
 Down Arrow . . . . . . . . . nckpACDN
 Right Arrow . . . . . . . . . nckpACRIGHT
 Left Arrow . . . . . . . . . nckpLEFT
 Page Up . . . nckpAPGUP nckpCPGUP nckpACPGUP
 Page Down . . . nckpAPGDN nckpCPGDN nckpACPGDN
 Home . . . . . . . . . . . .
 End . . . . . . . . . . . .
 Center(’5’) nckpCENTER nckpCENTER nckpCENTER nckpCENTER
 Insert . . . . . . . . . . . .
 Delete . . . . . . . . . nckpACDEL
 Enter(RET) nckpENTER nckpENTER nckpENTER nckpENTER
 Plus(’+’) nckpPLUS nckpPLUS . . . . . .
 Minus(’-’) nckpMINUS nckpMINUS . . . . . .
 Mult(’*’) nckpMULT nckpMULT nckpMULT . . .
 Div(’/’) nckpDIV nckpDIV nckpDIV . . .


Function Keys

A ’Function Key’ is any keycode which represents a command rather than a printing character.

The navigation keys described above are a type of function key because they command the cursor to go somewhere. However, there are many other command keys. The most notable of these is the row of 'Fnn' keys found at the top of most keyboards. The number of these keys actually present and the way they are interpreted are system dependent.

PCs, i.e. Wintel hardware generally have either ten or twelve ’Fnn’ keys. Mainframes, minicomputers and high-end workstations often have several more.

Some of these keys, (the ’F01’ equals ’Help’ for instance), have become standard keyboard fuctionality, while the definition of other key combinations is left to the system or to individual applications. As with the navigation keys described above, function keys can be combined with the modifier keys ’Shift’, ’Alt’ ("Meta’) and ’Ctrl’ to provide the user with many ways to quickly accomplish common tasks.

Some of these key combinations are not generated by the keyboard itself, some are captured by the system before they reach the terminal window, some are captured by the terminal window itself, and some cannot be handled by the underlying ncurses(w) C-language library. The remainder are available to an application running in a terminal window. The exact list of function/command keys which will be available to your application is system dependent. The following table describes the Function Key combinations defined directly by the underlying ncurses(w) library OR translated from escape sequenced by the NcDialog API.

The ncurses(w) C-language library can store up to a maximum of sixty-four (64) function keycodes. In practice, however, the library does not use all of this reserved storage. The NcDialog API uses some of the free keycode storage for other command keys, and some slots remain undefined.

 Name  Base  +Shift +Ctrl +Ctrl+Shift
 F01 nckF01 nckSF01 nckCF01 nckCSF01
 F02 nckF02 nckSF02 nckCF02 nckCSF02
 F03 nckF03 nckSF03 nckCF03 nckCSF03
 F04 nckF04 nckSF04 nckCF04 nckCSF04
 F05 nckF05 nckSF05 nckCF05 nckCSF05
 F06 nckF06 nckSF06 nckCF06 nckCSF06
 F07 nckF07 nckSF07 nckCF07 nckCSF07
 F08 nckF08 nckSF08 nckCF08 nckCSF08
 F09 nckF09 nckSF09 nckCF09 nckCSF09
 F10 nckF10 nckSF10 nckCF10 nckCSF10
 F11 nckF11 nckSF11 nckCF11 nckCSF11
 F12 nckF12 nckSF12 nckCF12 nckCSF12


Extended Command Codes

Because the ncurses(w) C-language library has limited space for keycode storage, it does not handle most command codes generated using the ’Alt’ (’Meta’) key.

To provide a larger variety of command codes for use in your applications, the NcDialog API defines as many of the 'Alt+n' command codes as is practical. These command codes are not as portable among different hardware platforms as are the base ncurses(w) keycodes; however, for systems that support them (Wintel/PC hardware for instance), these command codes are available for application development. (see NCursesKeyDef.hpp)

 Constant  Code Constant  Code Constant  Code
 nckA_A Alt+a nckAS_A Alt+Shift+a nckAC_A Alt+Ctrl+a
 nckA_B Alt+b nckAS_B Alt+Shift+b nckAC_B Alt+Ctrl+b
 nckA_C Alt+c nckAS_C Alt+Shift+c nckAC_C Alt+Ctrl+c
 nckA_D Alt+d nckAS_D Alt+Shift+d nckAC_D Alt+Ctrl+d
 nckA_E Alt+e nckAS_E Alt+Shift+e nckAC_E Alt+Ctrl+e
 nckA_F Alt+f nckAS_F Alt+Shift+f nckAC_F Alt+Ctrl+f
 nckA_G Alt+g nckAS_G Alt+Shift+g nckAC_G Alt+Ctrl+g
 nckA_H Alt+h nckAS_H Alt+Shift+h nckAC_H Alt+Ctrl+h
 nckA_I Alt+i nckAS_I Alt+Shift+i nckAC_I Alt+Ctrl+i
 nckA_J Alt+j nckAS_J Alt+Shift+j nckAC_J Alt+Ctrl+j
 nckA_K Alt+k nckAS_K Alt+Shift+k nckAC_K Alt+Ctrl+k
 nckA_L Alt+l nckAS_L Alt+Shift+l nckAC_L Alt+Ctrl+l
 nckA_M Alt+m nckAS_M Alt+Shift+m nckAC_M Alt+Ctrl+m
 nckA_N Alt+n nckAS_N Alt+Shift+n nckAC_N Alt+Ctrl+n
 nckA_O Alt+o nckAS_O Alt+Shift+o nckAC_O Alt+Ctrl+o
 nckA_P Alt+p nckAS_P Alt+Shift+p nckAC_P Alt+Ctrl+p
 nckA_Q Alt+q nckAS_Q Alt+Shift+q nckAC_Q Alt+Ctrl+q
 nckA_R Alt+r nckAS_R Alt+Shift+r nckAC_R Alt+Ctrl+r
 nckA_S Alt+s nckAS_S Alt+Shift+s nckAC_S Alt+Ctrl+s
 nckA_T Alt+t nckAS_T Alt+Shift+t nckAC_T Alt+Ctrl+t
 nckA_U Alt+u nckAS_U Alt+Shift+u nckAC_U Alt+Ctrl+u
 nckA_V Alt+v nckAS_V Alt+Shift+v nckAC_V Alt+Ctrl+v
 nckA_W Alt+w nckAS_W Alt+Shift+w nckAC_W Alt+Ctrl+w
 nckA_X Alt+x nckAS_X Alt+Shift+x nckAC_X Alt+Ctrl+x
 nckA_Y Alt+y nckAS_Y Alt+Shift+y nckAC_Y Alt+Ctrl+y
 nckA_Z Alt+z nckAS_Z Alt+Shift+z nckAC_Z Alt+Ctrl+z
      
 nckA_0 Alt+0 nckAS_0 Alt+Shift+0  
 nckA_1 Alt+1 nckAS_1 Alt+Shift+1  
 nckA_2 Alt+2 nckAS_2 Alt+Shift+2  
 nckA_3 Alt+3 nckAS_3 Alt+Shift+3  
 nckA_4 Alt+4 nckAS_4 Alt+Shift+4  
 nckA_5 Alt+5 nckAS_5 Alt+Shift+5  
 nckA_6 Alt+6 nckAS_6 Alt+Shift+6  
 nckA_7 Alt+7 nckAS_7 Alt+Shift+7  
 nckA_8 Alt+8 nckAS_8 Alt+Shift+8  
 nckA_9 Alt+9 nckAS_9 Alt+Shift+9  

A few additional ’Alt+____’ definitions using punctuation characters are also defined.

 Constant  Code Constant  Code
 nckA_DUBL Alt+ " nckA_RCHV Alt+ >
 nckA_SING Alt+ ’ nckA_QUES Alt+ ?
 nckA_PLUS Alt+ + nckA_LBRK Alt+ [
 nckA_COMM Alt+ , nckA_BSTR Alt+ \
 nckA_MINU Alt+ - nckA_RBRK Alt+ ]
 nckA_STOP Alt+ . nckA_UNDR Alt+ _
 nckA_FSTR Alt+ / nckA_GRAV Alt+ ‘
 nckA_COLO Alt+ : nckA_LBRC Alt+ {
 nckA_SEMI Alt+ ; nckA_VBAR Alt+ |
 nckA_LCHV Alt+ < nckA_RBRC Alt+ }
 nckA_EQUL Alt+ = nckA_TILD Alt+ ~



Previous: , Up: NCurses Engine   [Contents][Index]

Line Drawing Characters

Text characters may be used for simple line drawings using lines, corners and intersections. These characters can be quite useful in enhancing the readability of data written to a terminal window.

High-level utility methods which are available for line drawing:
See DrawLine method.
See DrawBox method.

Two development methods are available which display examples of the line drawing characters:
See DisplayLineDrawingSet method.
See DisplayLineDrawingExamples method.

The following is a chart of the basic Unicode line-drawing characters and their constant definitions as defined in NCurses.hpp. For additional Unicode graphic characters, please refer to Unicode Consortium’s Unicode Character Table, starting with codepoint U+2500.

┌───────────────────────────┤ Line-drawing Characters ├───────────────────────────┐ │ │ wcsHORIZs 0x2500 wcsLLs 0x2514 wcsTTEEbh 0x252F │ wcsHORIZb 0x2501 wcsLLb 0x2517 wcsTTEEdv 0x2565 │ wcsHORIZd 0x2550 wcsLLd 0x255A wcsTTEEdh 0x2564 │ wcsHDASH2s 0x254C wcsLRs 0x2518 wcsBTEEs 0x2534 │ wcsHDASH2b 0x254D wcsLRb 0x251B wcsBTEEb 0x253B │ wcsHDASH3s 0x2504 wcsLRd 0x255D wcsBTEEd 0x2569 │ wcsHDASH3b 0x2505 wcsLTEEs 0x251C wcsBTEEbv 0x2538 │ wcsHDASH4s 0x2508 wcsLTEEb 0x2523 wcsBTEEbh 0x2537 │ wcsHDASH4b 0x2509 wcsLTEEd 0x2560 wcsBTEEdv 0x2568 │ wcsVERTs 0x2502 wcsLTEEbv 0x2520 wcsBTEEdh 0x2567 │ wcsVERTb 0x2503 wcsLTEEbh 0x251D wcsINSECTs 0x253C │ wcsVERTd 0x2551 wcsLTEEdv 0x255F wcsINSECTb 0x254B │ wcsVDASH2s 0x254E wcsLTEEdh 0x255E wcsINSECTd 0x256C │ wcsVDASH2b 0x254F wcsRTEEs 0x2524 wcsINSECTbv 0x2542 │ wcsVDASH3s 0x2506 wcsRTEEb 0x252B wcsINSECTbh 0x253F │ wcsVDASH3b 0x2507 wcsRTEEd 0x2563 wcsINSECTdv 0x256B │ wcsVDASH4s 0x250A wcsRTEEbv 0x2528 wcsINSECTdh 0x256A │ wcsVDASH4b 0x250B wcsRTEEbh 0x2525 wcsBLOCKl 0x2591 │ wcsULs 0x250C wcsRTEEdv 0x2562 wcsBLOCKm 0x2592 │ wcsULb 0x250F wcsRTEEdh 0x2561 wcsBLOCKd 0x2593 │ wcsULd 0x2554 wcsTTEEs 0x252C wcsBLOCKs 0x2588 │ wcsURs 0x2510 wcsTTEEb 0x2533 │ wcsURb 0x2513 wcsTTEEd 0x2566 │ wcsURd 0x2557 wcsTTEEbv 0x2530 │ │ │ └───────────────────────────────────────────────────────────────────────────────────┘



Note that the ncurses(w) library defines macros for line-drawing characters which live in both the ’narrow’ and ’wide’ versions of the so-called ’Alternate Character Set’ which is part of the basic Linux/UNIX terminalcharacter set. Before there was a Unicode Standard, the Alternate Character Set may have seemed like a good idea; however, use of the ACS_xxx and WACS_xxx line-drawing characters in new code is strongly discouraged. Use the Unicode/UTF-8 characters directly. For more information on the Alternate Character Set, please see Color Attributes.




Next: , Previous: , Up: Top   [Contents][Index]

NcWindow Meta-layer

This chapter covers the NcWindow class definition. The NcWindow class is seldom, if ever used directly, but classes derived from it form the basis of the NcDialog class and its descendants which constitute the application programmer’s interface.

Some of the methods described in this chapter are inherited by the NcDialog class, while others are masked by NcDialog-class method redefinitions.







Next: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Class Overview

The NcWindow meta-layer performs three (3) major functions within the NcDialog API:

  1. NcWindow provides direct access to the underlying ’ncursesw’ C-language library.
  2. NcWindow is the ancestor class for most NcDialog constructs.
  3. NcWindow implements thread safety for the keyboard/mouse input stream and for the text (display) output stream.

Applications should never call NcWindow methods directly, but should call the equivalent NcDialog-class method instead. That’s why the NcWindow-class it is called a ’Metalayer’. The NcWindow-class functionality is discussed ONLY to provide a greater understanding of the API’s underlying structure.




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Public Methods

What follows is a list of all public methods of the NcWindow class.
Methods are arranged in functional groups.

Some of these methods are passthroughs to the NCurses class; others are directly inherited by the NcDialog class or are redefined in the NcDialog class.

        NcWindow Method Name            Chapter Reference    
 NcWindow [constructors] see NcWindow Basics
 ~NcWindow [destructor] 
 OpenWindow 
 WinDimensions 
  
 SetInteriorColor see NcWindow Formatting
 DrawBorder 
 DrawBox 
 DrawLine 
  
 WriteString see NcWindow Display Output
 WriteChar 
 WriteParagraph 
 ClearWin 
 RefreshWin 
 ClearLine 
 GetCursor 
 SetCursor 
  
 GetKeyInput see NcWindow Keyboard Input
 meEnableStableMouse 
 meEnableMouse 
 meDisableMouse 
 meMouseEnabled 
 meSetEventTypes 
 meGetEventTypes 
 meSetClickInterval 
 meGetClickInterval 
 meFlushEventQueue 
 meAutoConvert 
 meAutoFilter 
  
 KeyPeek see NcWindow Passthroughs
 UngetKeyInput 
 FlushKeyInputStream 
 GetCursorState 
 SetCursorState 
 RestoreCursorState 
 GetKeyProcState 
 SetKeyProcState 
 GetKeyDelay 
 SetKeyDelay 
 ScreenDimensions 
 Hibernate 
 Wake 
 Get_NCurses_Version 
 Get_nclibrary_Version 
  
 PaintData see NcWindow Data Scrolling
 PaintMenuData 
 RepaintData 
 EraseData 
 ScrollData 
 ScrollMenuData 
 ScrollView 
 GetHilightIndex 
 Track2Top 
 Track2Bottom 
 Track2Next 
 Track2Previous 
 Track2Item 
  
 Get_NcWindow_Version see NcWindow Utility Methods
 DebugMsg 



Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Basics




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Formatting




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Display Output




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Keyboard Input

The meta-layer keyboard input method and the application-layer mouse interface are implemented in the NcWindow class to provide thread-safe operation. The mouse method group is directly inherited by the NcDialog class.

Please see GetKeyInput method for a description of the application-layer keyboard input method.

Please see Mouse Configuration for full descriptions and notes on configuring mouse input.






Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Data Scrolling

One of the basic methods for viewing and selection of data items is scrolling the items up and down in a window. The following methods implement the meta-layer of data scrolling. For application-layer scrolling, please refer to the NcDialog-class user-interface control objects:

see Scrollbox Controls
see Scrollext Controls
see Dropdown Controls
see Menuwin Controls

Note than an example of scrolling using the NcWindow methods can be found in Test03 of the Dialog1 test application.






Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Passthroughs

Pass-through methods to the NCurses class. These methods are fully described in the indicated chapter on the NCurses Engine. Please note however, that the NcWindow passthroughs are the thread-safe versions of the low-level methods.




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Utility Methods




Next: , Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Hotkey Selection

A ’hotkey’ is a special key combination which is used as a shortcut access to a particular application function or information item. For instance, the 'F01' function key is traditionally used to invoke the application’s 'Help' information.

The NcWindow class implements hotkeys for window objects and for individual data items in menus. The NcDialog derived class builds on this basic hotkey functionality.

A complete discussion of using hotkeys may be found in the NcDialog chapter on hotkey definition and uses.
See Select Control via Hotkey.




Previous: , Up: NcWindow Meta-layer   [Contents][Index]

NcWindow Thread Handling

The NcDialog API thread-safety mechanism is implemented in the NcWindow-class meta-layer. This thread-safe operation carries through all classes derived from the NcWindow class. For a full discussion of multithreaded access to the NcDialog API, please see Thread Safety.




Next: , Previous: , Up: Top   [Contents][Index]

NcDialog Class Definition

This chapter describes in detail the NcDialog class and the control objects with which a dialog-based application is populated.







Next: , Up: NcDialog Class Definition   [Contents][Index]

NcDialog Class Overview

In previous chapters, we have examined the interface to the underlying ’ncursesw’ C-language library which implements the low-level curses interface for GNU/Linux systems.

In this chapter, we will describe the application layer interface. This is where the action is, and the information presented here provides nearly everything you need to create sophisticated, fast and bulletproof applications in a terminal environment.

We outline all the public methods of the NcDialog class, how they are used and when they are used. There is always a learning curve when working with a new tool, but we hope that once you have read this chapter, you will have no trouble easily and quickly building sophisticated applications without the kind of frustrating and time-consuming research which would be needed for some gigantic GUI API.

If you find that any of the concepts presented here are confusing or difficult to understand, please see Operational Overview for a review of basic concepts.


Eye Candy for Techies

We will present several screen captures for various dialogs and the controls they contain. We apologize that the Texinfo documentation system info-format document cannot render them in color, so some detail may be lost; but note that the HTML-format version of this document shows screen captures in eight(8) colors.

The screen capture below is the dialog we create in the chapter,
                  see Your First Application


╔══════════════════════╣ NcDialog - Our First Effort ╠═══════════════════════╗ Hello World! Favorite Linux flavor: Ubuntu! (Unity disabled)! Green Messages: [◆] Yellow Messages: [ ] My favorite Linux flavor is: Ubuntu! (Unity disabled)! DONE ╚══════════════════════════════════════════════════════════════════════════════╝

Note that the screen captures in this document use a simplified, but essentially accurate
color attribute map to compress the captured data. For details, see CSS for screenshots.





Next: , Previous: , Up: NcDialog Class Definition   [Contents][Index]

NcDialog Public Methods

        NcDialog Method Name            Chapter Reference    
 NcDialog [constructor] see Dialog Window Methods
 ~NcDialog [destructor] 
 OpenWindow 
 RefreshWin 
 ClearWin 
 SetDialogTitle 
 HideWin 
 MoveWin 
 SetDialogObscured 
 GetDialogPosition 
 GetDialogDimensions 
 EstablishCallback 
  
 NextControl see Using Dialog Controls
 PrevControl 
 GetCurrControl 
 ControlActive 
 ConnectControl2Border 
 DrawLabelsAsRTL 
 DrawContentsAsRTL 
  
 EditPushbutton see Pushbutton Controls
 SetPushbuttonText 
 EnablePushbuttonBorder 
  
 EditRadiobutton see Radiobutton Controls
 GetRadiobuttonState 
 SetRadiobuttonState 
 GroupRadiobuttons 
 GetRbGroupSelection 
 SetRbGroupSelection 
  
 EditTextbox see Textbox Controls
 SetTextboxText 
 GetTextboxText 
 ExtendedTextboxData 
 DisplayTextboxTail 
 DisplayTextboxMessage 
 VerifyTbText 
 TextboxAlert 
 Encode_URI 
 Decode_URI 
 SetTextboxCursor 
 SetTextboxReservedKeys 
 SetLocalClipboard 
 GetLocalClipboard 
 GetLocalClipboard_Bytes 
 GetLocalClipboard_Chars 
 SetTextboxInputMode 
 IsOverstrike 
  
 EditBillboard see Billboard Controls
 SetBillboard 
 GetBillboard 
 Append2Billboard 
 Insert2Billboard 
 ClearBillboard 
 GetBillboardColors 
 SetBillboardColors 
  
 EditScrollbox see Scrollbox Controls
 GetScrollboxSelect 
 SetScrollboxSelect 
  
 EditScrollext see Scrollext Controls
 ViewScrollext 
 GetScrollextSelect 
 SetScrollextSelect 
 SetScrollextText 
 RefreshScrollextText 
 MoveScrollextHighlight 
  
 Edit Dropdown see Dropdown Controls
 GetDropdownSelect 
 SetDropdownSelect 
  
 EditMenuwin see Menuwin Controls
 GroupMenuwinControls 
 AttachMenuwinSubmenus 
 HideMenuBar 
 ShowMenuBar 
 MenuBarVisible 
 PositionContextMenu 
 SetActiveMenuItems 
  
 EditSpinner see Spinner Controls
 GetSpinnerValue 
 SetSpinnerValue 
 SetSpinnerLimits 
  
 WriteString see NcDialog Display Output
 WriteChar 
 WriteParagraph 
 ClearLine 
 ClearArea 
 DrawLine 
 DrawBox 
 GetCursor 
 SetCursor 
 GetCursorState 
 SetCursorState 
 RestoreCursorState 
  
 GetKeyInput see Direct Keyboard Input
 KeyPeek 
 UngetKeyInput 
 FlushKeyInputStream 
 GetKeyProcState 
 SetKeyProcState 
 GetKeyDelay 
 SetKeyDelay 
  
 meEnableStableMouse see Mouse Configuration
 meEnableMouse 
 meDisableMouse 
 meMouseEnabled 
 meSetEventTypes 
 meGetEventTypes 
 meSetClickInterval 
 meGetClickInterval 
 meFlushEventQueue 
 meAutoConvert 
 meAutoFilter 
  
 InfoDialog see Utility Methods
 DecisionDialog 
 UserAlert 
 Hibernate 
 Wake 
 ShellOut 
 PseudoShell 
 ScreenDimensions 
 Get_NcDialog_Version 
 Get_NcWindow_Version 
 Get_NCurses_Version 
 Get_nclibrary_Version 
  
 Dump_uiInfo see Development and Debugging
 CaptureDialog 
 DisplayRadiobuttonTypes 
 DisplayAltCharacterSet 
 DisplayWideCharacterSet 
 DisplayLineDrawingSet 
 DisplayLineDrawingExamples 
 DebugMsg 



Next: , Previous: , Up: NcDialog Class Definition   [Contents][Index]

Dialog Window Methods

This chapter describes the NcDialog methods that affect the NcDialog-class object (dialog window) as a whole.




Next: , Previous: , Up: NcDialog Class Definition   [Contents][Index]

Dialog Control Objects

A dialog control object is a logical device used for collecting input from the user and displaying information to the user. Several different types of controls are available, so you can present and collect information in the way that is most convenient for that type of information.







Next: , Up: Dialog Control Objects   [Contents][Index]

Using Dialog Controls

All methods and data of dialog control objects are private.
This ensures data integrity and robust code execution.

Access to the data and functionality of control objects is through NcDialog-class methods which carefully range check all input parameters and validate all data returned from the target control.

Most of the methods in this chapter apply only to one, specific control type. However, there are several configuration and navigation methods which apply to all (or multiple) control types. These are listed below.


Configuration Of and Navigation Among Control Objects




Next: , Previous: , Up: Dialog Control Objects   [Contents][Index]

Pushbutton Controls

The DialogPushbutton-class user interface controls allow the user to select the activity the Pushbutton represents.

Please see InitCtrl class for information on defining and creating a DialogPushbutton-class user interface control.






Next: , Previous: , Up: Dialog Control Objects   [Contents][Index]

Radiobutton Controls

The DialogRadiobutton-class user interface controls allow the user to select between binary options: On and Off, Yes and No, Active and Inactive. Radiobutton controls may also be grouped to form an ’Exclusive OR’ selection, that is exactly one(1) of the options in the XOR group is active at any given time.

Please see InitCtrl class for information on defining and creating a DialogRadiobutton-class user interface control.






Next: , Previous: , Up: Dialog Control Objects   [Contents][Index]

Textbox Controls

The DialogTextbox-class user interface controls allow the user to enter text data. Textbox controls may also be configured to display temporary messages without disturbing the underlying data.

Textbox controls may be of any reasonable height and width, and have a data capacity of gsMAXBYTES (approximately four kilobytes).

Textbox controls accept both Left-To-Right (LTR) input and Right-To-Left (RTL) input, as well as right-justified input.

Please see InitCtrl class for information on defining and creating a DialogTextbox-class user interface control.






Next: , Previous: , Up: Dialog Control Objects   [Contents][Index]

Billboard Controls

The DialogBillboard-class user interface controls display text data to the user, and the data may be dynamically updated under program control to keep the user informed.

Billboard controls are read-only, that is the user may not directly modify the contents of the control.

Please see InitCtrl class for information on defining and creating a DialogBillboard-class user interface control.




Examples

Working code for the following examples may be found in test application Dialogw, Test07 ’Billboard Demonstration’.

╔════╣ Poetry Corner ╠════╗ Roses are red, Violets are blue, That's why I picked some just for you! --- Set Billboard Contents NEXT DONE ╚═══════════════════════════╝

╔════╣ Poetry Corner ╠════╗ Violets are blue, That's why I picked some just for you! -- -- -- -- -- -- -- --- Append a Line (1) NEXT DONE ╚═══════════════════════════╝

╔════╣ Poetry Corner ╠════╗ -- -- -- -- -- -- -- Violets are blue, That's why I picked some just for you! --- Insert Line at Top NEXT DONE ╚═══════════════════════════╝

╔════╣ Poetry Corner ╠════╗ -- -- -- -- -- -- -- Violets are blue, Forget the flowers, just for you! --- Replace Third Line NEXT DONE ╚═══════════════════════════╝

╔════╣ Poetry Corner ╠════╗ -- -- -- -- -- -- -- Violets are blue, the flowers, give jewelry! just for you! --- Extend Third Line NEXT DONE ╚═══════════════════════════╝

Comprehensive examples of Billboard usage are available in the various test applications. See Dialog4, Test05 and Test06.




Scrollbox Controls

The DialogScrollbox-class user interface controls present a list of data items to the user in the form of a scrolling list. The list may be of any reasonable length, with the number of simultaneously-visible items determined by the vertical dimension of the control.

Data items should be of equal width (column count), and the contents of the list are fixed at the time the control is created.

Please see InitCtrl class for information on defining and creating a DialogScrollbox-class user interface control.






Scrollext Controls

The DialogScrollext-class user interface controls presents a list of data items to the user in the form of a scrolling list. The list may be of any reasonable length, with the number of simultaneously-visible items determined by the vertical dimension of the control.

Scrollext-class controls are very similar to Scrollbox-class controls (see previous chapter), except that the data live in application space rather than in the control’s private memory space.

Data items should be of equal width, and the content and color attribute of each data item (but not the number of data items) may be dynamically updated in response to user action.

Please see InitCtrl class for information on defining and creating a DialogScrollext-class user interface control.




ssetData Class

The ssetData class is used to set the text and color attribute data for the Scrollext-class user interface control, or to modify the the existing data.


//* Structure for passing data to the SetScrollextText() method *
class ssetData
{
   public:
   //* Default constructor *
   ssetData ( const char** dt = NULL, const attr_t* dc = NULL, int di = ZERO,
              int hi = ZERO, bool hs = true ) :
               dispText(dt), dispColor(dc), dispItems(di),
               hlIndex(hi), hlShow(hs) {}
   const char** dispText ;  //* Pointer to an array of display string pointers
   const attr_t* dispColor ;//* Pointer to an array of color attributes 
                            //*  (one for each display string)
   int      dispItems ;     //* Number of display items in above arrays
   int      hlIndex ;       //* Index of display item initially highlighted
   bool     hlShow ;        //* Show/hide the highlight for current item
} ;



The following is an excerpt from the clipboard browser section of our FileMangler utility. Space is allocated for the text and color attribute data; then the data are formatted, and finally are loaded into a Scrollext control object for the user to play with.

//* If there is file data to display 'Just Do It' (tm Nike) *
char*       blkPtr ;
ssetData    sData( NULL, NULL, tlFiles, ZERO, false ) ;
if ( tlFiles > ZERO )
{
   //* Space for display strings (there is extra space for each string) *
   blkPtr = new char[dAlloc * dialogCols] ;
   //* Space for pointers to strings *
   sData.dispText  = new const char*[dAlloc] ;
   //* Space for color attributes *
   sData.dispColor = new attr_t[dAlloc] ;

   //* Create display strings *
   this->bcbStandardView ( blkPtr, sData ) ;

   //* Send the data to the scrolling control *
   dp->SetScrollextText ( scrollSE, sData ) ;
}  // if(tlFiles>ZERO)

//* Make everything visible *
dp->RefreshWin () ;
┌───────┤ FileMangler - v:0.0.33 (c)2005-2015 [Press F2 for Menu] ├───────┐ │Status: Ctrl+Q=Quit, Shift+F1=Help, F2=Menu ├────────────────────────────────────────────────────────────────────────────┤ /home/xiao/software/NcDialog/Dialog ├───────────────────────────┤ Browse Clipboard ├───────────────────────────┤ │ Source Dir: /home/xiao/software/NcDialog/Dialog │ Files on Clipboard: 25 Byte Total, All Files: 2,569,748 │ Clipboard Storage Bytes Allocated: 12,240 Pending Operation: Copy Detailed View Close Clear Clipboard │ TYPE FILENAME (Tab to list, then scroll keys to view) ├──────────────────────────┤ Files On Clipboard ├──────────────────────────┤ REG Dialog1.cpp REG NCurses.cpp REG NcDialog.cpp REG NcKey.cpp REG NcWindow.cpp REG NcdControl.cpp REG NcdControlBB.cpp REG NcdControlDD.cpp REG NcdControlMW.cpp REG NcdControlPB.cpp REG NcdControlRB.cpp REG NcdControlSB.cpp └────────────────────────────────────────────────────────────────────────────┘



Dropdown Controls

The DialogDropdown-class user interface controls presents a list of data items to the user in the form of a scrolling list. The list may be of any reasonable length, with the number of simultaneously-visible items determined by the vertical dimension specified for the ’expanded’ state.

Dropdown-class controls are very similar to Scrollbox-class controls described in a previous chapter, except that when the control is in the inactive (collapsed) state, it uses only three(3) display rows, displaying only the currently-selected item. When in the active (expanded) state, it performs exactly like a Scrollbox-class control.

Data items should be of equal width, and the contents of the list are fixed at the time the control is created.

Please see InitCtrl class for information on defining and creating a DialogDropdown-class user interface control.



  • short EditDropdown ( uiInfo& info );
      Input  :
         info : uiInfo class (by reference) - initial values ignored
                EXCEPTION: If info.viaHotkey != false, on entry, 
                           expand the control immediately on entry.
                (see description of uiInfo class for values returned)
    
      Returns: 
         index of control that currently has the input focus
    

    If the control with input focus is of type dctDROPDOWN, call this method to get user’s key input.

    Allows user to edit which item in the control is ’selected’ (highlighted).

    • On entry, the control is in its ’collapsed’ state i.e. only the currently ’selected’ data item is visible (see example below). User input is monitored to determine whether to expand the control for item selection, or to move on to another control without making a selection.
    • If user presses the ENTER key or SPACE key (or PAGE_DOWN), then the control is ’expanded’ for user selection.

      When the user has selected an item OR has indicated a move to another control, then the control is returned to the ’collapsed’ state. Thus, Dropdown controls are in the ’expanded’ state only when under edit.

    • Special case: If the ’viaHotkey’ flag is set (true) on entry, then the control will be expanded immediately.

    The EditDropdown method retains control of input until:

    • ’Collapsed’ state:
      • ENTER or SPACE key pressed.
        The control is expanded so user can select a new line item (see below).
      • TAB, SHIFT+TAB, LEFT_ARROW, RIGHT_ARROW,
        UP_ARROW or DOWN_ARROW key pressed.
        The input focus is shifted to the indicated control object. The Dropdown control has not been expanded, and no selection has been made.
      • A ’Hotkey’ combination has been detected.
        If the hotkey belongs to the current control, then control is expanded as with the ENTER key.

        Otherwise, the input focus has been shifted to another control object, and no selection has been made.

      • The nckESC (Escape) key pressed.
        The Escape key makes no sense in this context, so it is converted to the nckTAB key.
    • ’Expanded’ state:
      • ENTER or SPACE key pressed.
        The highlighted item has been actively selected, indicating that edit is complete. The ’dataMod’ flag of the returned data indicates whether the highlight position has been modified.

        Note: To determine whether the user has re-selected the original item, check for the Enter key using the following test:

           if ( Info.dataMod != false || Info.keyIn == nckENTER )
           {
              // do stuff here...
           }
        
      • TAB, SHIFT+TAB, LEFT_ARROW or RIGHT_ARROW key pressed.
        The highlighted item has been implicitly selected, indicating that edit is complete. The ’dataMod’ flag of the returned data indicates whether the highlight position has been modified.
      • A ’Hotkey’ combination has been detected.
        The edit is complete. If the Hotkey belongs to the current control, it is processed like the ENTER key.

        Otherwise, the input focus has been shifted to another control object, and the ’dataMod’ flag of the returned data indicates whether the highlight position has been modified.

      • The nckESC (Escape) key pressed.
        It is assumed that the user is in panic mode. The highlighted item is returned to its original position, and control is returned to the application as if the user had pressed the nckTAB key (’dataMod’ flag == ’false’).

    Please see uiInfo class for a discussion of the values returned from the Edit method.



  • short GetDropdownSelect ( short cIndex );
    Input  :
         cIndex : index number of source control
    
    Returns:
         index of 'selected' (highlighted) item
         returns ERR if:
           a) specified control is not of type dctDROPDOWN
    

    Returns the index of the highlighted item in the specified dctDROPDOWN control.



  • short SetDropdownSelect ( short cIndex, short selMember );
    Input  :
         cIndex    : index number of target control
         selMember : index of item to be set as 'selected'
    
    Returns:
         index of 'selected' member
         returns ERR if:
           a) invalid item index specified
           b) specified control currently has the input focus
           c) specified control is not of type dctDROPDOWN
    

    Set ’selected’ (highlighted) item in specified dctDROPDOWN control.


Dropdown Examples

┌──────────────────────────┤ National Football League ├──────────────────────────┐ Western Division North and South Divisions (expands downward) (expands up and down) ┌──────────────────────┐ San Francisco 49ers └──────────────────────┘ ┌──────────────────────────┐ Chicago Bears (N) └──────────────────────────┘ ┌──────────────────────┐ │ Miami Dolphins ◆ └──────────────────────┘ Eastern Division (expands upward) DONE └──────────────────────────────────────────────────────────────────────────────────┘

"Western Division" Dropdown control has focus, but is still in the collapsed state.


┌──────────────────────────┤ National Football League ├──────────────────────────┐ Western Division North and South Divisions (expands downward) (expands up and down) ┌──────────────────────┐ Kansas City Chiefs Oakland Raiders ┌──────────────────────────┐ St. Louis Rams Chicago Bears (N) San Diego Chargers └──────────────────────────┘ ┌──────────────────────┐ San Francisco 49ers │ Miami Dolphins ◆ Seattle Seahawks └──────────────────────┘ └──────────────────────┘ Eastern Division (expands upward) DONE └──────────────────────────────────────────────────────────────────────────────────┘

"Western Division" Dropdown control is expanded (downward).


┌──────────────────────────┤ National Football League ├──────────────────────────┐ Western Division North and South Divisions (expands downward) (expands up and down) ┌──────────────────────┐ ┌──────────────────────────┐ San Francisco 49ers Atlanta Falcons (S) └──────────────────────┘ Baltimore Ravens (N) Carolina Panthers (S) Chicago Bears (N) ┌──────────────────────┐ Cincinnati Bengals (N) │ Miami Dolphins ◆ Cleveland Browns (N) └──────────────────────┘ └──────────────────────────┘ Eastern Division (expands upward) DONE └──────────────────────────────────────────────────────────────────────────────────┘

"North and South Divisions" Dropdown control is expanded (upward and downward from center point).


┌──────────────────────────┤ National Football League ├──────────────────────────┐ Western Division North and South Divisions (expands downward) (expands up and down) ┌──────────────────────┐ ┌──────────────────────┐ Buffalo Bills San Francisco 49ers Dallas Cowboys └──────────────────────┘ ┌──────────────────────────┐ Miami Dolphins Chicago Bears (N) New England Patriots └──────────────────────────┘ New York Giants New York Jets └──────────────────────┘ Eastern Division (expands upward) DONE └──────────────────────────────────────────────────────────────────────────────────┘

"Eastern Division" Dropdown control is expanded (upward).


Working code for these examples is located in the Dialogw test application, Test07, ’Dropdown Demonstration’.




Menuwin Controls

The DialogMenuwin-class user interface controls are, as the name indicates, menus which may be opened, closed, linked into multi-level groups (MenuBar objects), or made invisible.

When you create the control, you specify its appearance in both the ’collapsed’ (inactive) state and the ’expanded’ (active) state. When in the active state, Menuwin controls behave much like Scrollbox-class controls (described in a previous chapter), but with enhanced features such as wrap-around scrolling, dynamic activation/deactivation of individual data items and cascading sub-menus.

Please see InitCtrl class for information on defining and creating a DialogMenuwin-class user interface control.

Please see Menuwin notes, below for further discussion of creating and configuring menus.



  • short EditMenuwin ( uiInfo& info );
      Input  :
         info : uiInfo class (by reference) - initial values ignored
                EXCEPTION: If info.viaHotkey != false, on entry, 
                           expand the control immediately on entry.
                (see description of uiInfo class for values returned)
    
      Returns: 
         index of control that currently has the input focus
    

    If the control with input focus is of type dctMENUWIN, call this method to get user’s key input. This method handles standalone (independent) menus and MenuBar groups. (For edit of ’context menus’, see next method).

    • On entry, the control is in its ’collapsed’ state i.e. only the title of the menu is visible (see example below). User input is monitored to determine whether to expand the control for item selection, or to move on to another control without making a selection.
    • If user presses the ENTER key or SPACE key (or PAGE_DOWN), then the control is ’expanded’ for user selection.
    • Special case: If the ’viaHotkey’ flag is set (true) on entry, then the control will be expanded immediately.
    • During user selection, the sequence of events depends upon:
         a) whether the menu is a standalone menu or a MenuBar member
         b) whether sub-menus have been attached to the parent menu
         c) whether the user selects an item from the menu.
      
    • Note on Hotkeys
      Because each menu item may optionally have an assigned Hotkey, all Hotkeys associated with external control objects are ignored while editing an ’expanded’ Menuwin control.

    The EditMenuwin method retains control of input until:

    • For a menu which is a member of a MenuBar:
      A MenuBar is a group of DialogMenuwin controls for which the Edit method retains control until either:
      • The user has selected an item from any member control in the MenuBar using the ENTER key or SPACE key.
      • The user has signalled a move to a control which is outside the MenuBar group (no menu item selected).
    • For a menu which is not a member of a MenuBar:
      • The user has selected an item from the menu using the ENTER key or SPACE key.
      • The user has signalled a move to a different control (no menu item selected).

    For working menu examples, please see Test10 of the Dialog1 test application, or see the examples below (see Menuwin examples)..

    Please see uiInfo class for a discussion of the values returned from the Edit method.



  • short EditMenuwin ( short cIndex, uiInfo& info );
      Input  :
         cIndex : index of a context (normally invisible) Menuwin control
         info   : uiInfo class (by reference) - initial values ignored 
    
      Returns: 
         index of control that currently has the input focus
    

    For a context menu (control of type dctMENUWIN which is normally inactive and invisible), call this method to get user’s key input.

    For standalone menus and MenuBar groups, please see previous method.

    This method does more than just collect user input.

     1. activate the menu (make it accessible to user input)
     2. make the menu visible
     3. get user’s key input
     4. hide the menu
     5. deactivate the menu (make it not accessible to user input)
    

    Note that in a GUI application, context menus are normally accessed by using the mouse (press Button #3). However, this option is not available to console applications because Button #3 is reserved by the GUI. For this reason, a context menu will typically be activated under direct program control in response to some user action.

    The context-menu version of the EditMenuwin method retains control of input until:

    • the user has selected an item from the menu using the ENTER key or SPACE key.
    • the user presses the TAB, SHIFT+TAB, LEFT_ARROW, RIGHT_ARROW or ESCAPE key indicating that no menu item has been selected.


  • short GroupMenuwinControls ( const short indexList[],
                                 wchar_t auxHotkey=ZERO );
      Input  :
         indexList: array of control index numbers (-1 ends list)
         auxHotkey: (optional, default==ZERO)
                    If a value is specified, it is an auxilliary hotkey
                    that toggles the MenuBar's visibility/invisibility.
                    Note that this keycode will be recognized only when
                    the input focus is not on a member of the MenuBar group.
    
      Returns:
         OK if successful
         returns ERR if:
           a) if index list contains an invalid index number
           b) an index in the list references a control which is not of
              dctMENUWIN type
           c) an index in the list references a sub-menu or context menu
              or a menu which is already a member of another MenuBar group
    

    Establish a MenuBar (a group of DialogMenuwin class objects).

    The controls of a MenuBar may be edited as a group by a call to the EditMenuwin method (see above).

    The list of group members is passed as an array of index numbers for the DialogMenuwin objects. List is terminated by a negative 1 (-1).
    Example:
    short List[] = { 4, 5, 6, -1 } ;
    This MenuBar has three top-level menus. Sub-menus may be added using the AttachMenuwinSubmenus method (see below).

    NOTE: Sub-menus and other context menus MAY NOT be specified as members of a MenuBar. Only visible, top-level menus, may be specified.

    NOTE: If specified indices are not consecutive, anomolous display behavior may result, so members of a group should have consecutive indices. (Sub-menus need not have sequential indices.)



  • short AttachMenuwinSubmenus ( short parentMenu,
                                  const short submenuList[] );
      Input  :
         pMenu  : index of parent menu of type dctMENUWIN to
                  which sub-menus will be attached.
         smList : array of index numbers for the DialogMenuwin
                  context menu objects to be associated with
                  each data item in the parent menu.
    
      Returns:
         OK if successful
         returns ERR if:
           a) invalid parent menu index number
           b) any specified sub-menu not defined as a context menu
           c) any referenced control is not of dctMENUWIN type
    

    Establish a connection between display elements in a dctMENUWIN control and context menus that will act as submenus for those elements.

    Note: A context menu is a dctMENUWIN control that has been defined with a NULL string label, and is therefore invisible when in its collapsed state. Context menus are always in the collapsed state unless they are currently under edit.

    Please see Menuwin examples below for more information.



  • short HideMenuBar ( short mbIndex );
      Input  :
         mbIndex: index of any top-level member control in a MenuBar
    
      Returns:
          OK if successful
          returns ERR if:
            a) mbIndex is not a valid control index
            b) specified control is not a dctMENUWIN control
            c) specified control is not a member of a MenuBar
            d) all active controls in the dialog are members of the MenuBar
    

    Make a MenuBar (DialogMenuwin control group) invisible.

    MenuBar group must have been previously established through a call to the GroupMenuwinControls method.

    Notes

    • An application may wish to hide a MenuBar to provide additional screen area, prevent user input, etc.
    • After being hidden, the controls in the MenuBar cannot be accessed by the navigation keys (TAB, SHIFT+TAB, etc.), nor under program control (NextControl / PrevControl methods).
    • Members of a hidden MenuBar can still be accessed and made visible again via the member controls’ respective hotkeys (if any).

      If a valid hotkey for any member of the MenuBar is detected, the MenuBar is made visible (see ShowMenuBar method) and the input focus is moved to that control.

    • Additionally, if caller specified an auxilliary hotkey for accessing the MenuBar when defining the DialogMenuwin group, then detection of that hotkey will toggle the visibility/invisibility of the MenuBar, and when MenuBar becomes visible via the auxilliary hotkey, the first (lowest index) member control in the MenuBar will get the input focus.
    • Notes on input focus:
      If this method is called while a member of the specified MenuBar has the input focus, we must move the focus to a control that is not a member of the group. At least one active control that DOES NOT belong to the MenuBar must be defined for this dialog in order to make the MenuBar invisible, because input focus must be somewhere after the specified MenuBar disappears. Thus, we force the focus out of the MenuBar to another active control before we hide the MenuBar.

      By enforcing this action we ensure that there is at least one active control outside the MenuBar. There is no guarantee that the application will be happy with our chosen focus position, so it is better if the application moves the focus out of the MenuBar BEFORE calling this method.

      If the application wants to hide a MenuBar containing all the active controls in the dialog, there is a work-around. Define a dummy active Pushbutton control which is active, but does nothing. Such a Pushbutton can be effectively invisible if it is one character wide, and that character is a SPACE of the same color as the dialog background. The application would then move the focus to that control before hiding the MenuBar. The user would never know the extra Pushbutton control existed.



  • short ShowMenuBar ( short mbIndex, bool setFocus );
      Input  :
         mbIndex : index of any top-level member control in a MenuBar
         setFocus: if 'true', immediately move the input focus to the
                              control indicated by mbIndex.
                   if 'false', input focus is not changed
    
      Returns:
         index of control with input focus.
    
         NOTE: This method will do nothing if:
           a) mbIndex is not a valid control index
           b) the specified control is not a dctMENUWIN control
           c) the specified control is not a member of a MenuBar
           d) If setFocus==true AND referenced MenuBar is already 
              visible, input focus is simply moved to the control 
              specified by mbIndex.
    

    For a MenuBar (group of DialogMenuwin controls) previously hidden by a call to the HideMenuBar method (above), make the MenuBar visible.

    Notes

    • This method is equivalent to the user pressing the auxilliary hotkey combination (optionally) specified when the MenuBar was established, except that the auxilliary hotkey always references the first menu in the MenuBar group..
    • Notes on input focus:
      The control that is RECOMMENDED to receive the input focus on return is determined by the method used to access the MenuBar.
      • If user presses a hotkey associated with one of the individual controls in the MenuBar, focus goes to that control.
      • If user presses the auxilliary hotkey (if any) specified when the MenuBar was established, the focus goes to the first (lowest index) control of the MenuBar.


  • bool MenuBarVisible ( short mbIndex );
      Input  :
         mbIndex : index of any top-level member control in a MenuBar
    
      Returns:
         current state of a MenuBar:
           'true' if specified MenuBar is visible
           'false' if MenuBar is invisible
                   OR
                   a) if mbIndex is not a valid control index
                   b) if specified control is not a dctMENUWIN control
                   c) if specified control is not a member of a MenuBar
    

    Returns the current Visible / Invisible state of the specified MenuBar.



  • short PositionContextMenu ( short cIndex, short offY, short offX );
      Input  :
         cIndex : index of control to be positioned
         offY   : Y offset from upper left of parent dialog
         offX   : X offset from upper left of parent dialog
    
      Returns:
         OK if successful
         returns ERR if:
           a) specified control is not a context menu
           b) the menu is already expanded (visible)
           c) specified offset puts menu on or beyond dialog border
    

    Set the position of a dctMENUWIN control which acts as a context menu.

    Context menus are DialogMenuwin class objects that are invisible when in the ’collapsed’ state, which is any time they are not being edited by the user. In a GUI application, this would be the menu that opens when the right mouse button is pressed. A context menu may be opened anywhere within the dialog window. A context menu is created by instantiating a dctMENUWIN object with a zero-length label.



  • short SetActiveMenuItems ( short cIndex, const bool aFlags[],
                               const attr_t* aColors=NULL, bool hide=false );
      Input  :
         cIndex : index of dctMenuwin control
         aFlags : an array of boolean values, one for each menu item
                  'true' == active, 'false' == inactive
                  Note: At least one menu item must be 'active'.
                        (To deactivate an entire menu, see ControlActive())
         aColors: (optional, NULL pointer by default)
                  if specified, pointer to an array of color attributes,
                  one for each menu item.
         hide   : (optional, false by default)
                  if 'true' hide (make invisible) all inactive menu items
                  [NOTE: 'hide' option is not currently implemented]
    
      Returns:
         OK if successful
         returns ERR if:
           a) cIndex is not a valid control index
           b) cIndex does not reference a control of dctMENUWIN type
           c) target control has input focus
    
    Set which menu items in a dctMENUWIN control are active (user-selectable). 
    These changes are allowed only for a control that currently does not have 
    the input focus.
    
    A dctMENUWIN control has a fixed number of menu items determined at the 
    time the control is instantiated; however, some menu items may have no 
    meaning or must be disallowed in certain contexts. Menu items may therefore 
    be activated or deactivated as necessary, and the item's color attribute 
    can be adjusted to visually indicate the item's current status.
    
    Note on beautification: If the 'active' menu color uses a 'reverse' 
    attribute, then the 'inactive' color should also use a 'reverse' attribute 
    to avoid confusion with the color of the highlighted item. For an example 
    of using this method, please see the examples below.
    
    This method can also be used to change the color attribute for each menu 
    item with-or-without affecting the active/inactive status of each item.
    



Menuwin Examples

A Menuwin control is a control object which contains scrollable data from which the user can select one line item. Menuwin controls have two ’states’:

’collapsed’ : only the menu title is visible
Menuwin controls are in the ’collapsed’ state unless the user is accessing the line items of the control

’expanded’  : both the menu title and the scrolling data list are visible
The user has requested access to the line items of the control, that is, the menu or one of its optional sub-menus currently has the input focus.


Menus come in four(4) configurations:

  1. Independent, stand-alone menu
    This is a single, top-level menu, with or without attached sub-menus.
  2. MenuBar group
    A MenuBar is a series of top-level menus, with or without attached sub-menus. The members of a MenuBar group are independent menus which have been collected into a tightly integrated group.
    See GroupMenuwinControls method.

    Normally, the menus of a MenuBar are positioned horizontally adjacent to one another (but this positioning is not enforced).

    ┌───────┤ My Killer App - (c) 2015 The Software Samurai ├───────┐ File Edit View Util Help │ │

    To reduce visual clutter, the entire MenuBar optionally can be set invisible when not in use.

  3. Context menu
    A context menu is an independent menu which is instantiated as invisible, and which becomes visible and active only within a specific context. A menu is defined as a context menu by setting its label to zero width i.e. an empty string: "".

    The menu becomes visible to the user only under a pre-defined set of circumstances (the context) and is completely invisible and inaccessible to the user at all other times. The application activates a context menu in response to this context being detected.

  4. Sub-menu attached to a line item of a parent menu
    Sub-menus are actually context menus, normally invisible, but activated in response to user selection of the associated line item of the parent menu.

    A sub-menu is visually in contact with the parent window (ideally, overlapping the right border of the parent), and has been associated with a line item of the parent menu.
    See AttachMenuwinSubmenus method.

    Visual association with the parent menu is not enforced but is recommended as the visual cue to which most users are accustomed.

    It is customary to visually indicate to the user that the menu item is associated with a sub-menu. The NcDialog API examples use the '>' character at the trailing edge of the item to indicate the associated sub-menu. (see examples below)

    Sub-menus may be cascaded to an unreasonable depth; however, a depth of more than three has been shown to confuse even an experienced user.

    The user activates a sub-menu by:

      a) highlighting the parent’s menu item and pressing 
         the ENTER or SPACE key or the RIGHT_ARROW key
      b) pressing the Hotkey (if any) associated with the 
         parent’s menu item
    

    The user returns from the sub-menu to the parent menu by:

      a) making a selection 
         In this case, the entire cascade of sub-menus is closed
         and then the parent menu is closed.
      b) pressing the LEFT_ARROW key
         In this case, the sub-menu is closed and the input focus
         returns to the (still open) parent menu.
    

    Note: Sub-menus can perform a dual function: a) as a sub-menu attached to its parent and b) as a context menu invoked under program control in the same or a different position (see PositionContextMenu method).


Menuwin Notes

  • The menu is in the expanded state only during item selection, and is in the collapsed state at all other times.
  • A menu is expanded for selection by moving the focus to the menu’s title and pressing the ENTER key or SPACE key (or PAGE_DOWN key).

    A menu may also be expanded for selection using its assigned Hotkey (if any).

    When a menu is expanded, the highlight is always placed on the first (topmost) active line item. (This is different from the other scrolling control types which remember the previous highlight position.)

  • A menu is returned to the collapsed state when user has made an item selection.

    A menu is also returned to the collapsed state when the input focus moves to another control. An exception to this is when a sub-menu of the target menu currently has focus, in which case the parent menu will not be collapsed until its sub-menu has been closed.

  • Scrolling:
    The usual navigation keys are available for scrolling through the items of a menu:
       UP_ARROW, DOWN_ARROW, PAGE_UP, PAGE_DOWN, HOME, END
    However, unlike the other scrolling controls, pressing the DOWN_ARROW while the highlight is on the last menu item will cause the highlight to wrap to the first item. Also, pressing the UP_ARROW while the highlight is on the first menu item will cause the highlight to wrap to the last item.
  • Each non-context menu may be assigned a Hotkey. In addition, a MenuBar object may be assigned its own Hotkey in addition to any Hotkeys defined for the individual member menus.

    Note: Hotkeys associated with other control objects are disabled during the period when the user is making a selection from the menu to avoid interferring with the menu’s own hotkeys.
    Please see hotkey specification for a further discussion of hotkeys.

  • Each line item of a menu may have an optional hotkey associated with it. These hotkeys may be selected from 'A' - 'Z' or 'a' - 'z', and are indicated to the user by underlining that character.

    As with hotkeys for control objects, try to avoid using ’i’, ’I’, ’j’, ’J’, ’m’, ’M’ as menu item hotkeys because in Linux/UNIX world, the these characters in combination with the CTRL key have special meanings.

  • When defining the menus for a MenuBar, the control indices of the top-level menus of the MenuBar must be sequential. Sub-menu indices need not be in sequence.
  • Line items within a menu can be individually activated or deactivated, and the active/inactive state may be optionally indicated by changing the color attribute of that line item.
    See SetActiveMenuItems method.

    For instance, menus often have an inactive item which separates different item groupings. This separator is of course inactive so the user cannot hilight it. In the example, the ’Flavors’ item, the ’Toppings’ item and the separator line would be inactive line items, so when the user scrolls through the list, the highlight will automatically skip over those item.

    ┌───────────────────────────────┐ Ice Cream ┌───────────────────┐ Flavors: Chocolate Vanilla Strawberry Peach Rocky Road ───────────────── Toppings: Cashews M & M Plain Marshmallow fluff Sprinkles Butterscotch └───────────────────┘ │ │ └───────────────────────────────┘

    Line items may also be dynamically activated/deactivated to allow the user access to some functionality only at certain times.


Menuwin Examples

┌─────────────────────────────────────────────────┐ Ice Cream │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ DONE └─────────────────────────────────────────────────┘

Independent menu in the 'collapsed' state.


┌─────────────────────────────────────────────────┐ Ice Cream ┌───────────────────┐ Flavors: Chocolate Vanilla Strawberry > Peach Rocky Road ───────────────── Toppings: Cashews M & M Plain Marshmallow fluff Sprinkles Butterscotch └───────────────────┘ DONE └─────────────────────────────────────────────────┘

Independent menu in the 'expanded' state.


┌─────────────────────────────────────────────────┐ Ice Cream ┌───────────────────┐ Flavors: Chocolate Vanilla ┌───────────────────┐ Strawberry > with fruit Peach with jam Rocky Road smooth (no fruit) ───────────────── └───────────────────┘ Toppings: Cashews M & M Plain Marshmallow fluff Sprinkles Butterscotch └───────────────────┘ DONE └─────────────────────────────────────────────────┘

Independent menu with sub-menu attached to 'Strawberry' item.


Working code for the above examples is located in the Dialogw test application, Test07, ’Menu Demonstration’.
A simplified version of the code is presented here.

const short  dialogROWS = 20,       // display lines
             dialogCOLS = 51,       // display columns
             ulY = 3,               // upper left corner in Y
             ulX = 21,              // upper left corner in X
             parITEMS = 13,         // Parent menu items
             parWIDTH = 19,         // Parent text column count
             subITEMS = 3,          // Sub-menu items
             subWIDTH = 19 ;        // Sub-menu text column count
const attr_t dColor = nc.blR,       // dialog background color
             bColor = nc.brbl ;     // dialog border color

//* Control indices *
enum mwControls : short { mwDonePB = ZERO, mwParMW,
                          mwSubMW, mwControlsDEFINED } ;

//* Text for parent menu (use care with column count) *
const char ParText[parITEMS][parWIDTH + 2] = 
{
   " Flavors:         ",
   " ^Chocolate         ",
   " ^Vanilla           ",
   " ^Strawberry       >",
   " ^Peach             ",
   " ^Rocky Road        ",
   " ---------------- ",
   " Toppings:        ",
   " Cashe^ws           ",
   " M & M Plai^n       ",
   " Marshmallow ^fluff ",
   " Sprin^kles         ",
   " ^Butterscotch      ",
} ;

//* Color attributes for parent menu *
attr_t ParAttr[parITEMS] = 
{ nc.gyR, nc.bl, nc.bl, nc.bl, nc.bl, nc.bl, nc.gy, nc.gyR, 
  nc.bl, nc.bl, nc.bl, nc.bl, nc.bl  } ;

//* Active/Inactive flags for parent menu *
const bool ParActive[parITEMS] = 
{ false, true, true, true, true, true, false, 
  false, true, true, true, true, true } ;

//* Text for sub-menu *
const char SubText[subITEMS][subWIDTH + 1] = 
{
   " with fruit        ",
   " with jam          ",
   " smooth (no fruit) ",
} ;

//* Color attributes for sub-menu (monochrome) *
attr_t SubAttr[2] = { attrDFLT, nc.bl } ;

InitCtrl ic[mwControlsDEFINED] = // array of dialog control initialization objects
{
{  //* 'DONE' pushbutton - - - - - - - - - - - - - - - - - - - - - mwDonePB *
   dctPUSHBUTTON,    // type:      
   ..  ..  ..
   &ic[mwParMW]      // nextCtrl:  link in next structure
},
{  //* 'Parent' Menuwin  - - - - - - - - - - - - - - - - - - - - -  mwParMW *
   dctMENUWIN,       // type:      define a Menuwin control
   rbtTYPES,         // rbSubtype: (n/a)
   false,            // rbSelect:  (n/a)
   1,                // ulY:       upper left corner in Y
   4,                // ulX:       upper left corner in X
   1,                // lines:     (n/a)
   (parWIDTH + 2),   // cols:      control columns
   (const char*)&ParText, // dispText:  text-data array
   nc.re,            // nColor:    non-focus border color
                     //            and focus title color
   nc.reR,           // fColor:    focus border color
                     //            and non-focus title color
   tbPrint,          // filter:    (n/a)
   "  Ice ^Cream  ", // label:     label text
   ZERO,             // labY:      (n/a)
   ZERO,             // labX       
   ddBoxTYPES,       // exType:    (n/a)
   parITEMS,         // scrItems:  elements in text/color arrays
   ZERO,             // scrSel:    (n/a)
   ParAttr,          // scrColor:  color-attribute list
   NULL,             // spinData:  (n/a)
   true,             // active:    allow control to gain focus
   &ic[mwSubMW]      // nextCtrl:  link in next structure
},
{  //* 'Sub-menu' Menuwin  - - - - - - - - - - - - - - - - - - - -  mwSubMW *
   dctMENUWIN,       // type:      define a Menuwin control
   rbtTYPES,         // rbSubtype: (n/a)
   false,            // rbSelect:  (n/a)
   short(ic[mwParMW].ulY + 4),            // ulY: upper left corner in Y
   short(ic[mwParMW].ulX + parWIDTH + 1), // ulX: upper left corner in X
   1,                // lines:     (n/a)
   (subWIDTH + 2),   // cols:      control columns
   (const char*)&SubText, // dispText:  text-data array
   nc.re,            // nColor:    non-focus border color
   nc.reR,           // fColor:    focus border color
   tbPrint,          // filter:    (n/a)
   NULL,             // label:     label text (no label for sub-menus)
   ZERO,             // labY:      (n/a)
   ZERO,             // labX       
   ddBoxTYPES,       // exType:    (n/a)
   subITEMS,         // scrItems:  elements in text/color arrays
   ZERO,             // scrSel:    (n/a)
   SubAttr,          // scrColor:  color-attribute list
   NULL,             // spinData:  (n/a)
   false,            // active:    sub-menu initially inactive
   NULL
},
} ;

   //* Initial parameters for dialog window *
   InitNcDialog dInit
                ( dialogROWS, // number of display lines
                  dialogCOLS, // number of display columns
                  ulY,        // Y offset from upper-left of terminal 
                  ulX,        // X offset from upper-left of terminal 
                  NULL,       // no title
                  ncltSINGLE, // border line-style
                  bColor,     // border color attribute
                  dColor,     // interior color attribute
                  ic          // list of controls
                ) ;

//* Instantiate the dialog window *
NcDialog* dp = new NcDialog ( dInit ) ;

//* Open the dialog window *
if ( (dp->OpenWindow()) == OK )
{
   //* Set the active items in parent menu item list.*
   dp->SetActiveMenuItems ( mwParMW, ParActive ) ;

   //* Attach the sub-menu to the parent menu at item index 3.*
   //* For data items with no associated sub-menu, use the    *
   //* default value of MAX_DIALOG_CONTROLS.                  *
   //* The list is terminated by a -1.                        *

   short smList[] = { MAX_DIALOG_CONTROLS, MAX_DIALOG_CONTROLS,
                      MAX_DIALOG_CONTROLS, mwSubMW, -1 } ;
   dp->AttachMenuwinSubmenus ( mwParMW, smList );

   dp->RefreshWin () ;        // make everything visible

   //* User interface loop *
   uiInfo   Info ;            // user interface data returned here
   short    icIndex = ZERO ;  // index of control with input focus
   bool     done = false ;    // loop control

   while ( ! done )
   {
      if ( ic[icIndex].type == dctPUSHBUTTON )
      {
         if ( Info.viaHotkey != false )
         {  // Selection of a Pushbutton via hotkey
            // means it was 'pressed'.
            Info.HotData2Primary () ;
         }
         else
            icIndex = dp->EditPushbutton ( Info ) ;

         if ( Info.dataMod != false )
            done = true ;
      }
      if ( ic[icIndex].type == dctMENUWIN )
      {
         if ( Info.viaHotkey != false )
         {
            // Nothing to be done here.
            // Menu will be expanded immediately
            // because 'viaHotkey' member is 'true'
         }

         icIndex = dp->EditMenuwin ( Info ) ;
      }

      //* Move to next/previous control *
      if ( done == false && !Info.viaHotkey )
      {
         if ( Info.keyIn == nckSTAB )
            icIndex = dp->PrevControl () ; 
         else if ( Info.keyIn != ZERO )
            icIndex = dp->NextControl () ;
      }
   }  // while()
}
if ( dp != NULL )    // close the window
   delete ( dp ) ;


┌──────────────────────────┤ Menu-Win Controls ├───────────────────────────┐ Menuwin A Menuwin B Menuwin C ┌────────────────┐ Status Messages VisibilSelect File > Welcome to Menu World-Enjoy! may be Select All │Copy Files │ Cut Files > Grand-chiPaste Files │Move-Dialog Menu PasteSpecial Delete Files Rename Files >ctivate Context Menu Touch Files NewDirectory >ove Context Menu └────────────────┘ [ ] Hide Menu Bar [ ] Disable Two Items in Menu A DONE └────────────────────────────────────────────────────────────────────────────┘

A MenuBar consisting of 'Menuwin A, 'Menuwin B' and Menuwin C' is defined in the upper left
corner of the dialog. 'Menuwin B' is in the expanded state.
Working code for this dialog is located in Dialog1, Test10.




Spinner Controls

The DialogSpinner-class user interface controls are used to collect range-limited numeric data from the user.

Please see InitCtrl class for information on defining and creating a DialogSpinner-class user interface control.

When you create the control, you specify the minimum, maximum and initial values to be displayed. Please see dspinData class below for information on setting the numeric limits.



  • short EditSpinner ( uiInfo& info );
      Input  :
         info : uiInfo class (by reference) - initial values ignored
                (see description of uiInfo class for values returned)
    
      Returns:
         index of control that currently has the input focus
    

    If the control with input focus is of type dctSPINNER, call this method to get user’s key input.

    The value of the control may be adjusted within the defined value range.

    The EditSpinner method retains control of input until:

    • ENTER, TAB, SHIFT+TAB, LEFT_ARROW or RIGHT_ARROW key pressed
      Indicates that edit is complete. The ’dataMod’ flag of the returned data indicates whether the value has been modified.
    • A ’Hotkey’ combination has been detected.
      If the Hotkey belongs to the current control, it is ignored, and the edit continues.

      Otherwise, the edit is complete and the input focus has been shifted to another control. The ’dataMod’ flag of the returned data indicates whether the value has been modified.

    • The nckESC (Escape) key pressed.
      It is assumed that the user is in panic mode. The control’s previous value is restored, and control is returned to the application as if the user had pressed the nckTAB key.
      (’dataMod’ flag == ’false’).

    Please see spinner key input, below for a list of user-interface keys which are recognized by the EditSpinner method.

    Please see uiInfo class for a discussion of the values returned from the Edit method.



  • short GetSpinnerValue ( short cIndex, int& spValue );
      Input  :
         cIndex : index of target dctSPINNER control
         spValue: (by reference, initial value ignored)
                  receives currently-displayed value
    
      Returns:
         OK if successful
         ERR if
           a) specified control is not of type dctSPINNER
    

    Return the currently-displayed value for the specified Spinner control.

    Important Note: Please carefully review the notes on Spinner control value encoding in the definition of see dspinData class.



  • short SetSpinnerValue ( short cIndex, int setValue );
      Input  :
         cIndex : index of target dctSPINNER control
         spValue: new 'current' value for the control
    
      Returns:
         OK if successful
         returns ERR if:
           a) spValue is outside the defined range for this control
           b) specified control currently has the input focus
           c) specified control is not of type dctSPINNER
    

    Set a new display value for specified Spinner control and update display.

    Important Note: Please carefully review the notes on Spinner control value encoding in the definition of see dspinData class.



  • short SetSpinnerLimits ( short cIndex, int minValue,
                             int maxValue, int setValue );
      Input  :
         cIndex   : index of target dctSPINNER control
         minValue : new minimum data value for the control
         maxValue : new maximum data value for the control
         setValue : new currently-displayed data value
    
      Returns:
         OK if successful
         returns ERR if:
           a) spValue is outside the minValue to maxValue range
           b) specified control currently has the input focus
           c) specified control is not of type dctSPINNER
    

    Specify a new minimum value, maximum value and current value for a dctSPINNER control.

    Important Note: Please carefully review the notes on Spinner control value encoding in the definition of see dspinData class.



dspinData Class

The dspinData class is used to set the initial parameters for a Spinner-class user interface control.

During instantiation of the control, the ’spinData’ member of the ’InitCtrl’ class points to an initialized instance of a dspinData-class object.

class dspinData
{
   public:
   int    minValue;      // minimum display value (default: 0)
   int    maxValue;      // maximum display value (default: 0)
   int    iniValue;      // initial display value (default: 0)
   dspinFormat dsFormat; // format code (member of enum dspinFormat)
                         // (default: dspinINTEGER)
   attr_t indiAttr;      // color attribute for +/- indicator
                         // (default: nc.bw)
   wchar_t indiChar ;    // indicator character (default: wcsPLMINUS)
   bool   leadZero;      // 'true' if display of leading zeros, or
                         // 'false' if no leading zeros (default)
} ;

The 'dspinData' class includes a full-initialization constructor with default values for each data member as indicated above.


  • ’dsFormat’
    The format of the displayed data is specified as a member of enum dspinFormat.
    • dspinINTEGER: Values are displayed and calculated as signed integers.
      Example: an initial value of 1524
               will be displayed to the user as as 1524
      
    • dspinDEC1: Values are displayed and calculated as signed decimal values with one(1) decimal place. In other words, the value displayed is 'value / 10'.
      Example: an initial value of 1524
               will be displayed to the user as as 152.4
      
    • dspinDEC2: Values are displayed and calculated as signed decimal values with two(2) decimal places. In other words, the value displayed is 'value / 100'.
      Example: an initial value of 1524
               will be displayed to the user as as 15.24
      
    • dspinDEC3: Values are displayed and calculated as signed decimal values with three(3) decimal places. In other words, the value displayed is 'value / 1000'.
      Example: an initial value of 1524
               will be displayed to the user as as 1.524
      
  • ’minValue’
    The minimum value which can be displayed in the control.
  • ’maxValue’
    The maximum value which can be displayed in the control.
  • ’iniValue’
    The initial value displayed in the control.
  • ’indiAttr’
    Specifies the color attribute for the indicator character.
    (see 'indiChar' below)
  • ’indiChar’
    The indicator character at the right edge of the control.
    The right edge of a Spinner control consists of an indicator character, (the plus/minus character '±' by default), which indicates to the user that the value in the control may be incremented and decremented.

    You may specify an alternate indicator character if desired.

    As a special case, a NULL character ('\0') specifies that there should be no indicator character. Adjust the field width specified in the DialogSpinner definition accordingly.

    As an example, the following group of three Spinner controls could be configured as a time widget for hours, minutes and seconds, with appropriate division characters between fields (see example output below).

    dspinData hspData( 0, 23, 0, dspinINTEGER, nc.blR, true, L':' );
    dspinData mspData( 0, 59, 0, dspinINTEGER, nc.blR, true, L':' );
    dspinData sspData( 0, 59, 0, dspinINTEGER, nc.blR, true, nckNULLCHAR );
    
  • ’leadZero’
    If this field is reset ('false'), then unused columns on the left will be padded with space (' ') characters.
                  POSITIVE      NEGATIVE
    Examples:    ▒▒  147±▒▒    ▒▒ -147±▒▒    
    

    If this field is set ('true'), then unused columns on the left will be padded with zero ('0') characters.

                  POSITIVE      NEGATIVE
    Examples:    ▒▒00147±▒▒    ▒▒-0147±▒▒    
    

Notes on display of values

The width of the control is specified during instantiation by the 'cols' member of the InitCtrl class object. The value specified must include the width of the numeric data (including minus sign, if any), as well as the width of the indicator character.

For example, a control which is to display a range of -100 to 100 must be specified with a width of at least five(5) columns: four(4) columns for the value and one(1) column for the ’±’ character.

If the display field is too narrow for the value to be displayed, then the field will be filled with HASH characters ('#') indicating overflow.

Example:     ▒▒###±▒▒    

The nominal maximum width of a DialogSpinner control is MAX_SPINNER_WIDTH(16) columns, which is more than enough to display any integer value up to a petabyte.

Whether you specify the display format for the value as an integer or as a decimal value, all data are passed to and from the Spinner control as signed integers. Thus, when you use a retrieved value or set a new value, you must do the proper conversion.

Example:
   // Retrieve the current value from a Spinner which
   // was defined with a format of dspinDEC3.
   int iValue ;
   dlgPtr->GetSpinnerValue ( index, iValue );
   
   // Convert to a decimal value for calculations.
   // ('spValue' will be equivalent to )
   // (the value displayed to the user.)
   double spValue = (double)iValue /= 1000.0;

Note on integer size: All values for Spinner-class objects are stored and retrieved as signed integer values. Thus, the minimum and maximum values which can be represented in the dspinData-class member variables is determined by the number of bits in a 'signed int' on your system. While this is unlikely to be a problem except on 8-bit or 16-bit controllers, it’s just something to keep in mind.


Spinner Controls - User Interface

The following keys and key combinations are available to the user to adjust the value of the Spinner control.

increase by 1    : Up Arrow
decrease by 1    : Down Arrow
increase by 10   : Shift + Up Arrow
decrease by 10   : Shift + Down Arrow
increase by 100  : Control + Up Arrow
decrease by 100  : Control + Down Arrow
increase by 1000 : Alt + Up Arrow
decrease by 1000 : Alt + Down Arrow

increase by 10%  : Page Up key
decrease by 10%  : Page Down key

max value (100%) : Home key
min value (  0%) : End key

Note that for convenience, the plus key ('+') is interpreted as ’UpArrow’, and the minus key ('-') is interpreted as ’DownArrow’.


Spinner Controls - Examples

The following examples are excerpted from the Dialog2 test application, Test03. See the test application for working examples.

//***********************
//* Spinner Definitions *
//***********************
// integer. 4-wide (positive values only)
dspinData dsData_A( 0, 999, 0, dspinINTEGER, nc.blG );

// integer, 5-wide (positive and negative values)
dspinData dsData_B( -999, 999, 0, dspinINTEGER, nc.blG );

// decimal, 5-wide (positive,  1 decimal place)
dspinData dsData_C( 0, 999, 25, dspinDEC1, nc.grG );

// decimal, 6-wide (positive and negative,  1 decimal place)
dspinData dsData_D( -999, 999, 0, dspinDEC1, nc.grG );

// decimal, 6-wide (positive,  2 decimal places)
dspinData dsData_E( 0, 9999, 0, dspinDEC2, nc.maG );

// decimal, 7-wide (positive and negative,  2 decimal places)
dspinData dsData_F( -9999, 9999, 0, dspinDEC2, nc.maG );

// decimal, 7-wide (positive,  3 decimal places)
dspinData dsData_G( 0, 10000, 0, dspinDEC3, nc.brG );

// decimal, 8-wide (positive and negative,  3 decimal places)
dspinData dsData_H( -10000, 10000, 0, dspinDEC3, nc.brG );


//***********************
//* User Interface Loop *
//***********************
uiInfo Info ;                 // user interface data returned here
short  icIndex = ZERO ;       // index of control with input focus
bool   done = false ;         // loop control
while ( ! done )
{
   ..  ..  ..

   if ( ic[icIndex].type == dctSPINNER )
   {
      Info.viaHotkey = false ; // discard any hotkey data

      icIndex = dp->EditSpinner ( Info ) ;

      // If displayed value has been modified
      if ( Info.dataMod != false )
      {
         // Take any necessary actions here
         switch ( Info.ctrlIndex )
         {
            case T3spinnerA:  break ;  // Spinner A
            case T3spinnerB:  break ;  // Spinner B
            case T3spinnerC:  break ;  // Spinner C
            case T3spinnerD:  break ;  // Spinner D
            case T3spinnerE:  break ;  // Spinner E
            case T3spinnerF:  break ;  // Spinner F
            case T3spinnerG:  break ;  // Spinner G
            case T3spinnerH:  break ;  // Spinner H
            case T3spinOvr3:  break ;  // Overflow Test 1
            case T3spinOvr5:  break ;  // Overflow Test 2
            default:          break ;  // should never happen
               
         }
      }
      else
      { /* Spinner data not modified, so nothing to do */ }
   }

   ..  ..  ..

}  // (while)


┌────────────────┤ DialogSpinner Class Controls ├────────────────┐ │ │ │ Integer, 4-wide (range: 0 to 999): A 86± │ │ │ Integer, 5-wide (range: -999 to 999): B -141± │ │ │ Decimal, 5-wide (range: 0.0 to 99.9): C 2.5± │ │ │ Decimal, 6-wide (range: -99.9 to 99.9): D 21.5± │ │ │ Decimal, 6-wide (range: 0.00 to 99.99): E 1.21± │ │ │ Decimal, 7-wide (range: -99.99 to 99.99): F -24.48± │ │ │ Decimal, 7-wide (range: 0 to 10.000): G 4.625± │ │ │ Decimal, 8-wide (range: -10.000 to 10.000): H -6.210± │ │ ├──────────────────────────────────────────────────────────────────┤ DONE └──────────────────────────────────────────────────────────────────┘


Working code for the following example can be found in
the Dialogw test application, Test07: ’Time Spinner’.

╔══════════╣ Time Spinner ╠══════════╗ Enter the time (24-hour) 16:15:35 hh mm ss DONE (Mouse input is active in this dialog.) ╚═══════════════════════════════════════╝




Select Control via Hotkey

Definition

A ’hotkey’ is a shortcut key which may be defined for any user-interface control. The user may press a hotkey in order to move directly to the indicated control without the need to move sequentially through the controls.

The hotkey is optionally specified for a control during the definition of the control object. This is done by placing the ’^’ (caret) character just before the desired hotkey character in the control’s ’label’ field, or in the case of Pushbutton controls (which do not have labels), in the ’dispText’ field of the InitCtrl class. This does two things:

  • It assigns the specified keycode as the hotkey character.
    The character itself is actually a group of related key combinations:
    a) the lowercase character
    b) the uppercase character
    c) the CTRL (Control) key + the character
    See example below.
  • It displays to the user the hotkey character in underlined text to indicate the hotkey which has been assigned to that control.

Hotkey Specification

Specification of a hotkey for a control is done according to the following guidelines.

  • The character must be an ASCII alphabetical character:
    'a'-'z' OR 'A'-'Z'
  • The use of ’M’, ’m’, ’J’, ’j’, ’I’ or ’i’ as a hotkey is strongly discouraged because when used in combination with the CTRL key, these keycodes have a special meaning under Linux/UNIX:
    ’CTRL+M’, ’CTRL+m’ ’CTRL+J’ and ’CTRL+j’ are all equal to the ENTER key, while ’CTRL+I’ and ’CTRL+i’ are equal to the TAB key.
  • The specified character must be used for only one(1) control. Duplicate hotkey characters, while not forbidden, will cause strange behavior.
  • While the user is editing a Textbox control the ASCII ’a’-’z’ and ’A’-’Z’ will be interpreted as text input, so only the CTRL+__ version of the hotkey will be recognized within a Textbox control.
    (Please see Textbox Controls for more information.)
  • In addition, if you use cut-and-paste within your application, then ’CTRL+C’, ’CTRL+V’ and ’CTRL+X’ (or whatever key combinations you have specified) will probably be reserved for that functionality (at least while the user is in a Textbox control), so these would then be in conflict with the equivalent hotkey.
  • If you define hotkeys for one or more controls, then your user-interface loop must be made aware. For an example of hotkey-awareness, please see uiInfo class.
  • For Menuwin controls ONLY:
    Menuwin controls allow for line-item hotkeys to be assigned to the individual display items within the control. Thus, while the user is inside a Menuwin control, all control object hotkeys are masked and inaccessible.
    Please see Menuwin Controls for a discussion of line-item hotkeys.
  • Note on Billboard controls:
    Because Billboard control never have the input focus, hotkey specification for these controls is ignored.

Control Object and Line-item Hotkey Examples

The following control definitions show how a hotkey may be assigned to a control, and the text array shows how hotkeys may be assigned to each line-item in a Menuwin control.

In the definition for the Pushbutton control:
The Pushbutton’s display text is the word "DONE" as specified in the ’dispText’ field. The hotkey is assigned to the ’D’ character by placing a ’^’ (caret) before it.

In the definition for the Menuwin control:
The label text for the Menuwin control is the string " Football team " as specified in the ’label’ field. The hotkey is assigned to the ’t’ character by placing a ’^’ (caret) before it.

The line-item data defined for the Menuwin contents contains a hotkey specification for each line item. (Be sure to avoid hotkey duplication among line items.)


const short DATA_ITEMS = 12; // items in list
const short ITEM_WIDTH = 20; // chars per item (incl. NULLCHAR)
//* Display text for Scrollbox control *
const char TeamNames[DATA_ITEMS][ITEM_WIDTH + 1] = 
{
   " ^Arsenal           ",
   " Aston ^Villa       ",
   " ^Blackburn         ",
   " B^ournemouth       ",
   " B^urnley           ",
   " ^Chelsea           ",
   " ^Liverpool         ",
   " ^Manchester United ",
   " Mancheste^r City   ",
   " ^Newcastle United  ",
   " ^Southhampton      ",
   " ^West Ham United   ",
};
//* Color attributes for list *
attr_t monoColor[2] = { attrDFLT, nc.gr };

InitCtrl ic[2] = // array of dialog control initialization objects
{
{  //* 'DONE' pushbutton - hsPB *
   dctPUSHBUTTON,    // type:      
   rbtTYPES,         // rbSubtype: (n/a)
   false,            // rbSelect:  (n/a)
   6,                // ulY:       upper left corner in Y
   10,               // ulX:       upper left corner in X
   1,                // lines:     control lines
   8,                // cols:      control columns
   "  ^DONE  ",      // dispText:  
   nc.gyR,           // nColor:    non-focus color
   nc.reG,           // fColor:    focus color
   tbPrint,          // filter:    (n/a)
   NULL,             // label:     (n/a)
   ZERO,             // labY:      (n/a)
   ZERO,             // labX       (n/a)
   ddBoxTYPES,       // exType:    (n/a)
   1,                // scrItems:  (n/a)
   1,                // scrSel:    (n/a)
   NULL,             // scrColor:  (n/a)
   NULL,             // spinData:  (n/a)
   true,             // active:    allow control to gain focus
   &ic[hsMW]         // nextCtrl:  link in next structure
},
{  //* 'Team' Menuwin - hsMW *
   dctMENUWIN,       // type:      define a scrolling-data control
   rbtTYPES,         // rbSubtype: (n/a)
   false,            // rbSelect:  (n/a)
   2,                // ulY:       upper left corner in Y
   3,                // ulX:       upper left corner in X
   1,                // lines:     (n/a)
   (ITEM_WIDTH + 2), // cols:      control columns
   (const char*)&TeamNames, // dispText:  text-data array
   nc.gr,            // nColor:    non-focus border color
   nc.grR,           // fColor:    focus border color
   tbPrint,          // filter:    (n/a)
   " Football ^team ", // label:     label text
   -1,               // labY:      label offset
   ZERO,             // labX       
   ddBoxTYPES,       // exType:    (n/a)
   DATA_ITEMS,       // scrItems:  elements in text/color arrays
   ZERO,             // scrSel:    (n/a)
   monoColor,        // scrColor:  color-attribute list
   NULL,             // spinData:  (n/a)
   true,             // active:    allow control to gain focus
   NULL              // nextCtrl:  link in next structure
},
} ;

(Working code for this example can be found in the Dialogw test application, Test07: ’Hotkey Demonstration’.)




Technical Note: If the mouse interface has been activated, then each control object will have an assigned hotkey character: the hotkey character explicitly assigned as described above, OR if no hotkey has been explicitly assigned, then a hidden hotkey referenced only within the API will be assigned. The reason this is done is so that if the user clicks the mouse over a control, then the focus can be immediately changed to that control as if the user had pressed the assigned key. Hidden hotkeys are guaranteed not to conflict with application-specified hotkeys.


NcDialog Display Output

  • winPos WriteString ( short startY, short startX,
                         const gString& gStr, attr_t cAttr,
                         bool refresh=false, bool rtl=false );
  • winPos WriteString ( const winPos& startYX, const char* uStr,
                         attr_t cAttr,
                         bool refresh=false, bool rtl=false );
  • winPos WriteString ( short startY, short startX,
                         const char* uStr, attr_t cAttr,
                         bool refresh=false, bool rtl=false );
  • winPos WriteString ( const winPos& startYX, const wchar_t* wStr,
                         attr_t cAttr,
                         bool refresh=false, bool rtl=false );
  • winPos WriteString ( short startY, short startX,
                         const wchar_t* wStr, attr_t cAttr,
                         bool refresh=false, bool rtl=false );
  • winPos WriteString ( const winPos& startYX, const gString& gStr,
                         attr_t cAttr,
                         bool refresh=false, bool rtl=false );
      Input  :
         startY : Y cursor start position
         startX : X cursor start position
               OR
         startYX: Y/X cursor start position
    
             FOR UTF-8-ENCODED CHARACTERS (INCLUDING ASCII)
         uStr : pointer to null-terminated UTF-8-encoded string
                (maximum number of bytes incl. NULLCHAR == gsMAXBYTES)
             OR FOR wchar_t 'WIDE' CHARACTERS
         wStr : pointer to null-terminated 'wide' wchar_t string
                (maximum number of chars incl. NULLCHAR == gsMAXCHARS)
             OR FOR DIRECT DISPLAY OF DATA STRING IN A gString CLASS OBJECT
         gStr : a gString-class object (by reference)
                (maximum number of chars incl. NULLCHAR == gsMAXCHARS)
             NOTE: String length will be truncated at edge of window.
             NOTE: ASCII control characters, 0x01 - 0x1F are ignored.
    
         cAttr  : color attribute
         refresh: (optional, default==false) refresh display window
         rtl    : (optional, 'false' by default) for RTL written languages
                  if 'true', cursor moves from right-to-left
      Returns:
         returns final position of cursor
          If specified starting Y/X position is valid, then cursor position 
          returned will be the the column following the last character of 
          displayed data (or the last column of the target line).
    
          If specified starting position is invalid, then cursor position 
          is set to window origin (ZERO/ZERO) and no data will be written.
    
         Note on cursor position returned: Y value will be the same as 
         startY, and X value will never be greater than last column in 
         the window, nor less than ZERO.
    

    Write a character string at the specified position in the window.
    Optionally, refresh the display before return to caller.

    Output will be truncated as necessary to avoid writing outside the dialog window’s boundaries.

    All languages AND the complete UTF-8 character set are supported. Please see Multi-language Support for a further discussion of internationalization.



  • winPos WriteChar ( short startY, short startX, const char* uChar,
                       attr_t cAttr,
                       bool refresh=false, bool rtl=false );
  • winPos WriteChar ( const winPos& startYX, const char* uChar,
                       attr_t cAttr,
                       bool refresh=false, bool rtl=false );
  • winPos WriteChar ( short startY, short startX, wchar_t wChar,
                       attr_t cAttr,
                       bool refresh=false, bool rtl=false );
  • winPos WriteChar ( const winPos& startYX, wchar_t wChar,
                       attr_t cAttr,
                       bool refresh=false, bool rtl=false );
      Input  :
         startY : Y cursor start position
         startX : X cursor start position
               OR
         startYX: Y/X cursor start position
    
             FOR UTF-8-ENCODED CHARACTERS (INCLUDING ASCII)
         uChar  : pointer to character to display
                 (single-byte or multi-byte character)
             OR FOR WCHAR_T CHARACTERS
         wChar  : character to display in wchar_t format
             NOTE: ASCII control characters, 0x01 - 0x1F are ignored.
    
         cAttr  : color attribute
         refresh: (optional, default==false) refresh display window
         rtl    : (optional, 'false' by default) for RTL written languages
                  if 'true', cursor moves from right-to-left
      Returns:
         returns final position of cursor
          If specified starting Y/X position is valid, AND if the 
          specified character is a printing character, then cursor position 
          returned will be the the column following the character (or the
          last column of the target line).
    
          If specified starting position is invalid, OR if non-printing
          character specified, then cursor position is set to window
          origin (ZERO/ZERO) and no data will be written.
    

    Write a single character at the specified position.
    Optionally, refresh the display before return to caller.

    Output will be truncated as necessary to avoid writing outside the dialog window’s boundaries.

    IMPORTANT NOTE: Because it can never be assumed that one character equals one byte, there is no option to pass a one-byte character by value. ("You’ll thank me later." - Adrian Monk)

    All languages AND the complete UTF-8 character set are supported. Please see Multi-language Support for a further discussion of internationalization.



  • winPos WriteParagraph ( short startY, short startX,
                            const gString& gStr,
                            attr_t cAttr,
                            bool refresh=false, bool rtl=false );
  • winPos WriteParagraph ( const winPos& startYX, const gString& gStr,
                            attr_t cAttr,
                            bool refresh=false, bool rtl=false );
      Input  : 
         startY : Y cursor start position
         startX : X cursor start position
               OR
         startYX: Y/X cursor start position
    
         gStr   : pointer to a gString-class object
                  (maximum number of characters incl. NULLCHAR == gsMAXCHARS)
                  NOTE: Except for the newline character '\n', ASCII control
                        characters, 0x01 through 0x1F are ignored
         cAttr  : color attribute
         refresh: (optional, default==false) refresh display window
         rtl    : (optional, 'false' by default) for RTL written languages
                  if 'true', cursor moves from right-to-left
    
      Returns: 
         returns final position of cursor
          If specified starting Y/X position is valid, then cursor position 
          returned will be the the column following the last character of 
          displayed data (or the last column of the final line).
    
          If specified starting position is invalid, then cursor position 
          is set to window origin (ZERO/ZERO) and no data will be written.
    

    Write a multi-line string, starting at the specified position.
    Optionally, refresh the display before return to caller.

    A newline character ’\n’ signals the end of each line. Automatic line-wrapping is not supported. It is the caller’s responsibility to correctly size each line to fit within the number of columns available in the target window.

    Output will be truncated as necessary to avoid writing outside the dialog window’s boundaries.

    All languages AND the complete UTF-8 character set are supported. Please see Multi-language Support for a further discussion of internationalization.



  • short ClearLine ( short ypos, bool clearAll = false,
                      short xpos = 1, bool rtl = false ) ;
      Input  : 
         ypos    : line number (zero-based)
         clearAll: (optional, false by default) if 'true', clear entire
                   line, if 'false' do not clear border area
         xpos    : (optional)
                   starting column (zero-based)
                   Note: for LTR, default is one(1)
                         for RTL, default is (columns-2)
                   (ignored if clearAll != false)
         rtl     : (optional, 'false' by default)
                   if 'false', then clear from 'xpos' toward right
                   if 'true',  then clear from 'xpos' toward left
                   (ignored if clearAll != false)
    
      Returns: 
         OK if successful
         ERR if invalid line index
    

    Clear the specified line of the dialog using the defined background color. Display is refreshed.



  • short ClearLine ( short lIndex, attr_t altColor
                      bool clearAll = false );
      Input  :
         lIndex  : index of line to be cleared (zero-based)
         altColor: color attribute for the target line
         clearAll: (optional, false by default) if 'true', clear entire
                   line, if 'false' do not clear border area
    
      Returns:
         OK if successful
         ERR if invalid line index
    

    Clear the specified line of the dialog using an alternate color attribute. Display is refreshed.



  • short ClearArea ( short lIndex, short cIndex, short lCount,
                      short cCount, attr_t fillColor = attr_t(-1),
                      wchar_t fillChar = nckSPACE);
      Input  :
         lIndex   : index of top line of target area
         cIndex   : index of left column of target area
         lCount   : number of lines in target area
         cCount   : number of columns in target area
         fillColor: (optional, dialog window's background color by default)
                    alternate color attribute for the target area
         fillChar : (optional, SPACE character by default)
                    alternate character to write into each character cell
                    NOTE: Specified character must be a single-column
                          character. A multi-column character
                          specification will be ignored.
    
      Returns:
         OK if successful
         ERR if parameter(s) out-of-range
    

    Clear the specified rectangular area of the dialog window.
    Optionally use an alternate color attribute and/or an alternate fill character. Display is refreshed.



  • short DrawLine ( const LineDef& ldef );
      Input  :
         lDef (by reference)
              initialized fields
           type   : ncltHORIZ or ncltVERT
           style  : normal, bold, dual-line, or one of the
                    dashed line types
           startY : y offset for start of line
           startX : x offset for start of line
           length : number of lines (vert) or columns (horiz)
           attr   : color attribute
      Returns:
         OK if success, else ERR
    

    Draw a line of the specified type and style.

    This is not just a simple line-drawing method. Instead, the drawing routine scans the existing contents of the window, and if the new line intersects an existing line, then a visual connection of the lines is drawn.

    To prevent these automatic line connections, please set the appropriate flag(s) to ’false’: (cLeft, cRight, cTop, cBot, cInsect). Does not refresh the window.

    Please see LineDef class for a further discussion of line drawing.

    Example: Split a window into four(4) sections.

           ╔═══════════════════════════════════╗ Section #1 Section #2 Section #3 Section #4 ╚═══════════════════════════════════╝        ╔═════════════════╦═════════════════╗ Section #1 Section #2 ╠═════════════════╬═════════════════╣ Section #3 Section #4 ╚═════════════════╩═════════════════╝

    Please see the test application Dialog2, Test08 for line drawing examples, and a table of line drawing characters is available at
    see Line Drawing Characters.



  • short DrawBox ( short startY, short startX,
                    short rows, short cols,
                    attr_t cAttr, const char* title=NULL,
                    ncLineType style=ncltSINGLE, bool rtlText=false );
      Input  :
         startY : y offset for start of line
         startX : x offset for start of line
         rows   : number of rows
         cols   : number of columns
         cAttr  : color attribute
         title  : (optional, default==NULL)
                  if non-NULL, points to box title string
         style  : (optional, ncltSINGLE by default)
         rtlText: (optional, false by default)
                  if 'title' != NULL, then:
                  if false, draw as LTR text, if true, draw as RTL text
      Returns:
         If no title string is specified:
           OK on success,
           else ERR (box would extend beyond parent window)
         If a title string IS specified:
           returns the X offset at which the title is displayed.
    

    Draw a rectangle within the dialog window.
    Does not refresh the display.


    This screenshot is from the Dialog1 test application, Test06.
    Note that we have used the DrawBox method (with title) to enclose the two Radiobutton groups, so the user can easily identify which controls which are grouped together.

    ┌──────────────────┤ Radio-Button Grouping Test ├──────────────────┐ │ ┌─────────┤ Group A ├─────────┐ ┌─────────┤ Group B ├─────────┐ │ │ │ [◆] rbtS3s Radio Button #1 │ │ [◆] rbtS3s Radio Button #5 │ │ │ │ [ ] rbtS3s Radio Button #2 │ │ [ ] rbtS3s Radio Button #6 │ │ │ │ [ ] rbtS3s Radio Button #3 │ │ [ ] rbtS3s Radio Button #7 │ │ │ │ [ ] rbtS3s Radio Button #4 │ └─────────────────────────────┘ │ │ └─────────────────────────────┘ [ ] Stand-alone Button #8 │ │ Group A Selection 01 Button 8 State reset │ Group B Selection 05 [◆] Stand-alone Button #9 │ │ Text Box Button 9 State set DONE └────────────────────────────────────────────────────────────────────┘


  • winPos GetCursor ( void );
      Input  : none
    
      Returns: a winPos class object containing the Y/X cursor position
    

    Get the current position of the cursor.

    This method has limited value because the cursor position is a shared resource, and therefore it can never be guaranteed that the cursor will remain where you put it.



  • short SetCursor ( short ypos, short xpos );
  • short SetCursor ( winPos yxpos );
      Input  : 
         ypos : Y offset from upper-left corner (zero-based)
         xpos : X offset from upper-left corner (zero-based)
           OR
         yxpos: Y/X offset from upper-left corner (zero-based)
    
      Returns: 
         OK if successful
         ERR if parameter(s) out of range
    

    Set the cursor position within the dialog window.

    This method has limited value because the cursor position is a shared resource, and therefore it can never be guaranteed that the cursor will remain where you put it.



  • ncCurVis GetCursorState ( void );
      Input  : none
    
      Returns: current cursor state (member of enum ncCurVis)
    

    Returns the current visibility state of the cursor.

    This method has limited value because the cursor should be set as invisible during startup, and should remain invisible unless there are exceptional circumstances. Under normal circumstances, the only time the cursor is visible is inside the EditTextbox method.



    short SetCursorState ( ncCurVis state );

      Input  :
         state : member of enum ncCurVis
    
      Returns: OK
    

    Set the visibility state of the cursor.

    This method has limited value because the cursor should be set as invisible during startup, and should remain invisible unless there are exceptional circumstances. Under normal circumstances, the only time the cursor is visible is inside the EditTextbox method.



  • short RestoreCursorState ( void );
      Input  : none
    
      Returns: OK
    

    Restore the previous visibility state of the cursor.

    This method has limited value because the cursor should be set as invisible during startup, and should remain invisible unless there are exceptional circumstances. Under normal circumstances, the only time the cursor is visible is inside the EditTextbox method.




Direct Keyboard Input

Configuration of the keyboard/mouse input stream is performed during the application’s startup sequence.
For key-input configuration, please see NCurses Startup-Shutdown.
For application-level mouse support, please see Mouse Configuration.

Gathering keyboard and mouse input from the user is generally done only by the ’Edit’ methods for the individual control objects.
Please see User Interface Loop for a discussion of the mechanism for user interaction.

In an NcDialog application, direct key/mouse input is neither necessary nor wise. Still, in addition to the setup and configuration operations, it is possible to directly monitor the keyboard/mouse input stream.



  • wkeyType GetKeyInput ( wkeyCode& wKey ) ;
      Input  :
         wKey   : wkeyCode-class object (by reference) to hold the 
                  key/mouse information (initial values ignored)
    
      Returns:
         member of enum wkeyType
         wktPRINT  if key is a valid, printing wchar_t character code
         wktFUNKEY if key is a translated function key (cursor key, Fnn, etc)
                   OR if key is an ASCII (non-printing) control code < 0x0020.
         wktMOUSE  if a mouse event in the input stream has been detected,
                   then the mouse event will be returned in wKey.mevent.
                   (wKey.key will be set to ZERO and should be ignored.)
         wktEXTEND This is an extension to the key input captured by the
                   ncurses library primitives. It provides additional
                   keycodes to the application: Alt+'a' through Alt+'z',
                   Alt+Shift+'a' through Alt+Shift+'z' and Alt+Ctrl+'a'
                   through Alt+Ctrl+'z'. (See notes in NCursesKeyDef.hpp.)
         wktESCSEQ if an untranslated escape sequence was captured.
                   The keycode (wKey.key) will be set to ZERO.
                   This should only happen if application has turned
                   off key-input translation (or in the rare case that
                   neither the ncurses library nor the NCurses class can
                   translate the sequence).
                   The captured escape sequence is held in a static
                   buffer and can be retrieved via GetEscapeSequence()
                   before it is overwritten by the next untranslated
                   sequence.
         wktERR    early return if no key data available.
                    This method performs a non-blocking read if 
                    'SetKeyDelay' method not set to nckdFOREVER,
                    and will retur wktERR if key data not available.
                   OR 
                    If system call returned an error (unlikely).
                   The keycode (wKey.key) will be set to ZERO.
    

    Get a keycode and keytype from the input stream.
    This includes all supported international character codes as well as captured-and-translated escape sequences representing function keys, cursor keys and other special-purpose key combinations.

    If mouse input has been enabled, mouse events will also be returned in caller’s wkeyCode-class object.
    Please see Mouse Configuration for additional information.

    Important note on the returned data:
    Never assume that it is safe to refer only to the returned keycode. The key ’type’ is critical to the correct interpretation of keyboard/mouse input. Always test the returned data as a keytype/keycode unit.
    Please see wkeyCode class for more information.



  • wkeyType KeyPeek ( wkeyCode& wKey );
      Input  :
         wKey : wkeyCode-class object (by reference) to
                hold the key information (initial values ignored)
    
      Returns:
         member of enum wkeyType
            if key data waiting:
               wKey.type == wktPRINT, wktFUNKEY or wktMOUSE
               wKey.key  == key code
            if no key data waiting (or untranslated escape sequence):
               wKey.type == wktERR
               wKey.key  == ZERO and should be ignored
    

    If there is key data waiting in the key input buffer, return a copy of it. Does not remove key data from input stream.

    Please see keyPushback class for information of the key-input queue.



  • short UngetKeyInput ( wkeyCode& wKey );
      Input  :
         wKey : (by reference)
                contains keycode and key type of input to be 
                returned to the input queue
    
      Returns:
         OK if successful
         ERR if invalid key data (e.g. part of an escape sequence)
             or if un-get queue is full
    

    Return the wchar_t keycode and its key type or a mouse event to the input queue.

    • Note that no more than KEYPUSH_MAX keycodes may be pushed back into the queue without an intervening call to the ’GetKeyInput’ method.
    • Note that only valid keycodes and mouse events can be pushed back into the queue. Escape sequences MAY NOT be pushed back into the queue.

    Please see keyPushback class for information of the key-input queue.



  • short FlushKeyInputStream ( void );
      Input  : none
    
      Returns: OK
    

    Flush (discard) any key input data currently in the key input stream. If mouse input has been enabled, then flush the mouse-event queue also.



  • ncKeyProc GetKeyProcState ( void );
      Input  : none
    
      Returns: member of ncKeyProc
    

    Returns the current level of key-input processing done by the kernel, the ncurses library, and the NCurses class before data are passed to the application.



  • short SetKeyProcState ( ncKeyProc procState );
      Input  :
         procState : member of ncKeyProc
    
      Returns: OK if successful, else ERR
    

    Set the level of key-input processing done by the kernel, the ncurses library, and the NCurses class before data are passed to the application code.

    The default value set during startup is nckpRAW because key input will be fully encapsulated in the NCurses class making the maximum number of keys available to the driving application. Have a good reason before modifying this parameter.

    Applications should always request key data through the ’GetKeyInput’ method, and NEVER directly from the system or through C/C++ functions.



  • short GetKeyDelay ( void );
      Input  : none
    
      Returns:
         delay in milliseconds
           or
         nckdFOREVER
           or
         nckdNODELAY
    

    Returns the current key-input delay in milliseconds.



  • short SetKeyDelay ( short delay );
      Input  :
         delay : delay in milliseconds (1 to 1000)
                   or
                 nckdFOREVER == wait indefinitely for input (blocking read)
                   or
                 nckdNODELAY == returns immediately if no key data available
    
      Returns: OK
    

    Set the number of milliseconds to delay while waiting for key input.

    If no key data arrives before the timeout, the ’GetKeyInput’ method will return ERR and the target variable will be undefined.

    The default value on startup is nckdFOREVER (blocking read).
    Please see NCurses Startup-Shutdown for more information on the startup defaults.




Mouse Configuration

Please Note:,
— The xterm mouse interface is garbage.
— The ncurses library mouse interface is rotting garbage.
— The NcDialog API mouse interface is rotting garbage in a colorful, heavy-duty trash bag.
Please don’t expect miracles.

(but see the meEnableStableMouse method)


This chapter is divided into the following sections.




Mouse Configuration Methods

This section describes the methods used to configure your application for mouse input.

The NcDialog API mouse interface provides the application with transparent access to the mouse interface without the need to interpret what mouse events mean in a given context.
Please see User Interface Loop for more information.

If you are curious about what the raw mouse data look like, the data are available through the low-level NCurses-class methods OR through the see Dump_uiInfo method; however, using the raw data directly within an application is strongly discouraged.



  • short meEnableStableMouse ( short dclick = ncmiSTABLE
                                bool swheel = true );
      Input  :
         dclick : (optional, ncmiSTABLE by default)
                  This parameter indicates the time to wait after the
                  first click event is received to determine whether
                  the event is a double-click.
                  Specified in thousandths (0.001) of a second OR a
                  member of enum ncmInterval.
         swheel : (optional, 'true' by default)
                  'true'  : ScrollWheel events are reported
                  'false' : ScrollWheel events ARE NOT reported
    
      Returns:
         OK  if mouse-event reporting enabled and the system
             supports all required mouse event types and 
             specified timing parameters.
         returns ERR if:
           a) mouse could not be enabled
           b) if optionally-specified 'dclick' value is out of range
           c) one or more required event types not supported
              by the system
           d) the required timing interval could not be established
           If ERR, then mouse interface will be disabled.
    

    Enable enhanced reporting of mouse events.
    The application does not directly interact with the mouse. Instead, mouse events are reported through the ’GetKeyInput’ keyboard input stream. The NcDialog API converts mouse events to the equivalent keycodes whenever possible, and ignores uncoverted mouse events within the user-interface methods for the individual dialog controls (see Mouse Event Translation). Thus, the application does not need to do any mouse-event processing beyond enabling and disabling the mouse event stream.

    This method requires that all necessary parameters be fully supported by the underlying mouse driver. If the system cannot be configured as required, then the mouse interface is disabled and ERR is returned.

    This form of enabling the mouse interface is different from the more general meEnableMouse method described below. The purpose of this configuration method is to reduce the unfortunate timing artifacts inherited from the xterm mouse driver and from the ncurses(w) C-language library. This is accomplished by synthesizing the mouse-event timing. It is hoped that this will reduce the loss of valid mouse-event data and improve overall mouse performance.

    This method restricts the mouse interface to the following event types, (with and without modifier keys CTRL, SHIFT and ALT).

    metB1_S : Button 1 - single click
    metB1_D : Button 1 - double click
    metB1_T : Button 1 - triple click
    
    metSW_U : Scroll-wheel - scroll up   (see 'swheel' parameter)
    metSW_D : Scroll-wheel - scroll down
    

    Note on timing: The ’dclick’ (double-click) delay interval may be set only at the time the mouse interface is enabled, (the meSetClickInterval method is disabled when using the stabilized mouse interface).

    To dynamically adjust this interval, it is necessary to disable the mouse interface and then call meEnableStableMouse with the new ’dclick’ value.

    Example:
       .. .. ..
       // get new interval from user (milliseconds)
       short newDelay = 400;
    
       if ( newDelay >= ncmiNONE && newDelay <= ncmiMAX )
       {
          dp->meDisableMouse ();
          if ( (dp->EnableStableMouse ( newDelay )) == OK )
             ;  // deliver good news
          else
             ;  // deliver bad news
       }
    

    The default double-click delay assumes a "typical" user. For a user with restricted vision, movement, or other accessibility issues, a longer delay should be specified. Also, if your application has multiple active threads, then specify a slightly longer delay to compensate for the task-switching overhead.

    A user-friendly application will provide the user with a way to interactively adjust the click interval.

    Example of interactive adjustment using a Spinner control:

    ╔══════╣ Adjust Click Interval ╠══════╗ Click the mouse inside the window. Button #1 - Double Click ypos: 5 xpos: 6 198± DONE Click Delay ╚═══════════════════════════════════════╝

    (Working code for this dialog can be found in the Dialogw test application, Test07: ’Set Mouse Click Interval’.)



    • short meEnableMouse ( mmask_t eventTypes,
                            short interval=ncmiDFLT );
        Input  :
           eventTypes : bit mask of event types to be enabled
                        Available bitmask definitions are listed below.
                        NOTE: If no mouse-event types are specified,
                              (eventTypes==ZERO), then the following mask
                              is set as the default:
                            eventTypes = BUTTON1_CLICKED |
                                         BUTTON1_DOUBLE_CLICKED |
                                         REPORT_SCROLL_WHEEL;
           interval  : (optional, ncmiDFLT by default)
                       the maximum interval to wait between the start of an
                       event and the end of the event in thousandths (0.001)
                       of a second. Specify an interval OR a member of
                       enum ncmInterval.
      
        Returns:
           OK  if mouse-event reporting enabled AND the system supports 
               all requested event types
           ERR a) if mouse could not be enabled
               b) if optionally-specified 'interval' value is out of range
               c) one or more of the requested event types is not supported by
                  the system (any valid event types will still be reported)
                  In this case, the meGetEventTypes() method should be called
                  to determine which events will be reported.
      

      Enable reporting of mouse events. This method provides full access to the ncurses(w) C library mouse interface, (but see below).

      Mouse events are reported within the ’GetKeyInput’ keyboard input stream. (See meEnableStableMouse method, above.)

      The ’eventTypes’ parameter specifies the type(s) of mouse events which will be reported.These definitions apply to the ’ncursesw’ library version 5.9 (mouse version 1).
      If your system defines mouse version > 1, then please see the ncurses.h header file for making bitmask substitutions.

      Note that not all events will be reported in the same way by all system/hardware combinations.

     BUTTON1_RELEASED BUTTON2_RELEASED
     BUTTON1_PRESSED BUTTON2_PRESSED
     BUTTON1_CLICKED BUTTON2_CLICKED
     BUTTON1_DOUBLE_CLICKED BUTTON2_DOUBLE_CLICKED
     BUTTON1_TRIPLE_CLICKED BUTTON2_TRIPLE_CLICKED
     BUTTON1_RESERVED_EVENT BUTTON2_RESERVED_EVENT
      
     BUTTON3_RELEASED BUTTON4_RELEASED
     BUTTON3_PRESSED BUTTON4_PRESSED
     BUTTON3_CLICKED BUTTON4_CLICKED
     BUTTON3_DOUBLE_CLICKED BUTTON4_DOUBLE_CLICKED
     BUTTON3_TRIPLE_CLICKED BUTTON4_TRIPLE_CLICKED
     BUTTON3_RESERVED_EVENT BUTTON4_RESERVED_EVENT
      
     BUTTON_CTRL (CTRL key down) 
     BUTTON_SHIFT (SHIFT key down) 
     BUTTON_ALT (ALT key down) 
     REPORT_MOUSE_POSITION
     REPORT_SCROLL_WHEEL 

    Note: For convenience, we locally define the additional event-type mask item ’REPORT_SCROLL_WHEEL’ in NCurses.hpp:
    REPORT_SCROLL_WHEEL = BUTTON4_PRESSED | REPORT_MOUSE_POSITION ;
    Please see NCurses Mouse Access for details.

    Only the specified mouse-event types will be reported. All non-requested events will be silently discarded UNLESS the event filter is disabled (see the ’meAutoFilter’ method, below).

    Note that the BUTTON_CTRL, BUTTON_SHIFT and BUTTON_ALT flags are for reporting only, and will be reported regardless of whether you specify them in the bitmask.

    If the mouse driver or mouse hardware does not support a requested event type, then this method will return an error condition; however, any requested event types which ARE supported, will still be reported.

    The optional ’interval’ parameter specifies the intra-event delay. so choose an ’interval’ parameter that works for your system. To test the delay value, please see the test applications Dialog4, Test04 OR Dialogw, Test07, ’Set Mouse Click Interval’.

    Please see NCurses Mouse Access for a more detailed discussion of the low-level mouse interface.

    Note that although this method gives the application full access to the functionality of the low-level ncurses(w) C-library mouse interface, significant timing issues may affect the reliability of event reporting. For this reason, unless you actually need the greater functionality, we suggest that you design your application to use the ’meEnableStableMouse’ method described above.


  • short meDisableMouse ( void );
      Input  : none
    
      Returns: OK  if mouse events successfully disabled
               ERR otherwise
    

    Disable reporting of mouse events.
    The connection with the mouse interface will be disabled and no further mouse events will be placed in the input queue. Note that this MAY also disable the xterm mouse driver (system dependent).

    Note that any mouse events that have already arrived in the queue will still be available, so it is good practice to flush the input queue after disabling the mouse interface: see FlushKeyInputStream method.



  • bool meMouseEnabled ( void );
      Input  : none
    
      Returns:
         'true' if mouse events can be detected and reported
         else 'false'
    

    Reports whether mouse input is currently enabled.
    (See meEnableMouse method, above.)



  • short meSetEventTypes ( mmask_t eventTypes );
      Input  :
         eventTypes: new mouse-event-type mask
    
      Returns:
         OK  if mouse enabled AND all specified bits of the event-type 
             mask have been set successfully
         returns ERR if:
           a) actual event-type mask set differs from that specified
              (valid event types will still be reported)
           b) if eventTypes == ZERO (i.e. invalid mask)
           c) mouse interface enabled with meEnableStableMouse,
              so event types may not be modified
           d) mouse-event reporting not enabled
    

    Modify the type(s) of mouse events which will be reported.

    Please see Mouse Event Types for a complete list of available event types.



  • short meGetEventTypes ( mmask_t& eventTypes );
      Input  :
         eventTypes: (by reference, initial value ignored)
                     receives the mouse-event-type mask
    
      Returns:
         OK  if successful
         ERR if mouse-event reporting not enabled
    

    Reports the mouse-event-type mask for currently-enabled event types.
    Generally useful only if ’meEnableMouse’ or ’meSetEventTypes’ methods return an error.



  • short meSetClickInterval ( short waitMax, short* oldMax=NULL );
      Input  :
         waitMax : interval in thousandths (0.001) of a second
                   specify an interval OR a member of enum ncmInterval
         oldWait : (optional, NULL pointer by default)
                   if specified, this is a pointer to a short int variable 
                   to receive previous interval value
    
      Returns:
         OK  if successful
         returns ERR if:
           a) 'waitMax' value out-of-range
           b) mouse interface enabled with meEnableStableMouse,
              so intra-click delay may not be modified
           c) mouse-event reporting not enabled
    

    Specify the maximum interval between a button-press event and a following button-release event that will result in a mouse ’click’ being reported.

    Note that this inverval is optionally set when mouse-event reporting is is enabled (see meEnableMouse method)..

    Note: this interval applies to single-, double- and triple-click events, as well as ScrollWheel events. If the interval between press and release is greater than this value, then a ’click’ event will not be reported. (Note also that for a ’click’ event to be reported, both the press and release must occur in the same character cell.)

    The specified interval must be either a member of 'enum ncmInterval', OR a value between one(1) and ncmiMAX. The default interval is ncmiDFLT (166 milliseconds), which is about right for capturing a single-click event. If your users have physical disabilities, or if your application uses double or triple click events, then the click interval should be longer. If you are capturing ScrollWheel events, then the click interval should probably be somewhat shorter for smoother scrolling.



  • short meGetClickInterval ( short& waitMax );
      Input  :
         waitMax   : (by reference, initial value ignored)
                     receives current interval
    
      Returns:
         OK  if successful
         ERR if mouse-event reporting not enabled
    

    Get the current maximum mouse-click interval in thousandths of a second.



  • short meFlushEventQueue ( void );
      Input  : none
    
      Returns:
         OK  if successful
         ERR if mouse-event reporting not enabled
    

    Remove any mouse events waiting in the mouse-event queue.
    Note that any events that have already been transferred from the low-level mouse-event queue to the key-input queue will not be discarded. (see FlushKeyInputStream method)



  • short meAutoConvert ( bool enable );
      Input  :
         enable : 'true'  to enable auto-conversion (default setting)
                  'false' to disable auto-conversion
    
      Returns:
         OK  if successful
         ERR if mouse-event reporting not enabled
    

    Within an NcDialog window, mouse event are, by default, automatically and intelligently converted to keycodes (when possible), and will ignore (meaningless) mouse events which occur outside of any control object. This eliminates the need for the application to handle mouse events manually. In general, the less the application needs to know about mouse events, the better.

    There may be circumstances, however, when you want to temporarily disable this automatic conversion, but use caution: It means that the edit routines for the dialog controls will will probably ignore the mouse event completely, or may in some cases, return from the edit with the unprocessed mouse event, and your user-input loop would need to know what to do with it.



  • short meAutoFilter ( bool filter );
      Input  :
         filter : 'true' enables filtering
                  'false disables filtering
    
      Returns:
         OK  if successful
         ERR if mouse-event reporting not enabled
    

    Temporarily disable filtering of spurious (unrequested) mouse events.
    Filtering is enabled by default, and should REMAIN enabled except for testing.




Mouse Event Types

This is a list of the mouse-event types which are recognized by the ncurses(w) C-language library and are therefore available to the NcDialog API. The naming convention is based on the enum eTypes enumerated type. These are roughly equivalent to the bitmask definitions discussed in the see meEnableMouse method.

The mouse event types actually available to your application depend on your system configuration, your mouse hardware and other factors. You may need to experiment a bit to determine which events are visible to your application. To test event visibility, please see test application Dialog4, Test04.


 Event Type CodeDescription
 metNONE no event
 metB1_P Button 1 - button press
 metB1_R, Button 1 - button release
 metB1_S ** Button 1 - single click
 metB1_D ** Button 1 - double click
 metB1_T Button 1 - triple click
  
 metB2_P Button 2 - button press
 metB2_R, Button 2 - button release
 metB2_S Button 2 - single click
 metB2_D Button 2 - double click
 metB2_T Button 2 - triple click
  
 metB3_P Button 3 - button press
 metB3_R, Button 3 - button release
 metB3_S Button 3 - single click
 metB3_D Button 3 - double click
 metB3_T Button 3 - triple click
  
 metB4_P Button 4 - button press
 metB4_R, Button 4 - button release
 metB4_S Button 4 - single click
 metB4_D Button 4 - double click
 metB4_T Button 4 - triple click
  
 metB5_P Button 5 - button press
 metB5_R, Button 5 - button release
 metB5_S Button 5 - single click
 metB5_D Button 5 - double click
 metB5_T Button 5 - triple click
  
 metPOS Mouse pointer position report
 (not implemented in the ncurses
 library at this time)
 metSW_D ** Scroll-wheel - scroll down
 metSW_U ** Scroll-wheel - scroll up

Legend: ’**’ indicates that if enabled, NcDialog will translate the event type to a keycode when possible

Notes:

  • When a mouse event is received (see GetKeyInput method), the event type is reported in the ’meType’ data member.

    Please see Direct Mouse-event Example for an example of how to access this information.

  • Although the full range of events is available through the xterm mouse driver, the ncurses(w) C-language library and the NCurses class interface, the NcDialog class translates the mouse event to a keycode ONLY for the event types which are indicated by ’**’ in the above table.

    All other event types are currently passed through as mouse events with only the Y/X coordinates being converted from terminal relative to dialog relative offsets. In practical terms, this means that mouse events which are not translated to keycodes will be ignored by the user-interface methods for the individual controls. Additional mouse-event-to-keycode translations may be implemented in future releases. Please see Mouse Event Translation for additional details.

  • A click event is reported only if its component press and release events occur within same character cell.
  • The BUTTON2_PRESSED event is ’paste selection’ in GNOME/KDE terminal world, meaning that Button 2 click events will never be seen. This can be disabled in the Xterm configuration if desired.
  • The BUTTON3_PRESSED event is ’context menu’ in GNOME/KDE terminal world, meaning that Button 3 click events will never be seen. This can be disabled in the terminal setup if desired.
  • All ’SHIFT + Click’ events will probably be captured by the terminal, but ’SHIFT + ScrollWheel’ events will probably be reported to us.
  • Some event types may not be supported by your particular system or mouse driver version, and some event-type combinations are not recommended due to event conflicts.
    • For instance, requesting BUTTON4_CLICKED makes no sense if your mouse has no fourth button.
    • Because a ’click’ event is a combination of a ’press’ event and a ’release’ event, requesting:
          ’BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED’
      would produce ambiguous results, depending on whether the user’s finger was fast or slow.
    • In general, only Button 1 ’click’ events and ’ScrollWheel’ events are recommended for NcDialog API applications.
    • The low-level ncurses(w) library defines two sets of mouse-event bits: Mouse Version 1, and Mouse Version >1. The NcDialog API supports both versions, but all GNU/Linux distributions we have tested use Mouse Version 1, so that will be the focus of our discussion. (See ’ncurses.h’ for alternate bit definitions.)
  • The low-level ncurses(w)-library mouse events are very sensitive to timing-related issues. These timining issues are responsible for the poor overall performance of the standard NcDialog mouse interface. Please see meEnableStableMouse method for circumventing most of the timing issues related to the low-level mouse interface.
    Please see NCurses Mouse Access for additional information on timing issues.



Translating Mouse Events to Keycodes

Note: The mouse-to-keycode translation algorithm meets all
specifications described in this section; however, we may
refine the algorithm based on feedback from the community.


What follows is a discussion of how mouse events which occur above a user-interface control object are translated into meaningful keycodes.
Please see wkeyCode class for information on how the data are stored and transmitted, and see MouseEvent class for details on the captured mouse data.

All of this translation occurs automatically in the background, so the application does not have to interpret mouse data; however, for an understanding of the user interface, it is useful to know the correspondence between mouse event and keycode.

The way mouse events are interpreted depends upon the type of mouse event detected as well as the type of user-interface control with which the mouse event is associated.

Mouse events that occur within the dialog window but NOT above a control object are meaningless and will be silently discarded, EXCEPT that if a control is in the ’expanded’ state, then a click event that is not associated with that control will collapse the expanded control.

Mouse events that occur outside any dialog window belong to someone else and are not available to the application.

For all active controls an associated mouse ’click’ event moves the input focus to that control as if the user had pressed a ’hotkey’ combination associated with the control. Note that it is not necessary to explicitly assign a hotkey to a control in order for the mouse interface to function properly.
Note: Hotkeys for all user interface control objects are masked when an expanded Menuwin control has the input focus (because line items in Menuwin controls have their own hotkeys).
For more information on hotkeys, please see Select Control via Hotkey.

Mouse ’ScrollWheel’ events apply to some control types but not to other types as detailed below.

Mouse event types in the following table are referenced as members of enum meTypes, and the event type is located in the ’meType’ member of the MouseEvent class. Please refer to the discussion of event types: see Mouse Event Types.

Note that left-handed users often swap mouse Button #1 and Button #3. This swap takes place at the mouse-driver level, so that for purposes of this discussion, the ’left’ mouse button is whatever button you have assigned as the primary button during system configuration.


  • Pushbutton Controls
    metB1_S : single click of left button
    metB1_D : double click of left button
    
    This is equivalent to the user ’pressing’ the button.
     a) If the target control is not the control with input focus,
        convert the mouse event to the hotkey associated with the target
        control. This will give the control focus AND automatically
        indicates that the button has been pressed.
     b) If the target control is already the control with
        input focus, then the mouse event is converted to an nckENTER 
        key.
    
    All other mouse event types are ignored for Pushbutton controls.
    
    
  • Radiobutton Controls
    metB1_S : single click of left button
    metB1_D : double click of left button
    
    This is equivalent to the user either ’toggling’ or ’selecting’
    the button (depending on whether the button is grouped).
     a) If the target control is not the control with input focus,
        convert the mouse event to the hotkey associated with the target
        control. This will give the control focus AND automatically
        indicates that the button has been ’selected’ (set).
     b) If the target control is already the control with
        input focus, then the mouse event is converted to an nckSPACE
        key.
     If the control is an independent Radiobutton, then nckSPACE
          toggles the state of the button.
     If the control is a member of an XOR Radiobutton group, 
          then nckSPACE sets the state of the button.
     Please see GroupRadiobuttons method for more information
          on XOR grouping, or see Your First Application for an
          example of grouping Radiobutton controls.
    
    All other mouse event types are ignored for Radiobutton controls.
    
    
  • Textbox Controls
    metB1_S : single click of left button (no modifier keys)
    
    The action is dependent on whether the control currently has focus.
     a) If the target control is not the control with input focus,
        then convert the mouse event to the hotkey associated with the
        target control. This will give the control focus.
     b) If the target control is the control with
        input focus, then these events are currently ignored.
    
    
    metB1_S : single click of left button (with CTRL modifier key)
    metB1_D : double click of left button
    
    The action is dependent on whether the control currently has focus.
     a) If the target control is not the control with input focus,
        then the event has the same effect as a single-click
        with no modifier key.
     b) If the target control is the control with input
        focus, then convert the event to the nckENTER key which will
        complete the edit and move the input focus to the next control.
    
    
    metSW_U : Scroll-wheel - scroll up
    metSW_D : Scroll-wheel - scroll down
    
    This is equivalent to pressing the Right or Left navigation keys.
     a) If the target control is not the control with input focus,
        these events are ignored.
     b) If the target control is the control with input focus:
        metSW_U is converted to the nckRIGHT navigation key, which will
                move the highlight one character toward the end of the
                data, if any.
                (with SHIFT modifier key, converted to nckSRIGHT)
        metSW_D is converted to the nckLEFT navigation key, which will
                move the highlight one character toward the beginning
                of the data, if any.
                (with SHIFT modifier key, converted to nckSLEFT)
        The data will be scrolled as necessary to bring the target
        character into view.
        Note that the sense of Right and Left are reversed if the control
        contains RTL language text.
    
    All other mouse event types are ignored for Textbox controls.
    
    
  • Scrollbox Controls and
  • Scrollext Controls
    metB1_S : single click of left button
    metB1_D : double click of left button
    
    The action is dependent on whether the control currently has focus.
     a) If the target control is not the control with input focus,
        then a click event anywhere on or within the control’s borders will
        be converted to the hotkey associated with the target control.
        This will give the control focus.
     b) If the target control is the control with input focus, then:
     if the event occurred in the control’s top border, then
          the event is converted to the nckUP navigation key, which
          will move the highlight upward to the previous line item, if any.
     if the event occurred in the control’s bottom border or
          in the left or right border, then the event is converted to the 
          nckDOWN navigation key, which will move the highlight downward
          to the next line item, if any.
     if the event occurs on a line item within the control, then
          the highlight is moved to that line item, and the event is
          converted to the nckENTER key which 'selects' that line item.
        The data will be scrolled as necessary to bring the target line
        item into view.
    
    
    metSW_U : Scroll-wheel - scroll up
    metSW_D : Scroll-wheel - scroll down
    
    This is equivalent to pressing the Up or Down navigation keys.
     a) If the target control is not the control with input focus,
        then these events are ignored.
     b) If the target control is the control with input focus, then:
        metSW_U is converted to the nckDOWN navigation key, which will
                move the highlight downward to the next line item, if any.
        metSW_D is converted to the nckUP navigation key, which will
                move the highlight upward to the previous line item, if any.
        The data will be scrolled as necessary to bring the target line
        item into view.
    

    All other mouse event types are ignored for Scrollbox and Scrollext controls.


  • Dropdown Controls
    metB1_S : single click of left button
    metB1_D : double click of left button
    
    The action is dependent on whether the control currently has focus and 
    whether the control is in the 'collapsed' or 'expanded' state.
     a) If the target control is in the collapsed state (with or
        without focus), then convert the mouse event to the hotkey 
        associated with the target control. This will give the control
        focus AND automatically indicates that the control is to be 
        immediately ’expanded’.
     b) If the control is in the expanded state, then it has
        focus by definition.
     if the event occurred in the control’s top border, then
          the event is converted to the nckUP navigation key, which
          will move the highlight upward to the previous line item, if any.
     if the event occurred in the control’s bottom border or
          in the left or right border, then the event is converted to the 
          nckDOWN navigation key, which will move the highlight downward
          to the next line item, if any.
     if the event occurs on a line item within the control, then
          tne highlight is moved to that line item, and the event is
          converted to the nckENTER key which 'selects' that line item.
          This selection returns the control to the 'collapsed' state.
        The data will be scrolled as necessary to bring the target line
        item into view.
    
    
    metSW_U : Scroll-wheel - scroll up
    metSW_D : Scroll-wheel - scroll down
    
    This is equivalent to pressing the Up or Down navigation keys.
     a) If the target control is not the control with input focus,
        or if the control is in the collapsed state, then these events
        are ignored.
     b) If the target control is the control with input focus,
        and is in the expanded state:
        metSW_U is converted to the nckDOWN navigation key, which will
                move the highlight downward to the next line item, if any.
        metSW_D is converted to the nckUP navigation key, which will
                move the highlight upward to the previous line item, if any.
        The data will be scrolled as necessary to bring the target line
        item into view.
    
    All other mouse event types are ignored for Dropdown controls.
    
    
  • Menuwin Controls
    metB1_S : single click of left button
    metB1_D : double click of left button
    
    The action is dependent on whether the control currently has focus, 
    whether the control is in the 'collapsed' or 'expanded' state, and 
    whether the control is a top-level menu or a submenu.
     a) If the target control is in the collapsed state (with
        or without focus), then convert the mouse event to the hotkey 
        associated with the target control. This will give the control
        focus AND automatically indicates that the control is to be 
        immediately ’expanded’.
    
     b) If the target control is in the expanded state, AND
        has input focus:
     if the event occurred in the title area, then the event
          is ignored.
     if the event occurred in the control’s top border, then
          the event is converted to the nckUP navigation key, which
          will move the highlight upward to the previous line item, if any,
          or will wrap around to the last item.
     if the event occurred in the control’s bottom border or
          in the left or right border, then the event is converted to the 
          nckDOWN navigation key, which will move the highlight downward to
          the next line item, if any, or will wrap around to the first item.
     if the event occurs on a line item within the control, then
          the highlight is moved to that line item, and the event is 
          converted to the nckENTER key which 'selects' that line item.
          This selection returns the control to the 'collapsed' state.
    
     c) If the target control is in the expanded state, BUT
        does not have focus, then:
        the target control is the parent of a submenu which does have
        focus. The event is converted into the nckLEFT key, which will
        collapse the submenu which has focus. This may, or may not return
        focus to the target control (depending on how many levels of 
        submenu are expanded).
    
    
    metSW_U : Scroll-wheel - scroll up
    metSW_D : Scroll-wheel - scroll down
    
    This is equivalent to pressing the Up or Down navigation keys.
     a) If the target control is not the control with input focus,
        or if the control has focus but is in the collapsed state,
        then these events are ignored.
     b) If the target control is the control with input
        focus, and the control is in the expanded state:
        metSW_U is converted to the nckDOWN navigation key, which will
                move the highlight downward to the next line item, if any,
                and otherwise will wrap around to the first line item.
        metSW_D is converted to the nckUP navigation key, which will
                move the highlight upward to the previous line item, if
                any, and otherwise will wrap around to the last line item.
    
    All other mouse event types are ignored for Menuwin controls.
    

    Please see Menuwin Controls for more information on menus and ’MenuBar’ menu groups.


  • Spinner Controls
    metB1_S : single click of left button (no modifier keys)
    
    The action is dependent on whether the control currently has focus.
     a) If the target control is not the control with input focus,
        then convert the mouse event to the hotkey associated with the
        target control. This will give the control focus.
     b) If the target control is already the control with
        input focus, then the event is ignored.
    
    
    metB1_S : single click of left button (with CTRL modifier key)
    metB1_D : double click of left button
    
    The action is dependent on whether the control currently has focus.
     a) If the target control is not the control with input focus,
        then the event has the same effect as a single-click
        with no modifier key.
     b) If the target control is the control with input
        focus, then convert the event to the nckENTER key which will
        complete the edit and move the input focus to the next control.
    
    
    metSW_U : Scroll-wheel - scroll up
    metSW_D : Scroll-wheel - scroll down
    
    This is equivalent to pressing the Up or Down navigation keys.
     a) If the target control is not the control with input focus,
        then these events are ignored.
     b) If the target control is the control with input focus:
        metSW_U is converted to the nckUP navigation key, which will
                increase the value in the control by one(1).
            - If the SHIFT key is held down when this event is received,
              the event is converted to the nckSUP (SHIFT+UpArrow) 
              key, which will increase the value in the control by ten(10).
            - If the CTRL key is held down when this event is received,
              the event is converted to the nckCUP (CTRL+UpArrow) 
              key, which will increase the value in the control by 100.
            - If the ALT key is held down when this event is received,
              the event is converted to the nckAUP (ALT+UpArrow) 
              key, which will increase the value in the control by 1000.
        metSW_D is converted to the nckDOWN navigation key, which will
                decrease the value in the control by one(1).
            - If the SHIFT key is held down when this event is received,
              the event is converted to the nckSDOWN (SHIFT+DownArrow)
              key, which will decrease the value in the control by ten(10).
            - If the CTRL key is held down when this event is received,
              the event is converted to the nckCDOWN (CTRL+DownArrow)
              key, which will decrease the value in the control by 100.
            - If the ALT key is held down when this event is received,
              the event is converted to the nckADOWN (ALT+DownArrow)
              key, which will decrease the value in the control by 1000.
    
    All other mouse event types are ignored for Spinner controls.
    
    

    Please see spinner key input for more information on key input to Spinner controls.


  • Billboard Controls
    Billboard controls are never active; they are read-only controls from the user’s point of view. Thus, they cannot receive the input focus, and cannot be associated with mouse events.

    Even if you mistakenly set a Billboard control as ’active’ in your application, input to that control will be ignored.
    Please see Billboard Controls for more information.




Direct Mouse-event Access Example

An instance of the MouseEvent class lives within the ’wkeyCode’ class, and if a mouse event occurs, it will contain the mouse-event information in the MouseEvent-class public data members. Whether the event is also converted to a keycode depends on the type of event, the position of the event in the dialog, and whether auto-conversion is enabled.

Please see MouseEvent class for additional details on direct access to mouse-event data.

Please Note: This example is provided for informational purposes only. In a production-worthy application, all mouse data are processed by the user-input method for each user-interface control type.
Please see User Interface Loop for additional information.



//** Instantiate the dialog window and user-interface controls **
NcDialog* dp = new NcDialog( dInit ) ;
  .  .  .
// Report the following mouse event types only:
mmask_t emask = BUTTON1_CLICKED | BUTTON2_CLICKED | REPORT_SCROLL_WHEEL ;
if ( (dp->meEnableMouse ( eMask )) != OK )
{ /* report initialization failure, and do recovery */ }
  .  .  .

gString gs ;               // used for formatting the output
wkeyCode wk ;              // receives the input data
dp->GetKeyInput ( wk ) ;   // get input
if ( wk.type == wktMOUSE ) // mouse event?
{
   gs.compose( "Mouse Event:\n"
               "position Y:%hd  X:%hd\n"
               "target control object index: %hd\n"
               "event type (see enum meTypes): %hd\n"
               "control key pressed: %hhd\n"
               "shift key pressed  : %hhd\n"
               "alt key pressed    : %hhd\n",
               &wk.mevent.ypos, &wk.mevent.xpos, &wk.mevent.cIndex,
               &wk.mevent.meType,
               &wk.cKey, &wk.sKey, &wk.aKey ) ;
}
else if ( wk.mevent.conv != false ) // converted mouse event?
{
   gs.compose( "Mouse event converted to keycode.\n"
               "Key Type (see enum wkeyType): %hd  Keycode: 0x%08X\n",
               &wk.type, &wk.key ) ;
}
else
{
   gs = "No mouse event, keyboard input only\n" ;
}

// Report the results
dp->WriteParagraph ( 1, 1, gs, nc.blR, true ) ;

dp->meDisableMouse ();        // Disable mouse-event reporting



Implementation Notes

Introduction

Mouse support for console applications is implemented in four(4) layers:

  1) Xterm mouse driver
  2) ncursesw C-language library interface to xterm
  3) NCurses interface to the ncursesw library
  4) NcDialog interpretation of the raw NCurses mouse-event data

None of these layers are very sophisticated nor particularly robust, so while mouse support is available for NcDialog-based console applications, please don’t expect a pristine implementation. However, considering the simple data stream we have available, the NcDialog API does what is possible to provide a moderately-useful interpretation of mouse input.
Please see meEnableStableMouse method for our best effort.


Data-flow Diagram

╔═════════════════╣ NcDialog API -- Mouse Data Flow ╠═════════════════╗ ┌──────────────────┐ │ │ XTERM │ │ └───────────────┘ ┌──────────────────┐ ┌───────────────┐ NcDialog-based │ │ Application NCURSES LIBRARY ( unaware of ) │ │ (mouse events) └───────────────┘ └───────────────┘ ┌───────────────┐ ┌──────────────────┐ │ │ │ │ NcDialog API: ├──▶─┤ NcDialog API: ├─┘ NCurses Class ├─◀──┤ NcDialog Class ├───┘ (capture/filter) │ │ (translation) └──────────────────┘ └──────────────────┘ ╚═══════════════════════════════════════════════════════════════════════╝


Implementation

  • Mouse data are delivered to the application through the key input stream, specifically, through the ’GetKeyInput’ method.
  • The ’GetKeyInput’ method translates the mouse position data from terminal-window-relative to dialog-window relative, so that the mouse event may be associated with the dialog control object which is directly under the mouse cursor. See diagram above.
  • Because the raw mouse event data are actually meaningless to an application, where possible, the ’GetKeyInput’ method also translates the mouse event into the equivalent keycode (button press, arrow key and so on), which can be used directly by the user interface.

    Because users cannot be expected to memorize complex mouse click sequences, and because the low-level xterm/ncursesw mouse interface is rather simplistic, only Button #1 (left mouse button) can be relied upon across all system configurations. Mouse hardware for most desktop/laptop systems also include ScrollWheels (or ScrollWheel emulation). The ’modifier keys’ (CTRL, ALT, SHIFT) are also often available. Beyond this, the GUI interface and the terminal emulator software will probably capture or redirect most mouse events for other purposes. For this reason, the NcDialog API uses only these event types in translation of mouse events to keycodes:

    Button #1 single-click events (metB1_S)
    Button #1 double-click events (metB1_D)
    ScrollWheel events            (metSW_U and metSW_D)
    with or without 'modifier keys'.
    

    Please see Mouse Event Translation for more information.

  • Your terminal emulation software will probably also provide the mouse-based selection and paste-selection events.
    Please see Interface to X-clipboard for more information on mouse-based copy-and-paste operations.
  • IMPORTANT NOTE: When mouse events are disabled, then the xterm mouse driver may convert ScrollWheel events to a series of UpArrow or DownArrow keys (usually groups of 3). This is a standard feature of terminals; however, within an NcDialog application, this feature will very likely confuse some users. If it does, then we suggest disabling this feature in the terminal configuration, or try the following:

    If you don’t use the mouse interface in your application, but you also don’t want the xterm ScrollWheel auto-conversions, there is a hack available:

    Example:
       dp->meEnableMouse ( REPORT_SCROLL_WHEEL ) ;
       dp->meAutoConvert ( false ) ;
    

    When mouse events are enabled, then xterm’s auto-conversion feature is disabled, and we receive the actual mouse events as expected.

  • To play with the NcDialog API mouse interface, please see the test application Dialog4, Test04 and Test06.
  • To play with the lower-level NCurses-class mouse interface, please see the test application Dialog4, Test03.
  • For a more detailed discussion of the underlying implementation structure, please see NCurses Mouse Access.



Utility Methods

The methods in this group fall into two categories: passthrough methods for access to lower-level functionality, and convenient, higher-level methods which can be used for common user interface tasks.


  • void InfoDialog ( genDialog& gd );
      Input  :
         gd : initialized genDialog class object (by reference)
    
      Returns: OK
    

    Generic information dialog.

    Within an application, there is often the need for a simple sub-dialog window which displays a message and waits for the user to finish reading the message before closing the window. It would be tedius and time consuming for an application developer to build individual dialog windows for each message the user needs to see.

    The InfoDialog method is exactly what the name implies—the simplest kind of dialog which displays the specified message and waits for the user to close the dialog window.

    The InfoDialog method may be used to display any list of constant strings, then waits for user to press ENTER (or ESC).

    Give attention to the formatting of line items within the dialog. Line items are drawn leaving one empty column at the edge of the dialog. Therefore when possible leave at least one empty column at the opposite edge. This is not enforced, but style counts for two reasons. First, it will give the user confidence that you, the application designer are not just a hairy troglodyte; and second, a well-crafted message will have a calming influence on a nervous user.

    Please see genDialog class for information and examples of generating an Information dialog.



  • bool DecisionDialog ( genDialog& gd );
      Input  :
         gd : initialized genDialog class object (by reference)
    
      Returns:
         'true' if user selects 'YES'
         else 'false'
    

    Generic decision dialog.

    Within an application, there is often the need for a simple sub-dialog which asks the user a simple ’Yes’-or-’No’ question. Use this method to display any list of constant strings that ends with such a question, and then wait for user to select either the ’YES’ or ’NO’ pushbutton (the ESC key also equals ’NO’).

    For reasons of beauty, messages begin offset by two(2) in X. See note in ’InfoDialog’ method above.

    Note that the question posed by the dialog should be written in such a way that can be answered with either a ’Yes’ or ’No’ response.

    Please see genDialog class for information and examples of generating a Decision dialog.



  • short UserAlert ( AudibleAlert* pingParms=NULL );
      Input  :
         pingParms : (optional, NULL pointer by default)
                     If specified, provides parameters for a ping sequence.
                     If not specified, a single ping is sounded.
      Returns:
         OK  if specified operation is supported by the system 
         ERR if alert not supported by the system
    

    Sound an audible alert to attract user’s attention.

    If setup parameters are provided the alert will follow the specified ping sequence until the terminal count is reached OR until user presses any key. If no parameters are provided, then a single ping will be generated, followed by an immediate return.

    Note that if user presses a key during the sequence, the key-input buffer will be flushed before returning; however, a caffinated user may manage to press a key we don’t see, so caller should check the key-input buffer on return.

    Note that this method supports only the ’beep’, not the ’flash’ functionality; however, the way the ’ncursesw’ C library implements these is that if the alert type specified by the caller is not supported by the system, then the other will be called. If neither is supported (unlikely), then nothing happens.

    Important Note: A very small value for ’pInterval’ may not be recognized due to the low-resolution timer used by the underlying C code. Range checking is performed (minUAINTERVAL) to keep the delay within the capabilities of most systems.

    While the NCurses class also includes a UserAlert() method, it is a simple, parameter-less beep. The NcDialog-class implementation is somewhat more sophisticated, yet still annoying enough to get the user’s attention.

    The following example is excerpted from the NcDialog API documentation example code.

    AudibleAlert aa( 3,  // number of pings in sequence
                     3,  // delay between pings (1/20 sec)
                     2,  // repeat count
                     5   // delay between repeats (1/10 sec)
                   ) ;
    dp->UserAlert ( &aa ) ;
    
    

    Please see AudibleAlert class for additional information and examples.



  • short UserAlert ( short count,
                      short interval = (minUAINTERVAL * 2) );
      Input  :
         count    : number of pings
         interval : delay between pings (optional)
                    a) if specified, then this is the inter-ping delay
                       in 20ths of a second
                    b) otherwise delay is set to approximately 0.2 seconds
                       which is a short, but reliable interval
    
      Returns:
         OK  if specified operation is supported by the system 
         ERR if alert not supported by the system
    

    Sound an audible alert to attract user’s attention.

    This is a simplified version of the UserAlert method above, and does not require full initialization. Instead of receiving the parameters in an AudibleAlert object, this method takes only a ping count and an (optional) inter-ping delay.



  • void Hibernate ( void ) ;
      Input  : none
    
      Returns: nothing
    

    Temporarily disable the NCurses engine so external terminal applications can gain full access to the terminal’s functionality.

    Call the ’Wake’ method to to re-activate the NCurses engine.

    The following example is a typical sequence of events for giving the user access to the command line.

    dp->SetDialogObscured () ; // save display data
    dp->Hibernate () ;         // put NCurses Engine to sleep
    
    // execute a terminal command
    
    dp->Wake () ;              // wake the NCurses Engine
    dp->RefreshWin () ;        // restore dialog display data
    

    IMPORTANT NOTE: The Hibernate/Wake sequence shown above is quite primitive. At the application level, calling the ’ShellOut’ method is much preferred for giving the user access to the command line.
    Please see ShellOut method below for more information.



  • void Wake ( void ) ;
      Input  : none
    
      Returns: nothing
    

    Re-enable the NCurses engine after a previous call to ’Hibernate’.
    Also flushes any remaining data from the keyboard input stream.

    See the example Hibernate/Wake sequence in the ’Hibernate’ method above.



  • void ShellOut ( soOptions = soE, const char* shellCmd = NULL,
                    const char* textColor = NULL );
      Input  :
         option   : member of enum soOptions (optional, 'soE' by default)
         shellCmd : (optional, NULL pointer by default)
                    - if specified, invoke the specified shell builtin
                      command, shell program or console utility program
                    - if NULL pointer, then invoke a copy of 'bash' shell
                      NOTE: If your default shell IS NOT 'bash', then you
                      should use this parameter to specify your shell
                      program.
         textColor: (optional, NULL pointer by default)
                    ANSI escape sequence which specifies the color of text
                    written to standard output. (see below)
      Returns: nothing
    

    Go to command shell.

    1. save the dialog display data
    2. put the NCurses Engine to sleep
    3. clear terminal screen if specified
    4. display ’exit-to-return’ message if specified
    5. invoke ’bash’ or the specified built-in or utility command
    6. on return:
      1. display ’press-Enter-key’ message if specified
      2. pause for Enter key if specified
      3. wake the NCurses Engine
      4. restore the dialog window

    Important Note:
    This method should be called ONLY from the primary dialog, AND
    there should be no sub-dialogs open. If invoked from a
    sub-dialog, then the primary dialog’s display data may not be
    properly saved.



    Examples:
      // invoke shell, default color with start msg
      dp->ShellOut ();
      // clear screen, write start msg, then invoke shell (blue text)
      dp->ShellOut ( soCE, NULL, "\033[0;34m" );
      // execute 'ps' utility (red text), then pause
      dp->ShellOut ( soP, "ps", "\033[0;31m" );
      // clear screen, execute 'cat' utility (default color), then pause
      dp->ShellOut ( soCP, "cat Makefile" );
    

    Please see the test application Dialog4, Test08 for additional examples.

    Note that there is also an Easter Egg for the ShellOut method in the Dialog1 test application, Test10, Menuwin_A.


    Applying ANSI Colors to the Standard Output Stream

    ANSI Color Escape Sequences (partial list):
    
    =========== Foreground  Background   ============ Foreground  Background
    Black       \033[0;30m  \033[0;40m   Dark Gray    \033[1;30m  \033[1;40m
    Red         \033[0;31m  \033[0;41m   Bold Red     \033[1;31m  \033[1;41m
    Green       \033[0;32m  \033[0;42m   Bold Green   \033[1;32m  \033[1;42m
    Brown       \033[0;33m  \033[0;43m   Yellow       \033[1;33m  \033[1;43m
    Blue        \033[0;34m  \033[0;44m   Bold Blue    \033[1;34m  \033[1;44m
    Purple      \033[0;35m  \033[0;45m   Bold Purple  \033[1;35m  \033[1;45m
    Cyan        \033[0;36m  \033[0;46m   Bold Cyan    \033[1;36m  \033[1;46m
    Light Gray  \033[0;37m  \033[0;47m   White        \033[1;37m  \033[1;47m
    

    Note that if your terminal does not support ANSI colors, then specifying ’textColor’ will have no effect (except writing some garbage into the window).

    It is assumed that the existing foreground/background of the terminal window is Black text on a White background. If this is not the case, then this method could cause minor color artifacts.

    Although ’textColor’ may specify both foreground and background color, a non-default background color may not be handled uniformly by the terminal. For this reason, it is recommended that only a foreground color be specified. However, if you feel lucky, please be sure to specify the background color sequence first:
    Example, Yellow-on-Blue: "\033[0;44\033[1;33m"

    Note that some shell commands add ANSI escape sequences of their own, and these may conflict with or override the ANSI sequence you specify.

    Note that under rare circumstances, when you exit the dialog application, the cursor may retain the color (if any) you specified with ’textColor’.

    Note that if ’shellCmd’ specifies a shell builtin command, and if ’textColor’ is specified, then the command will probably use the specified color, but this cannot be guaranteed.
    dp->ShellOut ( "ls", true, "\033[1;31m" ) ;
    dp->ShellOut ( "cat Makefile", true, "\033[1;31m" ) ;

    Console utilities are more independent and will probably not use the specified color.
    dp->ShellOut ( "less Makefile", true, "\033[1;31m" ) ;



  • void PseudoShell ( void );
      Input  :
      Returns:
    

    *** NOT YET IMPLEMENTED ***



  • void ScreenDimensions ( short& scrLines, short& scrColumns ) ;
      Input  :
         scrLines   : (by reference) receives screen line-count
         scrColumns : (by reference) receives screen column-count
    
      Returns: nothing
    

    Returns the terminal window dimensions in lines and columns.
    This is a pass-through method to the NCurses-class method of the same name.

    While you have probably checked the terminal screen dimension during application startup, the user may re-size the terminal window while your application is running. (User’s can be SO annoying :)

    Two things happen when the user re-sizes the terminal window:

    1. The dialog window needs to be refreshed.
      The NcDialog API internally monitors the size of the terminal screen and automatically refreshes the dialog when the screen dimensions change.
    2. The terminal screen may now be too small to hold your application dialog or may affect the performance of your application in other ways.

      For one possible solution, see the example, below.

    // start your application
    const short dialogROWS = 32, dialogCOLS = 132 ;
    
    ..  ..  ..
    
    // Place the following test at the top of your user-input loop:
    short dy, dx ;
    dp->ScreenDimensions ( dy, dx ) ;
    if ( dy < dialogROWS || dx < dialogCOLS )
    {
       attr_t attrList[] = 
       { nc.rebl, dColor, dColor, nc.reB, dColor, dColor, dColor } ;
       const char* msgList[] = 
       {
          "  Hey, Dumb Guy!  ",
          " ",
          "  The terminal screen must be at least",
          "          32 Rows by 132 Columns          ",
          "for the application to function properly.",
          " ",
          NULL
       } ;
       genDialog gd ( msgList, dColor, 9, 46, ZERO, ZERO, attrList ) ;
       dp->InfoDialog ( gd ) ;
    }
    

    ┌────────────┤ Hey, Dumb Guy! ├────────────┐ │ │ │ The terminal screen must be at least │ 32 Rows by 132 Columns │ for the application to function properly. │ │ │ │ │ OK └────────────────────────────────────────────┘


  • const char* Get_NcDialog_Version ( void );
      Input  : none
    
      Returns: pointer to const string
    

    Returns a pointer to the const string containing the NcDialog API version number. Note that this is also the NcDialog-class version number.

    The actual version number string, ’NcDialogVersion’ is located at the top of the NcDialog.cpp source module. The format is ’M.m.pp’, where ’M’ is the Major release, ’m’ is the Minor release and ’pp’ is the patch release.

    Example:
      NcDialog* dp = new NcDialog ( dInit ) ;
      . . .
    
      dp->WriteString ( 2, 0, dp->Get_NcDialog_Version(), nc.gr, true ) ;
    


  • const char* Get_NcWindow_Version ( void );
      Input  : none
    
      Returns: pointer to const string
    

    Returns a pointer to the const string containing the NcWindow-class version number.



  • const char* Get_NCurses_Version ( void );
      Input  : none
    
      Returns: pointer to const string
    

    Returns a pointer to the const string containing the NCurses-class version number.



  • const char* Get_nclibrary_Version ( void );
      Input  : none
    
      Returns: pointer to const string
    

    Returns a pointer to the const string containing the ’ncursesw’ C-language link library version number.

    Note that the library returns a longer string, but we report only the actual version number with release date.
    Example: 5.9.20130511




Development and Debugging

The following methods are for use ONLY during development. While these methods are stable and reliable, for efficiency reasons, they make intimate assumptions about the way the data are stored, retrieved and presented, and so are more likely to break if the underlying data definitions change. For this reason, these methods should not be used in production code.

Although the code for these methods is reasonably compact (less than 100Kb compiled), all development methods (except ’DebugMsg’) live under the conditional compilation flag: ENABLE_DEVELOPMENT_METHODS, located in ’NcDialog.hpp’, and can be excluded from production builds.


  • void Dump_uiInfo ( winPos wPos, uiInfo info ) ;
      Input  :
         wPos : screen coordinates for display of the sub-dialog
         info : uiInfo-class object containing the data to be displayed
         moose: (optional, 'false' by default)
                if 'true', then display mouse-event data (if any)
    
      Returns: nothing
    

    Open a sub-dialog window and display the contents of a uiInfo-class object returned from one of the control editing routines.
    Please see NcDialog Public Methods for a discussion of the individual Edit_xxx methods.

    This method is useful for visually verifying the data returned when the user has finished interacting with a particular control object.
    Please see uiInfo class for a discussion of the data displayed.

    Example:
    
       uiInfo Info ;                    // user-supplied data
       winPos wPos(2,1) ;               // position of sub-dialog
    
       dp->EditPushbutton ( Info ) ;    // get user input
       dp->SetDialogObscured () ;       // save parent dialog display
       dp->Dump_uiInfo ( wPos, Info ) ; // view the data
       dp->RefreshWin () ;              // restore parent dialog
    

    Examples of the Dump_uiInfo dialog for key and mouse input.

    ┌──────┤ Dump uiInfo ├───────┐ │ │ │ ctrlType : dctRADIOBUTTON │ │ ctrlIndex : 7 │ │ hasFocus : false │ │ dataMod : false │ │ keyIn : 0x0009 │ │ selMember : 24 │ │ isSel : false │ │ viaHotkey : true │ │ │ │ h_ctrlType : dctPUSHBUTTON │ │ h_ctrlIndex: 0 │ │ h_hasFocus : true │ │ h_dataMod : true │ │ h_keyIn : 0x0000 │ │ h_selMember: 24 │ │ h_isSel : true │ Press Any Key └────────────────────────────┘

    ┌──────┤ Dump uiInfo ├───────┐ │ │ │ ctrlType : dctPUSHBUTTON │ │ ctrlIndex : 0 │ │ Moose Droppings │ │ eventType (32-bit mask): │ │ 0000.0000.0000.0000. │ │ 0000.0000.0000.0100b │ │ deviceID : 0 │ │ screen y:24 x:106 z:0 │ │ dialog y:21 x:100 z:0 │ │ cIndex : 9 │ │ meType : metB1_S │ │ cKey : false │ │ sKey : false │ │ aKey : false │ │ mPos : false │ │ conv : true │ Press Any Key └────────────────────────────┘


  • short CaptureDialog ( const char* fPath, bool fhtml = false,
                          bool timeStamp = true,
                          const char* stylePath = NULL,
                          short cellsPerLine = 4,
                          bool lineComments = true,
                          attr_t simpleColor = ZERO ) ;
      Input  :
         fPath       : full path/filename for data to be saved
         fhtml       : (optional, false by default)
                       if 'false', then save as text data
                                   (viewable in text editor)
                       if 'true',  then save as HTML data
                                   (viewable in web browser)
         timeStamp   : (optional, default == true)
                       if 'true', then write the timestamp record at the 
                                  top of the output file.
                       if 'false, then do not write the timestamp record
         stylePath   : (optional, HTML output only)
                       if specified, path/filename to CSS definition file
                       (path will be written into the HTML <head></head>)
                       default == "screenshot-styles.css"
         cellsPerLine: (optional, HTML output only)
                       range: 1-40,  default == 4
                       if specified, the number of <td></td> cells written
                       per output line
         lineComments: (optional, HTML output only, default == true)
                       if 'true', then the raw text of each line is written
                                  as a comment for easy searching
                       if 'false', then do not write the comment lines
         simpleColor : (optional, HTML output only, default == ZERO)
                       interior color of the of the dialog being captured
                       Referenced ONLY when simple HTML capture has been
                       specified (see below for details)
    
      Returns:
         OK if successful
         ERR  : a) if parameter out-of-range
                b) if unable to write data to specified file
                c) if development methods are disabled
    

    Capture the dialog’s display data, text and color attributes to a file. This method provides three(3) output formats, each with its particular use.

    1. Plain Text Format
      Plain Text format writes the captured data to a plain text (UTF-8) file which can be read by any text editor or by text-viewing tools such as ’less’, ’more’, or ’cat’. The output of course is in simple black-and-white, with the formatted text data written first, followed by the color attributes written as a table of integers in ASCII hexidecimal.

      The text portion may be copied into another document such as the this one. Several examples of this capture are available throughout this document. Please see NcDialog Class Overview for one such example.

    2. Complex HTML Format
      The Complex HTML format creates an HTML document which relies on the CSS (Cascading Style Sheet) definition file: ’screenshot-styles.css’.
      Please see CSS for screenshots, below for a description of these CSS definitions.

      The data are formatted as an HTML <table> element to ensure consistent horizontal and vertical aligment and to maximize the number of color attributes which can be rendered into the browser window.

      By default, browser rendering engines automatically reformat the HTML document according to the rules of HTML syntax, character, word and line spacing schemes, and various non-standard or outrageously-clunky internal rules. For this reason, placing our data into a table is the only reliable way of preventing the browser from screwing with our formatting.

      Each character is placed into a <td> cell with its own color attribute as defined in the CSS definitions.

      Example:  <td class="bl_r">A</td>
      

      This presents a rather good-looking HTML document, with the downside that the document is rather large.

    3. Simple HTML Format
      The Simple HTML format creates an HTML document which relies on the CSS (Cascading Style Sheet) definition file: ’infodoc-styles.css’ which is Software Sam’s CSS definitions for Texinfo documentation output in HTML format.
      Please see Updating Texinfo Docs for a brief description of these CSS definitions or refer to the documentation for the ’infodoc-styles’ package available as a separate download.

      This simple format produces a much smaller footprint, and therefore the data can easily be pasted into another document. The drawback of the simplified format is that minor artifacts are produced between characters and between lines. Also, only a small subset of the color attributes available in the NcDialog API can be replicated by this type of capture.

    The capture (a.k.a. screenshot) is first saved to temporary memory in a ’SaveWin’ class object, and is then written to a file in the specified format. The capture must proceed in a very specific sequence because an NcDialog window may contain many user interface control objects, each of which has its own memory space. Depending on the state of each control, it may be obscuring part of the parent window or other controls, so these objects are integrated into the parent dialog’s memory space in layers.

    Capture to memory is performed by the public ’CaptureDialog’ method, and output to the target file is performed by the private ’ccapFile’ method.

    There are several parameters for the ’CaptureDialog’ call which may be used to customize the output. All but the ’fPath’ parameter (target filename) are optional.

    • By default, the ’fhtml’ parameter is reset (false) and the capture is saved as plain text as described above.
    • If the ’fhtml’ parameter is set (true), then the capture is saved as an HTML document.
    • The CSS file specified by the ’stylePath’ parameter determines whether the HTML output will be formatted for the Complex HTML or Simple HTML styles.

      By default, ’screenshot-styles.css’ is written into the HTML document as the source of CSS style. This produces Complex HTML output with full-color support.

      If however, ’stylePath’ includes the filename ’infodoc-styles.css’, then Simple HTML output is produced, as described above. Note that for Simple HTML captures, you must also specify ’simpleColor’ the color attribute used for the interior of the dialog window. The ’timeStamp’ and ’lineComments’ parameters are reset (false), and the ’cellsPerLine’ parameter is ignored. Below is an example of invoking a Simple HTML capture. Please see the Dialog1 test application (Test08) or the Dialog2 test application (Test07) to view the capture in action. The actual capture is done in the respective callback methods to avoid interfering with the user interface loop.

      short status = CaptureDialog ( "simplecap.htm", true, false, 
                                     "../screenshots/infodoc-styles.css",
                                     1, false, nc.grR ) ;
      


  • void DisplayRadiobuttonTypes ( winPos wPos,
                                   attr_t baseColor, attr_t selColor ) ;
      Input  :
         wPos     : screen coordinates for display
         baseColor: display color for all but 'select' character
         selColor : display color for 'select' character
    
      Returns: nothing
    

    Open a sub-dialog to display the standard formats available for radio buttons. When you define a DialogRadiobutton-class user interface control object, the Radiobutton type is specified by the ’rbSubtype’ member of the ’InitCtrl’ class.
    Please see InitCtrl class for details.

    See the Dialog1 test application (Test05) for an example of this method.

    ┌───────┤ Display Radio Button Types ├───────┐ │ │ rbtS1 - Standard, 1 columns wide │ │ <> rbtS3a - Standard, 3 columns wide │ │ [] rbtS3s - Standard, 3 columns wide │ │ () rbtS3p - Standard, 3 columns wide │ │ < > rbtS5a - Standard, 5 columns wide │ │ [ ] rbtS5s - Standard, 5 columns wide │ │ ( ) rbtS5p - Standard, 5 columns wide │ rbtC1 - Custom (example), 1 columns │ rbtC2 - Custom (example), 2 columns │ │ ⎼⎼ rbtC3 - Custom (example), 3 columns │ │ [] rbtC4 - Custom (example), 4 columns │ │ ⎻⎼⎼⎻ rbtC5 - Custom (example), 5 columns │ rbtC6 - Custom (example), 6 columns │ │ │ Press Any Key └────────────────────────────────────────────┘


    Notes On Custom Radiobutton Types:

    • rbtC1 is any one-column character.
    • rbtC2 is any two-column character
      (NOT two one-column characters).
    • rbtC3 is any three one-column characters, with the center character being the ’select’ character.
    • rbtC4 is any three characters, provided that
      the characters follow the column pattern: 1 - 2 - 1
      with the center character being the ’select’ character.
    • rbtC5 is one of:
      any five one-column characters with pattern: 1 - 1 - 1 - 1 - 1
      with the center character being the ’select’ character,
      OR
      any three characters provided that
      the characters follow the column pattern: 2 - 1 - 2
      with the center character being the ’select’ character.
    • rbtC6 is any three two-column characters, with the center character being the ’select’ character.
    • For more information on multi-column symbols, please refer to the Unicode character table, codepoints:
      "U+3000 to U+30FF: CJK Symbols and Punctuation".


  • void DisplayAltCharacterSet ( winPos wPos, attr_t baseColor ) ;
      Input  :
         wPos     : screen coordinates for display
         baseColor: display color dialog background
    
      Returns: nothing
    

    Open a sub-dialog to display the characters of the Alternate Character Set (ACS). These are the ’narrow’ characters embedded within the terminal’s character set. The ACS is a holdover from the old UNIX days when only one (hopelessly unsuitable) character set was available.

    The ACS is accessed by setting the ncaATTR bit of the color attribute used to display the character. The ACS is used for drawing lines in the window or displaying certain special characters. The individual characters may be accessed through the acsXXX group of constants in NCurses.hpp.

    Example of drawing a line-intersect character using the ACS:
       char intersection = acsINSECT ;
       dp->WriteChar ( 2, 2, &intersection, nc.bw | ncaATTR ) ;
    

    See the Dialog2 test application (Test10) for an example of this method.

       ╔═══════════════════════════════════════════════════════════╗
       ║ IMPORTANT NOTE:                                           ║
       ║  The ACS is documented for hysterical reasons only,       ║
       ║  and should never be used within an NcDialog application. ║
       ║  Use 'wide' line drawing, and special characters instead. ║
       ║  (see 'DisplayLineDrawingSet' method below).              ║
       ╚═══════════════════════════════════════════════════════════╝
    


  • void DisplayWideCharacterSet ( winPos wPos, attr_t baseColor ) ;
      Input  :
         wPos     : screen coordinates for display
         baseColor: display color dialog background
    
      Returns: nothing
    

    Open a sub-dialog to display the ’wide’ equivalents to the Alternate Character Set (ACS).

    These characters are used for drawing lines in the window or for displaying certain special characters. The individual characters may be accessed through the wcsXXXX group of constants in NCurses.hpp.

    NOTE: The ncaATTR color-attribute bit IS NOT USED to display these characters.

    Example of drawing a line-intersect character:
       wchar_t intersection = wcsINSECT ;
       dp->WriteChar ( 2, 2, intersection, nc.bw ) ;
    

    See the Dialog2 test application (Test10) for an example of this method.



  • void DisplayLineDrawingSet ( winPos wPos, attr_t baseColor ) ;
      Input  :
         wPos     : screen coordinates for display
         baseColor: display color dialog background
    
      Returns: nothing
    

    Open a sub-dialog to display the line-drawing characters defined within the wcsXXXX group of constants in NCurses.hpp.

    These characters are used for drawing lines in the dialog window. For additional information, please see LineDef class.

    See the Dialog2 test application (Test08) for an example of this method.



  • void DisplayLineDrawingExamples ( winPos wPos, attr_t baseColor ) ;
      Input  :
         wPos     : screen coordinates for display
         baseColor: display color dialog background
    
      Returns: nothing
    

    Display examples lines and shapes drawn using the line-drawing characters.

    For additional information, please see LineDef class.

    See the Dialog2 test application (Test08) for an example of this method.



  • void DebugMsg ( const char* msg, short pause = ZERO,
                    bool erase = false ) ;
      Input  :
         msg   : message to be displayed
                 if message == "%", clear the message area
         pause : (optional, ZERO by default, i.e. return immediately)
                 If specified, wait before return. Value is interpreted 
                 as the number of seconds to pause.
                 If value >= 30, then wait for keypress before returning
         erase : (optional, false by default): if true, erase the message 
                 after the pause interval
    
      Returns: nothing
    

    Display a message in the bottom line of the dialog window. Note that this method is not controlled by the ENABLE_DEVELOPMENT_METHODS flag, so it is always available.

    Example:
    
       uiInfo Info ;                    // user-supplied data
       dp->EditScrollbox ( Info ) ;     // get user input
    
       gString gs( "User selection index: %hd", &Info.selMember ) ;
       dp->DebugMsg ( gs.ustr(), 3, true ) ;
    


Taking Screenshots of NcDialog Windows

For screenshots that are to be integrated into a Texinfo document, please see Updating Texinfo Docs.

However, for a higher-quality capture, the CSS definition file ’screenshot-styles.css’ is provided. This file provides definitions for the full range of color attributes and color combinations supported by the NcDialog API. Thus, you can create a high-quality capture in a stand-alone HTML document which shows off your application to its best advantage.

Capture of an NcDialog window is performed through the ’CaptureDialog’ method as described above. ’screenshot-styles.css’ provides definitions for color and other character formatting required for display of full-color NcDialog window captures. The HTML colors displayed closely match the colors displayed in a terminal window configured to use the ’Linux Console’ color scheme.

Several examples of these screenshots are available on the author’s website (see Technical Support.)

For instance:
http://www.SoftwareSam.us/public_html/docs/ss/Dialog2_7_cap.html

The following is a screenshot of the ’Paste Special’ dialog in our FileMangler application. For the ’info’ formatted version of this document, this is a Plain Text capture, but for the HTML formatted version of the document this is a Simple HTML capture.

┌─────────────────┤ Paste Special ├─────────────────┐ │ Source file: findresults.txt │ File type: 'Regular' │ │ │ │ Specify type for new file: │ │ < ◆ > Copy the source file │ │ < > Create a symbolic link to source file │ │ < > Create a 'hard' link to source file │ │ Specify name for new file: │ │ < > Use the source file name │ │ < ◆ > Enter a new file name below │ │ ..... │ ├─────────────────────────────────────────────────────┤ │ Copying: findresults.txt │ │ To: findresults.bak │ │ OK CANCEL └─────────────────────────────────────────────────────┘



Secondary Classes

This chapter details definitions for several classes used to support data access and configuration.

Class NameDescription
 see AudibleAlert class make noise to alert user
 see cdConnect class visual connections to dialog border
 see dtbmData class post a temporary Textbox message
 see genDialog class create a utility dialog window
 see InitCtrl class define a control object
 see InitNcDialog class define a dialog window
 see keyPushback class key data pushback buffer
 see LineDef class character-based line drawing
 see MouseEvent class details on mouse-input events
 see NcColorMap class capture or initialize color map
 see NcColorPair class component of NcColorMap class
 see NcRgbSet class component of NcColorMap class
 see ssetData class initialize Scrollext-class data
 see uiInfo class contains user input data
 see winPos class specify y/x coordinates


InitNcDialog class

This class is used to pass initialization data to the NcDialog constructor.

class InitNcDialog
{
   public:                 // public data members

   // Dialog size in lines and columns
   short dLines;     // display lines
   short dColumns;   // display columns

   // Dialog position within the terminal window, interpreted as
   // an offset from the upper-left corner of terminal window
   short dYoffset;   // offset in Y (lines)
   short dXoffset;   // offset in X (columns)

   // Dialog title (optional, pass NULL pointer for no title)
   // If specified, title will be displayed as centered in the 
   // first line of the dialog (integrated with the dialog border
   const char* dTitle;

   // Dialog window's border style.
   // Member of enum ncLineType (see notes below)
   ncLineType borderStyle;

   // Color attribute for dialog window border
   attr_t borderColor;

   // Color attribute for dialog window interior
   attr_t interiorColor;

   // Pointer to an array of user interface control definitions.
   // (pass a NULL pointer if no controls defined)
   InitCtrl* ctrlPtr;
} ;

Notes

  • The size of the dialog must be <= the size of the terminal window.
  • The position of the dialog must provide for the entire dialog to be inside the terminal window.
  • If specified, the title will be centered in the top line of the dialog. (One or two leading and trailing spaces are recommended.)
    ╔════════════╣ Your Title Here ╠═════════════╗
  • You may also specify a dialog title after instantiation. This is useful when you want the title text to be a different color than the dialog border.
    ╔════════════╣ Colorful Title ╠═════════════╗
    Please see SetDialogTitle method.
  • The ’style’ of the dialog border is the line type. The recognized line types are:
    ncltSINGLE      ┌───────────┐
    ncltSINGLEBOLD  ┏━━━━━━━━━━━┓
    ncltDUAL        ╔═══════════╗
    ncltDASH2       ┌╌╌╌╌╌╌╌╌╌╌╌┐
    ncltDASH2BOLD   ┏╍╍╍╍╍╍╍╍╍╍╍┓
    ncltDASH3       ┌┄┄┄┄┄┄┄┄┄┄┄┐
    ncltDASH3BOLD   ┌┅┅┅┅┅┅┅┅┅┅┅┐
    ncltDASH4       ┌┈┈┈┈┈┈┈┈┈┈┈┐
    ncltDASH4BOLD   ┌┉┉┉┉┉┉┉┉┉┉┉┐
    
  • The color attribute for the dialog border may be any of the defined color-attribute variables.
    Please see NcDialog Color Variables.
  • The color attribute for the dialog interior area may be any of the defined color-attribute variables.
    Please see NcDialog Color Variables.

Example

The following is an example of initializing the InitNcDialog class and instantiating the dialog.

const short dialogROWS = 24,  // display lines
            dialogCOLS = 80,  // display columns
            ulY = 4,          // upper left corner in Y
            ulX = 1 ;         // upper left corner in X
attr_t dColor = nc.blR,       // dialog background color
       bColor = nc.brbl ;     // dialog border color

// array of dialog control initialization objects
InitCtrl ic[2] =
{
   {  // 'OK' pushbutton
      dctPUSHBUTTON,          // type:

      ..  ..  ..

      &ic[1]                  // nextCtrl:  link in next structure
   },
   {  // 'CANCEL' pushbutton
      dctPUSHBUTTON,          // type:

      ..  ..  ..

      NULL                    // nextCtrl:  link in next structure
   },
};

//* Initial parameters for dialog window *
InitNcDialog dInit( dialogROWS,     // lines
                    dialogCOLS,     // columns
                    ulY,            // Y offset 
                    ulX,            // X offset 
                    "  Press a Button, If You Dare!  ", // title
                    ncltDUAL,       // border line-style
                    bColor,         // border color attribute
                    dColor,         // interior color attribute
                    ic              // control definitions
                  ) ;

//* Instantiate the dialog window *
NcDialog* dp = new NcDialog ( dInit ) ;

//* Open the dialog window *
if ( (dp->OpenWindow()) == OK )
{

   ..  ..  ..

   delete ( dp ) ;   // close the dialog
}




InitCtrl class

Initialization data for a dialog control object.

A singly-linked list of InitCtrl objects is passed to the NcDialog constructor, one object for each control defined within the dialog. The array may contain from one(1) through MAX_DIALOG_CONTROLS control definitions. See also the ’ctrlPtr’ member of the InitNcDialog class, described above.

The definition and use of each member may be dependent upon the type of control being defined. See below for more detailed notes on the way each data member is used.

class InitCtrl
{
   public:     // public data members

   // Control type from enum DCType
   DCType type ;

   // For Radiobuttons controls only: button sub-type. See enum RBType.
   // For all other control types: unused
   RBType rbSubtype ;

   // For Radiobutton controls only: initial state of 'selected' flag.
   // For all other control types: unused
   bool rbSelect ;

   // Position of control expressed as an offset from the 
   // upper-left corner of the dialog window.
   short ulY ;       // offset in Y (lines)
   short ulX ;       // offset in X (columns)

   // Height of control expressed as the number of display lines.
   short lines ;

   // Width of control expressed as the number of display columns.
   short cols ;

   // Text data to be displayed within control.
   // If no text to be displayed, set to NULL pointer
   const char* dispText ;

   // Color attribute for the control while it 
   // DOES NOT have the input focus. (see notes)
   attr_t nColor ;

   // Color attribute for the control while it 
   // DOES have the input focus. (see notes)
   attr_t fColor ;

   // For dctTEXTBOX controls, the input filter. (see notes)
   // For all other control types: unused
   tbChars filter ;

   // Label text for the control. Set to NULL pointer for no label.
   // For dctPUSHBUTTON controls: unused.
   const char* label ;

   // For controls with a label specified (see 'label' member):
   // Y/X offset from the control's origin for display of the label text.
   short labY ;
   short labX ;

   // For Dropdown controls: direction of expansion.
   // For all other types: unused.
   ddBoxExpansionType exType ;

   // For scrolling controls (dctSCROLLBOX, dctSCROLLEXT, dctDROPDOWN, 
   // and dctMENUWIN), the number of items in the display list.
   // For all other types: unused.
   short scrItems ;
 
   // For scrolling controls (dctSCROLLBOX, dctSCROLLEXT, dctDROPDOWN, 
   // and dctMENUWIN), the INDEX of initially-highlighted line item.
   // For all other types: unused.
   short scrSel ;
 
   // For scrolling controls (dctSCROLLBOX, dctSCROLLEXT, dctDROPDOWN, 
   // and dctMENUWIN) controls and optionally for dctBILLBOARD controls,
   // specifies the color attribute for each line item. (see notes)
   // For all other types: unused.
   attr_t* scrColor ;

   // For dctSPINNER controls only: pointer to a dspinData class 
   // object for control initialization.
   // For all other types: unused.
   const dspinData* spinData ;

   // Indicates whether the control is initially 'active' i.e. is 
   // able to receive the input focus. (see notes)
   // Important Note: The first control in the list MUST 
   // have this member set ('true').
   // For dtcBILLBOARD controls: unused.
   bool active ;

   // Pointer to next instance in the singly-linked list of 
   // control definitions..
   // If last entry in the list, then set to NULL pointer.
   InitCtrl* nextCtrl ;
};

Notes

  • Important Note: The NcDialog instantiation process will modify the data in this object ONLY if an invalid value is found. This is an important point because the application will very likely need to reference these data from within the user input loop.

    As a correlary, DO NOT declare this class as ’const’ because the data may need to change dynamically.

  • Important Note: If the position of a control places any part of that control outside the dialog window, then the NcDialog constructor will attempt to move it inside the dialog. See the ’ulY’ and ’ulX’ members for more information.
  • Important Note: If the size and/or position of a control places any part of that control outside the dialog window, then the control MAY NOT be created.
  • Important Note: If the size and/or position of a control places any part of that control outside the terminal window, then the control WILL NOT be created.

  • ’type’ member
    The type of user-interface control being defined is indicated as a member of ’enum DCType’. The value of this data member determines how the remaining members will be interpreted by the constructor.

  • ’rbSubtype’ member
    For dctRADIOBUTTON controls only. Specifies the size and appearance of the Radiobutton as a member of ’enum RBType’. For all other control types, this member is ignored.

    Please see DisplayRadiobuttonTypes method for a chart of the Radiobutton formats available.


  • ’rbSelect’ member
    For dctRADIOBUTTON controls only. Specifies whether the initial state of the control will be ’true’ (set) or ’false’ (reset). For all other control types, this member is ignored.

    For independent Radiobutton controls, either state is allowed.

    For Radiobutton controls which are part of a Radiobutton group, only one member of the XOR group may be ’true’, so be sure to define this member as ’true’ for only one control in the group.
    Please see GroupRadiobuttons method for details.


  • ’ulY’ and ’ulX’ members
    Indicates the position of the control within the dialog window. The dimension is the offset from the upper-left corner of the dialog window to the upper-left corner of the control object.
    • The position specified must place the control entirely within the dialog window.

      Exception:
      Menuwin controls may be positioned to descend beyond the lower boundary of the parent dialog when expanded. Sub-menus may not be attached at a position beyond the dialog window.

      Note that it is the application’s responsibility to restore any display data outside the parent dialog that may be overwritten by the Menuwin expansion.

      Please be aware that writing outside the dialog window carries some risk. The size of the terminal window physically limits how far below the dialog the Menuwin control can expand, and if there is insufficient space, the menu will not expand. The user will see this as an application error, so defend yourself: if the Menuwin control extends beyond the dialog window, then test the height of the terminal window before calling the Edit method to expand the control.

    • For Pushbuttons, Radiobuttons, Textboxes, Billboards, Dropdowns and Spinners: For stylistic reasons, it is strongly recommended that these controls be positioned entirely within the dialog’s borders.

      Scrollbox and Scrollext controls are designed to extend into the dialog’s border area if needed.
      Please see ConnectControl2Border method for additional information.

    • User interface control objects must never overlap each other. Although this rule is not enforced in the code, overlapping controls would be visual chaos for the end user.

      Note however, that the expanding controls, Menuwin and Dropdown controls, may obscure other controls when in the expanded state because the underlying data are saved before the control is expanded, and are restored after the control returns to the collapsed state.

    • NOTE: If an invalid positioning parameter is passed to the constructor, then the constructor will attempt to automatically reposition the control to achieve a valid position. Thus, if the control appears in a position other than the one you specified, it’s likely that you specified an invalid Y or X offset.

  • ’lines’ member
    The ’lines’ member applies to control types whose vertical size may be specified. Note that the minimum and maximum value for this member varies according to control type.

 Control Type Minimum Maximum
 dctPUSHBUTTON 1height of dialog
 dctTEXTBOX 1calculated according to capacity of buffer
 dctBILLBOARD 1height of dialog
 dctSCROLLBOX 3height of dialog
 dctSCROLLEXT 3height of dialog
 dctDROPDOWN 3height of dialog (when expanded)
 dctMENUWIN (ignored) 
 dctRADIOBUTTON forced to 1 
 dctSPINNER forced to 1 
  • NOTE: The height of an ’expanded’ Menuwin control is fixed as the number of line items (’scrItems’ member) + 2 (top and bottom borders).
  • NOTE: The height of a Textbox control may be automatically adjusted by the constructor to ensure that:
    (lines * cols) <= gsMAXCHARS
  • NOTE: The height of an ’expanded’ Dropdown control may be automatically adjusted by the constructor to ensure that the control stays entirely within the dialog window.

  • ’cols’ member
    Specifies the width of the control in display columns.
    The minimum value varies according to control type.
    • For borderless controls, the minimum width is one(1).
    • For controls with borders, the minimum width is three(3).
    • For dctSPINNER controls, the minimum value is determined by the maximum size of the data to be displayed, plus one. For example, the following Spinner would be defined with cols=9;
      ▒▒▒▒▒2045.198±▒▒▒▒▒
    • For dctRADIOBUTTON controls, this member is unused.
      (’rbSubtype’ member determines control width)

 Control Type Minimum Maximum
 dctPUSHBUTTON 1width of dialog
 dctTEXTBOX 1width of dialog
 dctBILLBOARD 1width of dialog
 dctSCROLLBOX 3width of dialog
 dctSCROLLEXT 3width of dialog
 dctDROPDOWN 3width of dialog
 dctMENUWIN 3 (expanded) 
 dctRADIOBUTTON (ignored) 
 dctSPINNER max data  width + 1 

  • ’dispText’ member
    This member takes a pointer to the text data that will be displayed within the control and is defined as: const char*.

    Note that this is a multi-purpose pointer which accomodates several different text-formatting options for different control types:

    dctPUSHBUTTON
    dctTEXTBOX
    dctBILLBOARD   (optional)
    dctRADIOBUTTON (custom designs ONLY) 
    Points to a UTF-8 character string, or set to NULL pointer.
        dispText = "Eat at Elliot's Donut Hole, Chicago" ;
        dispText = "[是]" ; // Radiobutton example
        dispText = " ^Exit " ; // Pushbutton example
        dispText = NULL ;  // no data specified
    
    dctSCROLLBOX
    dctDROPDOWN
    dctMENUWIN
    Points to a two-dimensional UTF-8 character array, OR 
    to a single array of UTF-8 characters.
    
    For the two-dimensional array, this will require 
    that you cast your pointer. (see example)
    Note that the two-dimensional array with line-item data
    that has been pre-sized is much faster to parse, and is 
    recommended for that reason.
    

    IMPORTANT NOTE ON WIDTH OF DISPLAY ITEMS:
    It can NEVER be assumed that one character is one column wide.
    In the real world, people often speak a language other than English, so prepare to become multilingual.

    To size your line items, you can use experimentation, OR you can use the gString
    internationalization tool’s ’gscols’ method: (see Statistical Info)

    If you are new to internationalization of user interfaces, please see the chapter on preparing for
    multiple languages: (see Multi-language Support)


    const char srcA = // one-dimensional array (in logical chunks)
    {
      " Item One  "
      " Item Two  "
      " Item Three"
    };
    dispText = srcA;
    
    const char srcB[][12] =    // two-dimensional array
    {
      " Item One  ",
      " Item Two  ",
      " Item Three"
    };
    dispText = (const char*)&srcBS;
    
    
    dctSCROLLEXT   (optional)
    If specified, points to an array of const char pointers, 
    one pointer for each data item. This will require 
    that you cast your pointer. (see example)
    
    const char* srcC[] = 
    {
      " Item One  ",
      " Item Two  ",
      " Item Three"
    };
    dispText = (const char*)&srcC ;
    

    For dctSCROLLEXT controls, this data member is optional.
    This means that the control’s display data may either be set during instantiation OR may be set after instantiation. If set during instantiation, it requires that ALL of the following members be initialized:
    dispText, scrColor, scrItems and scrSel.
    Please see SetScrollextText method for more information.

    This member may also optionally be used to set the initial display text for each display line of a dctBILLBOARD control.
    Please see Billboard Controls for more information.

    For standard dctRADIOBUTTON controls and dctSPINNER controls, this member is ignored.


    For certain control types, the display text may optionally contain a ’hotkey’ character, specified by placing the ’^’ (caret) character before the hotkey character:

    • For dctPUSHBUTTON controls ONLY, the hotkey if specified references the control itself.
    • For dctMENUWIN controls ONLY, the hotkey(s) if specified reference the individual line items displayed in the control.
    Examples:
       const char src[][13] = 
       {
         " Item ^One  ",
         " Item ^Two  ",
         " Item T^hree"
       };
    
       const char* src = " ^DONE " ;
    

    For other control types, the hotkey is placed in the ’label’ text.
    Please see Select Control via Hotkey for more information on how to specify and use hotkeys.


  • ’nColor’ member
    ’Non-focus’ color.

    For controls with borders, this is the color attribute of the border while control DOES NOT have input focus:
    dctSCROLLBOX
    dctSCROLLEXT
    dctDROPDOWN

    For borderless controls, this is the color attribute of the text while control DOES NOT have input focus:
    dctPUSHBUTTON
    dctTEXTBOX
    dctBILLBOARD
    dctRADIOBUTTON
    dctSPINNER

    For dctMENUWIN controls, the ’nColor’ and ’fColor’ members have a special interpretation.

    • Text is displayed in ’fColor’ when Menuwin is in the collaped state (only the menu title is visible), and control DOES NOT have input focus.
    • Text is displayed in ’nColor’ when Menuwin is in the collaped state (only the menu title is visible), and control DOES have input focus.
    • Menu border is displayed in ’nColor’ when Menuwin is in the expanded state, and control DOES NOT have input focus.
    • Menu border is displayed in ’fColor’ when Menuwin is in the expanded state, and control DOES have input focus.

    This is confusing, but was a necessary design compromise. Please see the Dialog1 test application, Test10 for a demonstration of how focus and non-focus colors are implemented for Menuwin controls.


  • ’fColor’ member
    ’Focus’ color.

    For controls with borders, this is the color attribute of the border while control DOES have input focus:
    dctSCROLLBOX
    dctSCROLLEXT
    dctDROPDOWN
    dctMENUWIN (when in expanded state - see above)

    For borderless controls, this is the color attribute of the text while control DOES have input focus:
    dctPUSHBUTTON
    dctTEXTBOX
    dctBILLBOARD
    dctRADIOBUTTON
    dctSPINNER

    For the special case of dctMENUWIN controls, please see note for the ’nColor’ member, above.


  • ’filter’ member
    For dctTEXTBOX controls only. This is a member of 'enum tbChars'. Specifies the text input filter to be applied to user input. The filter is also applied to any initial contents of the control specified during instantiation, AND to re-initialization performed through see SetTextboxText method.

    For all other control types, this member is ignored.


  • ’label’ member
    All controls (except Pushbuttons) may have an optional label which is specified during instantiation. If specified, the label text will be drawn directly into the dialog’s interior using the dialog’s specified interior color attribute. The position of the label text is specified as an offset from the control’s position within the dialog (see ’labY’ and ’labX’ below).
    • Both single-line and multi-line labels are supported. For a multi-line label, simply insert newline ('\n') characters as line breaks.
      Examples:
      
          label = "Append to existing data" ;
          label = "To retain existing data\n"
                  "select the 'apppend' button." ;
      

      Note that for labels which are positioned as ’Title’ text, only a single line of text is displayed, so any line breaks will be discarded.

    • A ’hotkey’ (shortcut key) may be specified as a character in the label text.
      Example:
          label = "Specify a ^Horrible Choice";
      

      Please see Select Control via Hotkey for more information on how to specify and use hotkeys.

    • For dctMENUWIN controls, the contents of the ’label’ member is the text displayed while the control is in the ’collapsed’ state, i.e. the menu’s title. Note that the number of display columns in the specified text determines the width of the Menuwin control when in the ’collapsed’ state, so at least one leading, and one trailing SPACE character is recommended.
    • For dctSCROLLBOX and dctSCROLLEXT controls only, the label text may be positioned as a ’Title’, drawn centered in the top border of the control itself, (see ’labY’ and ’labX’ below).
    • For dctPUSHBUTTON controls, this member is ignored.
      (Pushbuttons have no label.)

  • ’labY’ and ’labX’ members
    Specifies the position of the control’s label text relative to the upper-left corner of the control.
    POSITION: ulY+labY and ulX+labX

    • The position specified must place the entire label within the dialog window, and for stylistic reasons, it is strongly recommended that the label be positioned entirely inside the dialog’s border.
    • The position specified must place the entire label outside the area occupied by the control itself. If not, then all or part of the label will be obscured by the control.
    • For dctSCROLLBOX and dctSCROLLEXT controls only, the label text may be embedded into the top border of the control to form a ’Title’. This is done by setting both ’labY’ and ’labX’ to ZERO:
          labY = labX = ZERO ;
      The label will be centered in the top border of the control; thus, choose the width of the label text to fit within the width of the control.
    • For dctDROPDOWN controls, good programming practice indicates that the label should be positioned where it will not be obscured when the control is expanded.
    • For dctPUSHBUTTON and dctMENUWIN controls, these members are ignored.
    Examples:
       // one line above the control and starting in the same column
       labY = -1 ;
       labX = ZERO ;
    
       // one line below the control and offset two columns to the right
       labY = 1 ;
       labX = 2 ;
    
       // on the same line as the control and to the right of it
       labY = ZERO ;
       labX = 24 ;
    
       // on the same line as the control and to the left of it
       labY = ZERO ;
       labX = -24 ;
    
       // label to be drawn as a control 'Title'
       // dctSCROLLBOX and dctSCROLLEXT controls only
       labY = labX = ZERO ;
    

  • ’exType’ member
    For dctDROPDOWN controls only.
    Member of 'enum ddBoxExpansionType'. Specifies the direction in which the Dropdown control will expand when it receives the input focus.

    Notes on Size and Position of Dropdown Controls

    • Dropdown controls must remain within the parent dialog’s borders at all times, for both the ’expanded’ and ’collapsed’ states.
    • To ensure this, a complex calculation is performed during instantiation to be sure the position of the collapsed control is within the dialog’s borders, and the Y/X base position is adjusted as necessary.

      The ’ulY’ and ’ulX’ members determine the control’s position.

    • In addition, when the control expands in the specified direction, it must not extend into the dialog’s border, so the height (line count) for the expanded control is reduced as necessary.

      The ’exType’ member determines the direction(s) of expansion.

      The ’lines’ member determines the expanded height of the control, with a minimum value of three(3) and a maximum value determined by the available dialog space in the specified direction(s).

    • These adjustments will be carried out silently, so if your control does not appear in the dialog exactly as you specified it, then it is likely that the specified position and/or size of the control would have violated the integrity of the dialog’s border.

    For all other control types, this member is ignored.


  • ’scrItems’ member
    For controls that contain scrolling data, this is the total number of line items.
    dctSCROLLBOX
    dctSCROLLEXT   (optional - see ’dispText’ member)
    dctDROPDOWN
    dctMENUWIN
    

    NOTE: For Scrollbox, Scrollext and Dropdown controls, the height of the control (see ’lines’ member) determines the number of line items that are simultaneously visible.

    NOTE: For Menuwin controls, ALL line items are simultaneously visible when the control is in the ’expanded’ state; therefore, the height of a Menuwin control is fixed as: ’scrItems + 2’.

    For all other control types, this member is ignored.


  • ’scrSel’ member
    For controls that contain scrolling data, this is the index of the line item which will be initially highlighted.
    dctSCROLLBOX
    dctSCROLLEXT   (optional - see ’dispText’ member)
    dctDROPDOWN
    

    For dctMENUWIN controls, when the menu is expanded, the highlight is always placed on the first line item.

    For all other control types, this member is ignored.


  • ’scrColor’ member
    For controls that contain scrolling data, this member points to an array of color attributes, one attribute for each line item.
    dctSCROLLBOX
    dctSCROLLEXT   (optional - see ’dispText’ member)
    dctDROPDOWN
    dctMENUWIN
    

    This member may also optionally be used to set the initial color attributes for each display line of a dctBILLBOARD control IF ’dispText’ member is also specified.
    Please see Billboard Controls for more information.

    For all other control types, this member is ignored.

    • If all line items will have the same color attribute, then the array referenced by this member will have two elements:
          scrColor[0] = attrDFLT; and
          scrColor[1] == desired color attribute for all line items
      Note that this option does not apply to dctSCROLLEXT-type controls. If specified, Scrollext control data must be specified with one color attribute for each line item.
    • If each line item is to have its own color attribute, then the array referenced by this member will contain at least as many attributes as the number of line items.
          scrColor[0] == color attribute for line item 0,
          scrColor[1] == color attribute for line item 1,
          scrColor[2] == color attribute for line item 2,
      and so on. Failure to count the attributes carefully will result in an out-of-bounds system exception and the ridicule of your peers. For this reason, it is recommended that you always build your array with a few extra color attributes to avoid embarrassment.

  • ’spinData’ member
    For dctSPINNER controls ONLY, this is a pointer to the initialization data for the control.
    Please see dspinData class for more information.

    For all other control types, this member is ignored.


  • ’active’ member
    This member indicates whether the control can initially receive the input focus. To put it another way, if this member is set (’true’), then the user may interact with the control, and if this member is reset (’false’), then the control cannot be accessed by the user (i.e. it becomes read-only).

    Because the input focus must be somewhere, the first control in the array of controls passed to the constructor is forced to active = true;.

    Billboard controls are always read-only and can never receive the input focus, so for dctBILLBOARD controls this member is forced to active = false;.

    Please see ControlActive method for toggling the active/inactive state of the control under program control.


  • ’nextCtrl’ member
    This is a pointer to InitCtrl, indicating the next item in the list of control definitions.

    Because the NcDialog API fully supports the C++11 standard, the entire array of controls may be initialized at once, OR each control may be defined independently and then pointers to the individual definitions may be placed into an array of pointers.

    To signal the end of the list, BE SURE to set the ’nextCtrl’ member of the last item in the array to the NULL ptr:
    nextCtrl = NULL
    Failure to do so would cause runtime embarrassment.





uiInfo class

Data returned to the application’s input loop from the Edit method for each user interface control type.

Please see Dialog Control Objects for more information on the specific values returned from each control’s Edit method.

class uiInfo
{
 public:              // public data members

 short ctrlType ;     // type of control edited (enum DCType)
 short ctrlIndex ;    // index of edited control in control list
 bool  hasFocus ;     // true if control has input focus on return from edit
 bool  dataMod ;      // true if control's data modified during edit
 wchar_t keyIn ;      // last key input received (or ZERO if n/a)
 wkeyCode wk ;        // most recent actual keycode or mouse event
                      // (generally used only for debugging)
 short selMember ;    // for controls that have more than one member:
                      //  i.e. Scroll Box, Scrollext, Radio Button Group, 
                      //  Drop-Down, and Menu-win, this is the
                      //  currently-selected member of the group
 bool  isSel ;        // for controls that have binary data i.e. Pushbuttons
                      //  and independent Radio Buttons, current state:
                      //     Radio Button: true if 'selected'
                      //     Pushbutton  : true if 'pressed'

 bool  viaHotkey ;    // true if a valid hotkey was detected during edit, 
                      // causing the control being edited to lose focus

       // NOTE: The following fields contain valid data only if the viaHotkey 
       //       flag != false on return from edit routine
 short h_ctrlType ;   // type of control accessed via hotkey (enum dcTYPE)
 short h_ctrlIndex ;  // index of control that received focus via hotkey
 bool  h_hasFocus ;   // true if the control has input focus
 bool  h_dataMod ;    // true if control's data modified
 wchar_t h_keyIn ;    // last key input received (or ZERO if n/a)
 wkeyCode h_wk ;      // actual last key pressed (generally unused)
 short h_selMember ;  // for controls that have more than one member:
                      //  i.e. Scroll Box, Radio Button Group, and Drop-Down,
                      //  this is the currently-selected member of the group
 bool  h_isSel ;      // for controls that have binary data i.e. Pushbuttons
                      //  and independent Radio Buttons, current state:
                      //     Radio Button: true if 'selected'
                      //     Pushbutton  : true if 'pressed'
};

Notes

  • Each application will have a primary user interface loop.
    When a control receives the input focus, the user interface loop will call the appropriate Edit_Xnnn method for that control type with a pointer to a uiInfo-class object. Please see the example user interface loop, below for more information (see sample UI loop).
  • The user input method for each of the control types returns a summary of the user’s actions in the data members of the specified uiInfo-class object. Please see Application Layer Methods for more information.
  • There are two groups of data members:
    • Primary Group:
        ctrlType
        ctrlIndex
        hasFocus
        dataMod
        keyIn
        selMember
        isSel
        wk (for debugging)
        viaHotkey
    • Hotkey Group:
        h_ctrlType
        h_ctrlIndex
        h_hasFocus
        h_dataMod
        h_keyIn
        h_selMember
        h_isSel
        h_wk (for debugging)
    • With the exception of the ’viaHotkey’ member, all of these data members should be considered as read-only.
    • The only time to write to the ’viaHotkey’ member is when you are about to call either the EditDropdown method or the EditMenuwin method, AND you want the target control to expand immediately, rather than waiting for the user to expand the control. In this case, set:  'info.viaHotkey = true;'
      For all other Edit methods, this flag is ignored on entry; however, set  'info.viaHotkey = false;' before the call, if you want a warm-and-fuzzy feeling.

  • The Primary Group
    The members of the Primary group return the results of the call to one of the Edit_Xnnn methods.
    • On return from the Edit method, the first member to check is the ’dataMod’ flag.

      If 'dataMod == false', then the data of the control have not changed, so there is nothing for your user interface loop to do.

      If 'dataMod != false', then the data of the control have changed, and you may want to take some action based on those changes.

    • For binary controls (Pushbuttons and independent Radiobuttons), the ’isSel’ member indicates the true/false state of the control.
    • For multi-item controls (Scrollbox, Scrollext, Dropdown, Menuwin and Radiobutton groups), the ’selMember’ member indicates which data item in that control has been selected.
    • For Textbox and Spinner controls, if 'dataMod != false', then call the GetTextboxText or GetSpinnerValue methods, respectively to retrieve the user’s changes.
      See GetTextboxText method.
      See GetSpinnerValue method.
    • The other members of the Primary group contain utility information for special circumstances. Please study the user interface loops of the various test applications to see how these data members can be interpreted.
      Please see Application Examples for more information.

  • The Hotkey Group
    The members of the Hotkey group contain any data generated as a result of the user pressing one of your defined hotkey combinations to shift the input focus away from the control currently under edit to a different control.
    • Members of the Hotkey group should never be referenced directly. Instead, if you are finished with the data in the Primary group, you can use the HotData2Primary method (see below) to move the Hotkey group into the Primary group.
    • Data returned to the application’s user interface loop in the Hotkey group may be safely discarded without processing EXCEPT when the target control is one of the binary controls, either a Pushbutton control or a Radiobutton control. For other types of controls, the Hotkey data are meaningless because the Edit method for the target control must still be invoked to gather user input.

      For the binary controls, moving the input focus to the control by pressing its hotkey combination is equivalent to navigating to that control and then pressing the ENTER key. For more information, please see the Edit methods for the binary controls:
      See EditPushbutton method.
      See EditRadiobutton method.


Public Methods

One public method, ’HotData2Primary’ is defined for the uiInfo class.

On return from an Edit method for any of the user-interface control types, the user may have shifted the input focus to another control via a defined ’hotkey’.

When this happens, the results of the edit for the control which previously had the focus are stored in the primary data members, and the results of the completed edit (if any) for the control which now has the focus are stored in the hotkey member variables designated by names of the format 'h_xxx'. This applies ONLY to binary-state controls, which at this time include Pushbuttons and Radiobuttons.

When a hotkey has been used to shift the input focus to a Pushbutton or Radiobutton control, then that control is likely to have changed its state. Thus, there will be no need to call the Edit method for the control because the user’s wishes are already contained in the 'h_xxx' members, so simply copy the data from the hotkey members to the primary members using the ’HotData2Primary’ method.

You will know whether the edit was terminated by a hotkey by testing the 'viaHotkey' member, and you will know whether the control’s state has changed by testing the new value of the 'dataMod' member. Please study the example user-interface loop below.

//* Move the hotkey data and invalidate the h_xx fields *
void HotData2Primary ( void )
{
   ctrlType  = h_ctrlType ;
   ctrlIndex = h_ctrlIndex ;
   hasFocus  = h_hasFocus ;
   dataMod   = h_dataMod ;
   keyIn     = h_keyIn ;
   wk        = h_wk ;
   selMember = h_selMember ;
   isSel     = h_isSel ;
   viaHotkey = false ;
}

User Interface Loop Example

Example:

   uiInfo Info;           // user interface data returned here
   short  icIndex = ZERO; // index of control with input focus
   bool   done = false;   // loop control
   while ( ! done )
   {
      // If user selects a Pushbutton control using a hotkey,
      // then this implies that the button has been pressed.
      if ( ic[icIndex].type == dctPUSHBUTTON )
      {
         // If the control obtained input focus through TAB, STAB, etc.
         // then call the edit method for this type of control.
         if ( !Info.viaHotkey )
            icIndex = dp->EditPushbutton ( Info ) ;

         // However, if we arrived at this control through a 
         // 'hotkey', then move the results of the completed 
         // edit into the main data members
         else
            Info.HotData2Primary () ;

         // If user modified the control's state
         if ( Info.dataMod != false )
         {
            ..  ..  ..
         }
      }

      // If user toggles a Radiobutton control using a hotkey,
      // then this implies that the button state has been modified.
      else if ( ic[icIndex].type == dctRADIOBUTTON )
      {
         // If the control obtained input focus through TAB, STAB, etc.
         // then call the edit method for this type of control.
         if ( !Info.viaHotkey )
            icIndex = dp->EditRadiobutton ( Info ) ;

         // However, if we arrived at this control through a 
         // 'hotkey', then move the results of the completed 
         // edit into the main data members
         else
            Info.HotData2Primary () ;

         // If user modified the control's state
         if ( Info.dataMod != false )
         {
            ..  ..  ..
         }
      }

      // EXCEPT for binary-state controls (Pushbuttons and Radiobuttons),
      // data in the hotkey members is meaningless and can be ignored.
      else if ( ic[icIndex].type == dctTEXTBOX )
      {
         icIndex = dp->EditTextbox ( Info ) ;
         // If user modified the control's state
         if ( Info.dataMod != false )
         {
            ..  ..  ..
         }
      }

      // For expanding controls, (Dropdown and Menuwin):
      // If you want to force the control to expand immediately
      // on entry to the Edit method, set the 'viaHotkey' flag.
      // If we arrived at this point via hotkey, then the 
      // 'viaHotkey' flag is _already_ set.
      else if ( ic[icIndex].type == dctDROPDOWN )
      {
         Info.viaHotkey = true ; // force expansion
         icIndex = dp->EditDropdown ( Info ) ;
         if ( Info.dataMod != false )
         {
            ..  ..  ..
         }
      }
      else if ( ic[icIndex].type == dctMENUWIN )
      {
         Info.viaHotkey = true ; // force expansion
         icIndex = dp->EditMenuwin ( Info ) ;
         if ( Info.dataMod != false )
         {
            ..  ..  ..
         }
      }
      ..  ..  ..

      // If the loop will continue AND if the input focus
      // has not already been changed via hotkey, then 
      // move the input focus to the next/previous control.
      if ( done == false && !Info.viaHotkey )
      {
         if ( Info.keyIn == nckSTAB )
            icIndex = dp->PrevControl () ; 
         else if ( Info.keyIn != ZERO )
            icIndex = dp->NextControl () ;
      }
   }  // while()




winPos Class

class winPos
{
   public:
   winPos ( void ) ;             // Default constructor

   winPos ( const short& yval,   // Initialization constructor
            const short& xval ) ;

   short ypos ;                  // Line number (zero-based)
   short xpos ;                  // Column number (zero-based)
} ;

This class implements an Y/X coordinate pair for use in positioning objects (or the cursor) on the screen.

Note that in mathematics and in most real-world environments, the order of operators is X-then-Y; however, in ’ncurses’ world the order is Y-then-X, that is Row-then-Column. This is logical, but may take a bit of getting used to. Initialization values are interpreted as offsets from the terminal window origin (upper-left corner of the window) defined as offset zero/zero.

The most common use of winPos is for positioning text output.
Here’s a typical example of following one text string with another. Note that the value returned by the ’WriteString’ method is the current cursor position in the form of a winPos class object.

Examples:

// instantiate the object and initialize it to row 2, column 4
winPos wp( 2, 4 ) ;

// write the question, then follow with the answer
wp = dp->WriteString ( wp, " What's the sum of 4 + 6 + 8 ? : ", nc.bl );
wp = dp->WriteString ( wp, "The sum is 18 !", nc.re, true );

// The resulting output would be:

What's the sum of 4 + 6 + 8 ? : The sum is 18 !


Implementation Note: The winPos class supports the initialization of object arrays for certain class objects under the C++11 standard.





wkeyCode Class

Please see wkeyCode class in the chapter on Keycode Constants.





keyPushback Class

This class is used internally by the NcDialog API to implement the key input look-ahead buffer.

  • The ncurses C-library function, ’unget_wch’ is very limited: it has a depth of one(1), it can only store the keycode (not the key type), and frankly, it’s too buggy to be useful.

    For these reasons, we have made the design decision to implement a more rubust push-back queue within the NCurses class.

  • Note that no more than KEYPUSH_MAX keycodes may be pushed back into the queue without an intervening call to the ’GetKeyInput’ method.
  • Note that only valid keycodes and mouse events can be pushed back into the queue. Escape sequences MAY NOT be pushed back into the queue.

If the application uses ’KeyPeek’ (see KeyPeek method) to look ahead in the key-input queue, OR if the application pushes a keyboard/mouse event back into the queue using ’UngetKeyInput’ (see UngetKeyInput method), then the data are stored in this class.

The keyPushback FIFO queue is private data, so the application never accesses the queue directly. Data are retrieved from the queue using ’GetKeyInput’ (see GetKeyInput method).





MouseEvent Class

Although it is generally not necessary for application code to look inside the MouseEvent class, it is useful to know what is happening behind-the-scenes.

An instance of the MouseEvent class lives within the ’wkeyCode’ class, and if an un-converted mouse event occurs, it will contain the following information in its public data members; however, if ’wkeyCode’ contains a converted mouse event or other valid key data, then the mouse-event data are undefined. See GetKeyInput method.

   // this is the event-type bitmask used by the ncurses library
   mmask_t  eventType ;

   // id of mouse or mouse-like device (for multiple devices)
   short    deviceID ;

   // Screen-relative position group.
   // These coordinates are relative to the upper left corner of the 
   // ACTIVE area of the terminal window, and are used internally 
   // by the NCurses-class, NOT by NcDialog API applications.
   short    sypos ;
   short    sxpos ;
   short    szpos ;  // for touchpads only (not supported)

   // Dialog window-relative position group
   // (used only by NcDialog class, not referenced by NCurses class)
   // These coordinates are relative to the upper left corner of the 
   // dialog window to which the mouse event belongs.
   short    ypos ;
   short    xpos ;
   short    zpos ;   // for touchpads only (not supported)

   short    cIndex ; // NcDialog-class control index (if any)
   short    meType ; // NcDialog-class event-type
                     //   (member of enum meTypes)
   bool     cKey ;   // 'control' key down during event
   bool     sKey ;   // 'shift'   key down during event
   bool     aKey ;   // 'alt'     key down during event
   bool     conv ;   // 'true' if event data have been 
                     //   converted to keycode data

Please see Mouse Configuration for additional information.





termColorInfo Class

Please see termColorInfo class in the chapter discussing the NCurses Engine startup sequence.





NcColorPair Class

Component class of the NcColorMap class (see below).

class NcColorPair
{
   public:
   NcBaseColors fgnd ;  // indicates foreground color
   NcBaseColors bkgnd ; // indicates background color
};


NcRgbSet Class

Component class of the NcColorMap class (see below).

class NcRgbSet
{
   public:
   short r ;   // Red color register
   short g ;   // Green color register
   short b ;   // Blue color register
};


NcColorMap Class

The NcColorMap class is used to capture the current display color settings, or to modify the existing color map.

class NcColorMap
{
   public:
   // The constructor takes one, optional parameter:
   // the default background color.
   NcColorMap ( NcBaseColors bkgnd = ncbcDEFAULT );
   
   // Public data members
   //--------------------
   //* Array of foreground/background color-pairs
   NcColorPair pairMap[ncPAIR_MAX];

   //* Array of (virtualized) R-G-B register values
   NcRgbSet rgbMap[ncCOLOR_MAX];

   // Default backgroud for all foreground colors
   NcBaseColors bkgndColor;

   // Number of color pairs supported by terminal
   short pairCount;

   // Number of RGB registers supported by terminal
   short rgbCount;

   // Number of colors supported by terminal
   // (This is for information only, and is )
   // (not used in any mapping calculations.)
   short supColors;

   // 'true' if terminal supports color-pair modification
   bool  canModifyPairs;

   // 'true' if terminal supports RGB register modification
   bool  canModifyRGB;
};

For more information on color mapping, please see Color Attributes.

For more information on adjusting the color map, please refer to:
see NCurses Startup-Shutdown: ’StartNCursesEngine’ and ’StartColorEngine’ methods, and
see NCurses Configuration: ’GetColorMap’ and ’SetColorMap’ methods.





LineDef class

class LineDef
{
   public:
   // The initialization constructor takes the following parameters:
   LineDef( type,    // indicates line direction: horizontal (ncltHORIZ)
                     // or vertical (ncltVERT) 
            style,   // indicates the line style as a member of
                     // enum ncLineType (see below)
            startY,  // 
            startX,  // 
            length,  // length of line in rows (vertical lines)
                     // or columns (horizontal lines)
            color    // color attribute
          ) :
   // The constructor sets all the auto-intersection flags to 'true':
   // cLeft   // connect at left end   (horizontal lines)
   // cRight  // connect at right end  (horizontal lines)
   // cTop    // connect at top end    (vertical lines)
   // cBot    // connect at bottom end (vertical lines)
   // cInsect // connect when lines cross

   // Public data members:
   // --------------------
   ncLineType  type ;    // line type (ncltHORIZ or ncltVERT)
   ncLineType  style ;   // line style: member of enum ncLineType
   short       startY ;  // start position Y
   short       startX ;  // start position X
   short       length ;  // line length in rows or columns
   attr_t      color ;   // color attribute

   // if 'true' create an intersection with an existing 
   // line, (except at at new line's endpoints)
   bool cInsect ; 

   // if 'true' connect on left with an existing line
   bool cLeft ;

   // if 'true' connect on right with an existing line
   bool cRight ;

   // if 'true' connect on top with an existing line
   bool cTop ;

   // if 'true' connect on bottom with an existing line
   bool cBot ;
} ;

//* Line-drawing codes for drawing lines and window borders *
enum ncLineType : short 
{ 
   ncltHORIZ,        // horizontal line
   ncltVERT,         // vertical line
   ncltSINGLE,       // single line
   ncltSINGLEBOLD,   // single, bold line
   ncltDUAL,         // dual line
   ncltDASH2,        // dashed line style 2
   ncltDASH2BOLD,    // dashed line style 2, bold
   ncltDASH3,        // dashed line style 3
   ncltDASH3BOLD,    // dashed line style 3, bold
   ncltDASH4,        // dashed line style 4
   ncltDASH4BOLD     // dashed line style 4, bold
} ;

Line definition class used as a parameter for the ’DrawLine’ method.
See DrawLine method.

Lines are drawn left-to-right and top-to-bottom.
By default, as new lines are drawn, they will form a visual connection with existing lines. To prevent this automatic connection, set the appropriate flag(s) to ’false’.

Please see Line Drawing Characters for a table of line-drawing characters.

Please see DrawBox method to draw a rectangle in one operation.

Line-drawing examples

┌────────────────────────────┤ Line-drawing Examples ├────────────────────────────┐ │ │ Line Styles Connect With Verticals │ ┌─────────────── ncltSINGLE │ │ ┃ ║ ╎ ╏ ┆ ┇ ┊ ┋ │ │ │ │┏━━━━━━━━━━━━━━ ncltSINGLEBOLD ├──┼──╂──╫──┼──╂──┼──╂──┼──╂──┤ ncltSINGLE │ │ │┃╔═════════════ ncltDUAL ┝━━┿━━╋━━╫━━┿━━╋━━┿━━╋━━┿━━╋━━┥ ncltSINGLEBOLD │ │ │┃║┌╌╌╌╌╌╌╌╌╌╌╌╌ ncltDASH2 ╞══╪══╪══╬══╪══╪══╪══╪══╪══╪══╡ ncltDUAL │ │ │┃║╎┏╍╍╍╍╍╍╍╍╍╍╍ ncltDASH2BOLD ├╌╌┼╌╌╂╌╌╫╌╌┼╌╌╂╌╌┼╌╌╂╌╌┼╌╌╂╌╌┤ ncltDASH2 │ │ │┃║╎╏┌┄┄┄┄┄┄┄┄┄┄ ncltDASH3 ┝╍╍┿╍╍╋╍╍╫╍╍┿╍╍╋╍╍┿╍╍╋╍╍┿╍╍╋╍╍┥ ncltDASH2BOLD │ │ │┃║╎╏┆┌┅┅┅┅┅┅┅┅┅ ncltDASH3BOLD ├┄┄┼┄┄╂┄┄╫┄┄┼┄┄╂┄┄┼┄┄╂┄┄┼┄┄╂┄┄┤ ncltDASH3 │ │ │┃║╎╏┆┇┌┈┈┈┈┈┈┈┈ ncltDASH4 ┝┅┅┿┅┅╋┅┅╫┅┅┿┅┅╋┅┅┿┅┅╋┅┅┿┅┅╋┅┅┥ ncltDASH3BOLD │ │ │┃║╎╏┆┇┊┌┉┉┉┉┉┉┉ ncltDASH4BOLD ├┈┈┼┈┈╂┈┈╫┈┈┼┈┈╂┈┈┼┈┈╂┈┈┼┈┈╂┈┈┤ ncltDASH4 │ │ │┃║╎╏┆┇┊┋ ┝┉┉┿┉┉╋┉┉╫┉┉┿┉┉╋┉┉┿┉┉╋┉┉┿┉┉╋┉┉┥ ncltDASH4BOLD │ │ │┃║╎╏┆┇┊┋ │ │ ┃ ║ ╎ ╏ ┆ ┇ ┊ ┋ │ │ │ │┃║╎╏┆┇┊┋ │ │ │┃║╎╏┆┇┊┋ Connect With Horizontals │ │┃║╎╏┆┇┊┋ ───┬──┰──╥──┬──┰──┬──┰──┬──┰─── │ │ │┃║╎╏┆┇┊┋ ───┼──╂──╫──┼──╂──┼──╂──┼──╂─── ncltSINGLE │ │ ━━━┿━━╋━━╫━━┿━━╋━━┿━━╋━━┿━━╋━━━ ncltSINGLEBOLD │ Boxes With Connections ═══╪══╪══╬══╪══╪══╪══╪══╪══╪═══ ncltDUAL │ │ ┌────┬──────┐┏━━━━┳━━━━━━┓ ╌╌╌┼╌╌╂╌╌╫╌╌┼╌╌╂╌╌┼╌╌╂╌╌┼╌╌╂╌╌╌ ncltDASH2 │ │ │ │ │┃ ┃ ┃ ╍╍╍┿╍╍╋╍╍╫╍╍┿╍╍╋╍╍┿╍╍╋╍╍┿╍╍╋╍╍╍ ncltDASH2BOLD │ │ ├────┼──────┤┣━━━━╋━━━━━━┫ ┄┄┄┼┄┄╂┄┄╫┄┄┼┄┄╂┄┄┼┄┄╂┄┄┼┄┄╂┄┄┄ ncltDASH3 │ │ │ │ │┃ ┃ ┃ ┅┅┅┿┅┅╋┅┅╫┅┅┿┅┅╋┅┅┿┅┅╋┅┅┿┅┅╋┅┅┅ ncltDASH3BOLD │ │ └────┴──────┘┗━━━━┻━━━━━━┛ ┈┈┈┼┈┈╂┈┈╫┈┈┼┈┈╂┈┈┼┈┈╂┈┈┼┈┈╂┈┈┈ ncltDASH4 │ │ ╔════╦══════╗┌╌╌╌╌┬╌╌╌╌╌╌┐ ┉┉┉┿┉┉╋┉┉╫┉┉┿┉┉╋┉┉┿┉┉╋┉┉┿┉┉╋┉┉┉ ncltDASH4BOLD │ │ ║ ║ ║╎ ╎ ╎ ───┴──┸──╨──┴──┸──┴──┸──┴──┸─── │ │ ╠════╬══════╣├╌╌╌╌┼╌╌╌╌╌╌┤ │ │ ║ ║ ║╎ ╎ ╎ │ │ ╚════╩══════╝└╌╌╌╌┴╌╌╌╌╌╌┘ Press Any Key └───────────────────────────────────────────────────────────────────────────────────┘




dspinData Class

Please see dspinData class in the chapter on Spinner-class user interface controls..





cdConnect class

class cdConnect
{
   public:
   cdConnect()    // constructor
   {
      ul2Left     = false ;
      ul2Top      = false ;
      ll2Left     = false ;
      ll2Bot      = false ;
      ur2Right    = false ;
      ur2Top      = false ;
      lr2Right    = false ;
      lr2Bot      = false ;
      connection  = false ;
   }
   // connect upper-left of control to left edge of parent dialog
   bool  ul2Left ;
   // connect upper-left of control to top edge of parent dialog
   bool  ul2Top ;
   // connect lower-left of control to left edge of parent dialog
   bool  ll2Left ;
   // connect lower-left of control to bottom edge of parent dialog
   bool  ll2Bot ;
   // connect upper-right of control to right edge of parent dialog
   bool  ur2Right ;
   // connect upper-right of control to top edge of parent dialog
   bool  ur2Top ;
   // connect lower-right of control to right edge of parent dialog
   bool  lr2Right ;
   // connect lower-right of control to bottom edge of parent dialog
   bool  lr2Bot ;
   // if 'true', then apply specified connections, else ignore connections
   bool  connection ;
} ;

This class is used for passing connection-point data to the ’ConnectControl2Border’ method. The method copies the specified ’cdConnect’ class data into the target control’s definition and then redraws the control.





genDialog class

An initialized genDialog-class object is used to pass parameters to the ’InfoDialog’ and ’DecisionDialog’ methods to produce a simple message dialog.

The ’InfoDialog’ method displays the data contained in the genDialog-class object, and then waits for the user to close the dialog.
Please see InfoDialog method for more information.

The ’DecisionDialog’ method displays the data contained in the genDialog-class object (formatted as a yes-or-no question), and then waits for the user to respond.
Please see DecisionDialog method for more information.


Dialog Dimensions and Positioning

The minimum dimensions for the ’InfoDialog’ and ’DecisionDialog’ are defined by two constants.

const short MIN_genDialog_LINES = 4 ;
const short MIN_genDialog_COLS  = 15 ;

The maximum dimensions are the dimensions of the parent dialog window. See the 'dLines' and 'dCols' members.

By default, the dialog will be centered in the parent dialog window, but this can be overridden by passing positioning offsets to the constructor. See the 'yoffset' and 'xoffset' members.


Constructors

Two constructors are provided: a full-initialization constructor used by the application to create the dialog, and a default constructor which simply inserts a place-holder dialog (see example below).

Note that there are no public data members in the genDialog class. All initialization is based upon the parameters passed to to constructor. While this is somewhat unusual for a class definition, the text and color-attribute data are formatted according to internal criteria for display within the defined dialog dimensions. This makes for a simple, reasonably-bulletproof, general-purpose dialog.

(If you are curious about the details of the data formatting, the constructor is implemented in the ’NcDialog.cpp’ source module.)

// Full-initialization constructor.
// Parameters are fully range-checked and are adjusted if necessary.
genDialog
(
  // list of constant string to be displayed,
  // one string per dialog display line
  const char** msgList,

  // dialog background color
  attr_t dColor,

  // number of dialog lines
  // (including top and bottom borders)
  short dLines,

  // number of dialog columns
  // (including left and right borders)
  short dCols,

  // Optional:
  // Y and X offsets from upper-left of parent dialog
  // (default: centered in parent dialog)
  short yoffset=(-1),
  short xoffset=(-1),

  // Optional: 
  // array of color attributes, one attribute for each line of 
  // data to be displayed.
  // (default: all data written using 'dColor')
  const attr_t* msgAttr=NULL,

  // Optional:
  // If 'true', interpret strings as RTL language
  // If 'false' (default), interpret string as LTR language
  bool rtl=false,

  // Optional:
  // Color of Pusbutton control when it DOES NOT have focus.
  // (default: nc.gyR)
  attr_t pnColor=attrDFLT,

  // Optional:
  // Color of Pusbutton control when it DOES have focus.
  // (default: nc.reG)
  attr_t pfColr=attrDFLT,

  // Optional:
  // Specify the text (and width) of the 'YES' and 'OK' Pushbuttons.
  // [not yet implemented]
  const char* yes_text,

  // Optional:
  // Specify the text (and width) of the 'NO Pushbutton.
  // [not yet implemented]
  const char* no_text,
);

The default constructor (no parameters specified) simply creates one message: "unitialized", which will be displayed in the title line of the dialog as a reminder that your dialog definition is incomplete.

┌┤unitialized├┐ │ │ OK └─────────────┘

Notes

  • ’msgList’ member
    An array of pointers to UTF-8 text strings. Each string will be displayed on a separate display line.
    • The first item in the array is interpreted as the dialog title. If the first item is a string consisting of a single space: " ", it indicates that the dialog should have no title.
    • Except for the first (title) item, a string consisting of a single space, " " denotes an empty line.
    • The last item in the pointer array should be the NULL pointer.
    • The maximum number of items should be no more than:
          dLines - 3
      to avoid interfering with the Pushbutton control(s) located on
          dLines - 2.
    • Each message begins offset at two(2) from the edge.

  • ’dColor’ member
    This is the background color (and border color) for the dialog window. If no color-attribute list is specified by ’msgAttr’, then this is also the color attribute for all displayed data.

  • ’dLines’ member
    Specifies the number of lines in the dialog window, including the top and bottom border lines.
    Minimum: MIN_genDialog_LINES
    Maximum: number of lines in the parent dialog.

    Note that the dialog must be positioned entirely within the parent dialog. If it is not, then the dialog will be automagically resized.


  • ’dCols’ member
    Specifies the number of columns in the dialog window, including the left and right border lines.
    Minimum: MIN_genDialog_COLS
    Maximum: number of columns in the parent dialog.

    Note that the dialog must be positioned entirely within the parent dialog. If it is not, then the dialog will be automagically resized.


  • ’yoffset’ member (optional)
  • ’xoffset’ member (optional)
    Specifies the position of the dialog as the Y/X offset from the upper-left corner of the parent dialog to the upper-left corner of this dialog.

    The default values are -1/-1 indicating that the dialog is to be centered within the parent dialog. Note that the dialog must be positioned entirely within the parent dialog. If it is not, then the position will be automagically shifted.


  • ’msgAttr’ member (optional)
    Specifies an array of color attributes, one for each line of displayed text, with the first attribute being assigned as the color of the dialog title.

    NOTE: Failure to carefully count the attributes in the array will result in an out-of-bounds system exception. For this reason, it is recommended that you always build your array with a few extra color attributes to avoid embarrassment.

    By default, this is a NULL pointer, indicating that all text should be displayed using the color attribute specified by the ’dColor’ member.


  • ’rtl’ member (optional)
    Specifies whether the data should be interpreted as an LTR (left-to-right) language or as an RTL (right-to-left) language.
    ’false’ indicates LTR language (default)
    ’true’ indicates RTL language

    Note that the names of the Pushbutton(s) are fixed, and are therefore always drawn as LTR text. (but see ’yes_text’ and ’no_text’ members)
    InfoDialog    : "OK"
    DecisionDialog: "YES" and "NO".


  • ’pnColor’ member (optional)
  • ’pfColor’ member (optional)
    Specifies an alternate color scheme for the Pushbutton control(s).

    Each Pushbutton control has two associated color attributes:

    1. Color attribute when button has input focus.
      The default is 'nc.reG' (bold white text on red background.)
    2. Color attribute when button does not have input focus.
      The default is 'nc.gyR' (white text on grey background.)

    If the background color specified by the ’dColor’ member is aesthetically incompatible with the default Pushbutton colors, then you can specify a different set of colors to match the dialog background.

    For instance, your application dialog may have an overall blue color scheme. In that case the the default Pushbutton colors would have good contrast and would be easily visible. On the other hand if your application’s color scheme were red, then the red Pushbuttons would be invisible against the background. In that case, a different Pushbutton color such as brown or black would provide better contrast.


  • ’yes_text’ member (optional)
    [This parameter not implemented at this time.]

  • ’no_text’ member (optional)
    [This parameter not implemented at this time.]


Examples

The following is an excerpt from Test05 of the Dialog2 test application.

short dlines = 10,   // vertical size of dialog
      dcols  = 46,   // horizontal size of dialog
      yoff   = 10,   // Y offset from upper-left of parent
      xoff   = 14 ;  // X offset from upper-left of parent

//* Create the array of display strings.*
gString gs( "              %s              ", lfName ) ;
const char* mList[] = 
{ "  Save Captured Key Data To Log File  ",  // (title)
  " ",
  "Captured key data will be appended to the",
  "following file in the current directory: ",
  gs.ustr(),
  "If file does not exist, it will be created.",
  "                 Continue?",
  " ",
  NULL
} ;

//* Color attributes, one for each line *
const attr_t mAttr[] = 
{
   nc.brcy,    // (title) 
   dColor, dColor, dColor, nc.bwB, 
   dColor, dColor, dColor
} ;

//* Create an initialized instance of a genDialog class object.*
genDialog gd( mList, dColor, dlines, dcols, yoff, xoff, mAttr );

//* Open the dialog and get user's response.*
bool result = dp->DecisionDialog ( gd ) ;

┌──┤ Save Captured Key Data To Log File ├──┐ │ │ │ Captured key data will be appended to the │ │ following file in the current directory: │ KeyCapture.txt │ If file does not exist, it will be created.│ │ Continue? │ │ │ YES NO └────────────────────────────────────────────┘


The following example displays our somewhat cynical poem in both English (LTR language) and Yiddish (RTL language), as well as a Chinese version to demonstrate multi-column character data.
(Working code for this example can be found in the Dialogw test application, Test07: ’genDialog Demonstration’.)

┌────┤ סנאמאר ├────┐ טיור ןענעז ןזיור יולב ןענעז ץאלייוו סיז זיא רעקוצ טעפ ריא טכאמ ןוא │ │ OK └────────────────────┘

┌───┤ Romance ├────┐ Roses are red, Violets are blue Sugar is sweet And makes you fat. │ │ OK └────────────────────┘

┌──┤ 浪漫的爱情 ├──┐ 玫瑰是昂贵 紫罗兰是不是在赛季 糖是育肥 此外,你永远不会 找到一个丈夫 OK └────────────────────┘




dtbmData class

The dtbmData class is used for passing text and color parameters to the ’DisplayTextboxMessage’ method (see DisplayTextboxMessage method)).

The ’enum dtbmColors’ constants indicate default text color attributes for the data to be displayed (see the ’colorData’ member variable).

enum dtbmColors : attr_t 
{
  // use non-focus color defined for the Textbox
  dtbmNFcolor = (attrDFLT - 1),
  // use focus color defined for the Textbox
  dtbmFcolor  = (attrDFLT - 2)
} ;

class dtbmData
{
   public:     //* public data members

   // wchar_t (wide) text data
   wchar_t textData[gsMAXCHARS];

   // color attribute data
   attr_t colorData[gsMAXCHARS];

   // if 'true' (default) refresh display after update
   bool   refreshControl;

   // if 'true' adjust message to be centered in textbox
   bool   centered;

   // if 'true' process data as RTL (right-to-left)
   bool   rtlText;
};

dtbmData Constructors

For convenience, several different constructors are available for initializing the dtbmData class.

  • dtbmData ( void );
    This constructor initializes all data members to default values:
     - text data is initialized to a null string
     - text color is control’s non-focus color (dtbmNFcolor)
     - ’refreshControl’ == ’true’
     - ’centered’ == ’false’
     - ’rtlText’ == ’false’
    
  • dtbmData ( const char* tData );
  • dtbmData ( const wchar_t* tData );
    This constructor initializes the text data. tData may be either:
    pointer to a UTF-8 encoded string
    (max length gsMAXBYTES), OR
    pointer to a wchar_t string
    (max length gsMAXCHARS)
    All other fields are set to their default values (see above).
  • dtbmData ( const char* tData, const attr_t* cData,
               bool refresh = true, short center = ZERO,
               bool rtl = false );
  • dtbmData ( const wchar_t* tData, const attr_t* cData,
               bool refresh = true, short center = ZERO,
               bool rtl = false );
    This constructor initializes both text data AND color attribute data. Initialization of text data is as above, and initialization of color-attribute data is an array of color-attribute values:
        an array of color attribute values, one for each character
        cData[0] == dtbmNFcolor, (use non-focus color)
        cData[0] == dtbmFcolor,  (use focus color)
    All other fields are set to their default values (see above).

    CAUTION: If cData[0] != dtbmNFcolor && cData[0] != dtbmFcolor, then your color data buffer must have at least as many elements as the number of characters (not byte count) in tData. ALSO, if the ’center’ parameter is specified, the length of the tData string is dynamically adjusted to fill the target field, so the number of color attributes in the ’cData’ array must be at least as great as the total number of displayed characters. Otherwise, a system memory access violation and application crash will be the likely results.
    To be safe, if you are not using one of the defined default color attributes, always use a color buffer >= number of columns defined for the target control, or MAX_DIALOG_WIDTH.

dtbmData Public Methods

To assist in data initialization, the following public methods are defined for the dtbmData class.

  • ’operator=’ method
    Assign a new text array to be displayed (UTF-8 or wchar_t). All other parameters remain unchanged.
  • ’GetTextPtr’ method
    Returns a const pointer to wchar_t for the current text contents. (not useful in application space, but used internally by the NcDialog API)
  • ’GetColorPtr’ method
    Returns a const pointer to attr_t for the current color-attribute contents. (not useful in application space, but used internally by the NcDialog API)
Examples:

   NcDialog* dp = new NcDialog( init );
   short cIndex = 2 ;   // index into array of controls
   .  .  .
   dtbmData dispdata( L"Processing - Please Wait..." );
   dp->DisplayTextboxMessage ( cIndex, dispdata ) ;
   .  .  .
   dispdata = "Processing Complete!" ;
   dp->DisplayTextboxMessage ( cIndex, dispdata ) ;
   .  .  .

For the curious, the dtmbData class implementation is located in the ’NcdControlTB.cpp’ source code module.





AudibleAlert class

The AudibleAlert class is used to pass setup parameters to the UserAlert method to produce an audible user-alert sequence.

Please see UserAlert method for more information.

class AudibleAlert
{
   public:

   // Default constructor - parameters set for a single ping
   AudibleAlert ( void )
   {
      this->pCount = 1 ;
      this->pInterval = minUAINTERVAL ;
      this->pRepeat = ZERO ;
      this->pPattern = NULL ;
   }

   // Initialization constructor - specify ping sequence.
   // Note that all parameters except 'pCount' are optional.
   AudibleAlert ( short c, short i = minUAINTERVAL, short r = ZERO, 
                  short d = ZERO, const short* p = NULL )
   {
      this->pCount    = c ;
      this->pInterval = i ;
      this->pRepeat   = r ;
      this->pDelay    = d ;
      this->pPattern  = p ;
   }


   // -------------------
   // Public Data Members
   // -------------------
   // number of pings  (range: 1 - 32,768)
   short pCount ;

   // delay between pings (in twentieths of a second)
   // (range: minUAINTERVAL - 32,768)
   // Note that units of this parameter were selected as a 
   // compromise between what the simple hardware device can 
   // produce, the resolution of the internal timer, and 
   // what the human ear can distinguish.
   short pInterval ;

   // number of times to repeat pCount/pInterval sequence
   // (range: 1 - 32,768)
   // A value of -1 indicates that the sequence should be 
   // repeated until a key is pressed.
   short pRepeat ;

   // delay (in tenths of a second) between repeats of sequence
   // (range: 0 - 32,768)
   short pDelay ;

   // If specified, this array of interval values replaces 
   // the 'pInterval' data with a sequence of interval values 
   // (in tenths of a second).
   // The 'pCount' member indicates the length of the array.
   const short* pPattern ;
} ;

For an interactive example of audible-alert sequences, please refer to the test application Dialog4, Test02.

Here is the interesting part of the code and the result:

if ( Info.ctrlIndex == donePB )
   done = true ;
else if ( Info.ctrlIndex == testPB )
{
   //* Use contents of spinner controls to     *
   //* initialize AudibleAlert object for call.*
   int c, i, r, d ;
   dp->GetSpinnerValue ( countSP, c ) ;
   dp->GetSpinnerValue ( intervalSP, i ) ;
   dp->GetSpinnerValue ( repeatSP, r ) ;
   dp->GetSpinnerValue ( delaySP, d ) ;
   AudibleAlert aa( c, i, r, d ) ;
   dp->UserAlert ( &aa ) ;
}
else if ( Info.ctrlIndex == fancyPB )
{
   AudibleAlert ab ( 8, 2, 1, 2, fancyPattern ) ;
   dp->UserAlert ( &ab ) ;
}

╔════════════════════════╣ Audible Alerts ╠════════════════════════╗ Music, it's not, but it will get the user's attention. The actual tone produced for the ping is system dependent. Set call parameters as desired, then press 'Test' button. Ping Count Ping Interval Seq. Repeat Seq. Delay 5± 4± 0± 0± (0.05s) (0.10s) FANCY TEST TEST DONE ╚════════════════════════════════════════════════════════════════════╝




Widget Classes

See Progbar class, for a description of the Progress Bar widget.




XWindows Clipboard

This chapter is for applications which require ’Copy’, ’Cut’ and ’Paste’ operations, either within the application itself or for data sharing with other applications.




Clipboard Overview

’Copy’, ’Cut’ and ’Paste’ operations are common in interactive applications, and these operations use a globally-accessible, temporary buffer called the ’clipboard.’

To avoid forcing the use of third-party libraries onto the application software designer, the NcDialog API is built without direct access to the system clipboard; however, a separate library, the tbXClip library is provided to support data sharing among applications.

  • The XWindows (system) clipboard handles ordinary text, formatted text, images, and much more; however, applications based on the NcDialog API live within a terminal environment, and so they are primarily concerned with the plain-text functionality of the X-clipboard.

    Clipboard access applies only to Textbox-class user interface controls. All other controls contain only static (or preformatted) data.

  • Many simple applications will not benefit from clipboard access, and therefore this chapter will not apply.
  • Other applications will require only internal cut-and-paste operations among the Textbox controls. For these, the NcDialog internal clipboard is available (see Local NcDialog Clipboard). Internal cut-and-paste is handled within the NcDialog API and does not require access to the X-clipboard.
  • If your application collects significant text input from the user, it is likely that the user will want to copy some of that text from other applications, or may want to copy text from your application to other applications. In this case, your user interface will benefit from full clipboard support, so link in the tbXClip library (see Interface to X-clipboard).
  • For the curious, see Inside the X-clipboard provides a quick look at the internal structure of the X-server clipboard. Applications require no information at the X-server level, but we are aware that programmers are an inquisitive class of folks.



Local NcDialog Clipboard

The NcDialog API Local Clipboard is a static local buffer used for transfer of text data among the Textbox controls of the application, and provides the application with a means to transparently connect the application’s Textbox controls with the X-server (system) clipboard for Copy/Cut/Paste operations.

The Local Clipboard IS NOT a member of the NcDialog class. It is a single, shared resource used by all instances of the NcDialog class within the terminal window, but is not visible to other applications.

Please see clipboard flow diagram for a graphical representation of the Local Clipboard’s place in the copy/cut/paste sequence.

Internally, the Local Clipboard is implemented as a private class,
        class TextboxLocalClipboard
which is accessed through the following NcDialog public methods:

SetLocalClipboard :
   Copy the specified text data to the Local Clipboard.
   Any existing data in the Local Clipboard will be discarded.

GetLocalClipboard :
   Returns a copy of the Local Clipboard's current contents.
   This is a null-terminated string.
   Clipboard data is unchanged.

GetLocalClipboard_Bytes :
   Returns the number of bytes (including null terminator) in the Local 
   Clipboard's data.

GetLocalClipboard_Chars :
   Returns the number of characters (including null terminator) in the Local 
   Clipboard's data.

These methods are described in detail in the discussion of Textbox controls. See Textbox Controls.




Interface to X-clipboard

Xterm Mouse-based Copy and Paste

The XWindows system, and specifically terminal programs based on Xterm implement a very simple interface with the system clipboard. While this interface is somewhat inflexible and difficult to use, as a developer of terminal applications, you should be aware of how the mouse/clipboard interface works.

First, text in a terminal window or other XWindows-based application may be ’selected’ using the mouse.

  • Position the mouse cursor over the first character of the text you want to copy.
  • Press and hold the left mouse button (Button 1).
  • Drag the mouse pointer to the end of the desired block of text.
  • Release the left mouse button.
  • A copy of the highlighted text is now available in a special system text buffer (not on the clipboard).

Now that the source text has been selected, you have two options. The first is the quick paste which bypasses the X clipboard:

  • Paste the selected text to the target terminal window by pressing Mouse Button 2. For most mouse designs, Mouse Button 2 is the Scroll Wheel.

    Try it out:
    Using the mouse as described above, highlight the sentence you are now reading. Then position the mouse cursor over an empty terminal window and press Mouse Button 2. Your highlighted text should be pasted into the target window.

  • NOTE: Selection of text using the mouse and the Button-2 quick-paste are possible only when Xterm is in control of the mouse. When the NcDialog/ncurses(w) mouse interface has been enabled within a terminal window, text selection and quick-paste within that terminal window is disabled.
    Text selection in other terminal windows and other applications will still be under Xterm control.

Your second option is to copy the highlighted text to the X clipboard so it can be accessed by any application:

  • If the text you previously highlighted was in the info-format version of this document (i.e. in a terminal application), then use a special key combination:
            CTRL+SHIFT+C
    to copy the text to the clipboard.
  • If the text you previously highlighted was in the HTML-format version of this document, then use the standard key combination:
            CTRL+C
    to copy the text to the clipboard.
  • While we assume that you know how to use the copy-and-paste functionality CTRL+C and CTRL+V in GUI applications, pasting text from the clipboard into a terminal window (or a terminal-based application) requires a slightly different action. Use either of the key combinations:
            CTRL+SHIFT+V
            CTRL+SHIFT+INSERT
    to paste the contents of the clipboard into the terminal window.

    As a technical note, what happens is not an actual ’paste’ operation. Instead, the contents of the clipboard are converted to a stream of keycodes which are sequentially written into the window, so pasting text into a terminal window is exactly the same as typing each of the characters on the keyboard (only faster).

    Additional technical note: The CTRL+SHIFT+V and CTRL+SHIFT+INSERT commands do not filter ASCII control codes (such as newline '\n' characters) and other non-printing characters, which will be interpreted by your application as unexpected command codes (e.g. the ENTER key). (We did mention that the Xterm interface is a simple one, right?)

Our experience shows that novice users of GNU/Linux don’t understand this simple copy-and-paste sequence, and more importantly they don’t want to know about it; so let’s move on to a more direct and intuitive way for users to access the clipboard from within your NcDialog application.



Program Access to the X clipboard

To provide greatest freedom and flexibility in application design, we take a layered approach to clipboard access within an NcDialog-based application. The following diagram describes the flow of data to and from the XWindows clipboard, as well as the flow between the application and the NcDialog Local Clipboard.

For more information on using the NcDialog Local Clipboard as a stand-alone substitute for the X-clipboard, please see Local NcDialog Clipboard.

╔═══════════════╣ NcDialog API -- Clipboard Data Flow ╠═══════════════╗ ┌───────────────────────┐ ┌───────────────────┐ ├─────────────────────▶─┤ tbXClip Clipboard │ │ X-WINDOWS │ ║ Interface Library │ │ CLIPBOARD │ ║ ├─◀─────────────────────┤ └─────────────────────┘ └───────────────────┘ └─────────────────────┐ └──'tbXClip::WriteSelection'───┐ ┌──────────────────────────────┘ ┌──'tbXClip::ReadSelection'──┘ ╔══╧════════╧═══════════╦═════════════════════════════════════════╗ Application's Your Application Callback Method ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ ╚═════════════════════╣ ( within a call to: ) ▼ ▲ ('NcDialog::EditTextbox') │ │ ╚═════════════════════════════════════════╝ │ │ ┌───────────────────┐ │ │ │ │ │ │ NcDialog API ├─▶─────────┐ │ │├─◀───┐ │ │ │ │ │ ▲ │ │ │ └───────────────────┘ │ ▼ │ │ ┌─────────────────┐ │ └───'NcDialog::GetLocalClipboard'────────◀─┤ NcDialog API Local Clipboard └──────'NcDialog::SetLocalClipboard'────────▶─┤ └───────────────────┘ ╚═══════════════════════════════════════════════════════════════════════╝


Makefile modification to include necessary libraries

To access the XWindows clipboard, you must first modify your Makefile to include the necessary library modules.

  • ’X11’ link library
    ** (OBSOLETE) - UNDER CONSTRUCTION **
  • ’Xmu’ link library
    ** (OBSOLETE) - UNDER CONSTRUCTION **
  • ’pthread’
    In order to monitor the Selection Event Queue (clipboard communications channel) for requests from other applications, it is strongly recommended that you launch an execution thread which is dedicated to that purpose.

    Although the ’tbXClip’ library can be built without the queue monitor, and thus without the need for multi-threading, this is not recommended because delays (> 200ms) in responding to external requests may cause those requests to be lost.

  • ’tbXClip’ link library
    Provides access to the X-server commands necessary for communication with the X-clipboard.

Example Link Command:

XLINKLIBS = -lX11 -lXmu -lpthread
g++ -o Dialogx Dialogx.o NcDialog.a tbxclip.a -lncursesw $(XLINKLIBS)

Please see tbXClip Library for a description of the tbXClip clipboard interface library.



NcDialog application startup sequence

This is a simplified example of the NcDialog instantiation sequence. The steps related to clipboard access are explained in greater detail in the sections that follow.

   //* Define the dialog and its contents *
   ..  ..  ..

   //* Module-scope pointer to your application class   *
   //* object. (used by the non-member callback method) *
   static dXClip* dxPtr = NULL ;

   //* Start the NCurses Engine *
   ..  ..  ..

   //* Instantiate your application class.      *
   //* Members of your application class should *
   //* include pointers similar to:             *
   //* NcDialog* dp;  and  tbXClip* tbxPtr;     *
   dXClip dxc( clArgs ) ;

   //* Execution is now INSIDE your application   *
   //* class. Initialize the module-scope pointer.*
   dxPtr = this ;
   ..  ..  ..

   //* Instantiate the NcDialog-class object. *
   this->dp = new NcDialog ( dsInit ) ;

   //* Establish a connection with the X Server.*
   this->tbxPtr = new tbXClip () ;

   //* Define a list of keycodes reserved for *
   //* access to clipboard functionality.     *
   this->dp->SetTextboxReservedKeys ( rk ) ;

   //* Establish a callback method to peek *
   //* at the user's keyboard/mouse input. *
   this->dp->EstablishCallback ( &UserInteractControlUpdate ) ;

   //* Enter the user interface loop.*
   this->UserInteract () ;
   ..  ..  ..


Connecting to the X Server

Connecting your application with the XWindows server and the X-clipboard is accomplished by instantiating the ’tbXClip’ class object.

this->tbxPtr = new tbXClip () ;

Quite a lot of foolishness goes on behind the scenes which the tbXClip code handles so the application doesn’t need to understand X. Here is a summary:

  • The tbXClip-class internal data are initialized.
  • The link with the X Server is established.
    This includes opening an invisible X window through which to communicate with the server.
  • The types of messages to be received and transmitted are established, and the names for those messages and for the clipboard buffers are negotiated.
  • A separate execution thread is optionally established to monitor the Selection Event Queue.


Specifying keycodes for user access

In order for the user to access the clipboard, it will be necessary to assign keycodes for the ’Select’, ’Copy’, ’Cut’ and ’Paste’ operations. This is done through a call to the ’SetTextboxReservedKeys’ method. Typically, these keycodes will be:

'SelectAll' : CTRL + A
'Copy'      : CTRL + C
'Cut'       : CTRL + X
'Paste'     : CTRL + V

Note that these are all ASCII control codes, defined in the NcDialog API as members of the ’wktFUNKEY’ (function-key) group. Of course any keycodes can be assigned to these operations, but in general, it is best to give the user what he or she expects.

   //* Clipboard access keycodes.  Note that by isolating   *
   //* the keycode definitions within an enum, the keycodes *
   //* can be painlessly modified later if desired.         *
   enum dsClipboardAccess : wchar_t 
   {
      caCOPY_SELECTED = nckC_C,     // Control+C
      caCUT_SELECTED  = nckC_X,     // Control+X
      caPASTE         = nckC_V,     // Control+V
      caSELECT_ALL    = nckC_A,     // Control+A
      caSELECT_LEFT   = nckSLEFT,   // Shift+LeftArrow
      caSELECT_RIGHT  = nckSRIGHT   // Shift+RightArrow
   } ;
   
   //* Initialize the 'reservedKeys' class.*
   reservedKeys rk ;
   rk.reserve = true ;
   rk.copyKey     = { caCOPY_SELECTED, wktFUNKEY } ;
   rk.cutKey      = { caCUT_SELECTED,  wktFUNKEY } ;
   rk.pasteKey    = { caPASTE,         wktFUNKEY } ;
   rk.selAllKey   = { caSELECT_ALL,    wktFUNKEY } ;
   rk.selLeftKey  = { caSELECT_LEFT,   wktFUNKEY } ;
   rk.selRightKey = { caSELECT_RIGHT,  wktFUNKEY } ;
   
   //* Establish the set of reserved keycodes.*
   this->dp->SetTextboxReservedKeys ( rk ) ;

Please see SetTextboxReservedKeys method for more information on assigning keycodes for clipboard access.



Building a ’callback’ method to monitor user input

Because the NcDialog API knows nothing about X or about the system clipboard, the application must provide the interface between the Textbox Local Clipboard and the X-clipboard. Therefore, after you have defined and instantiated your NcDialog-based application AND the clipboard interface, activate the callback method.

Because the compiler insists that this method be a non-member method (i.e. not a member of any class), you will need to provide it with access to the necessary data and methods using a limited-scope pointer (see ’dxPtr’ definition above).

this->dp->EstablishCallback ( &UserInteractControlUpdate ) ;

Please see EstablishCallback method for more information.

The established callback method is called from within the Edit methods for each control type. However, it should be obvious that the reserved keycodes must be processed by the callback method ONLY when called from within the ’EditTextbox’ method because only Textbox controls can send text data to the clipboard or receive text data from the clipboard.

Inside the EditTextbox method, the callback method is called every time the user presses a key (or uses the mouse, if activated).

At minimum, your callback method must monitor user key input for the ’Copy’, ’Cut’ and ’Paste’ keycodes defined for clipboard access. If one of these keycodes is detected, then move the text data between the Local Clipboard and the X-clipboard as indicated by the user input.

(Note that the keycodes defined for text-selection are handled internally by the EditTextbox method, so it is not necessary to scan for them here.)

The following is a simple callback example which monitors the keyboard input stream for the clipboard access keycodes.


static short UserInteractControlUpdate ( const short currIndex, 
                                         const wkeyCode wkey,
                                         bool firstTime )
{
   //* First-time processing *
   if ( firstTime != false )
   { /* nothing to do */ }

   //* If control currently under edit is a Textbox control, AND   *
   //* if your application is configured to access the X-clipboard,*
   //* then scan for clipboard keycodes (otherwise do nothing).    *
   if ( (sic[currIndex].type == dctTEXTBOX) && (dxPtr->useXclip != false) )
   {
      if ( (wkey.type == wktFUNKEY) &&     // key type is function key
           (wkey.key == caCOPY_SELECTED || // the reserved keycodes
            wkey.key == caCUT_SELECTED  ||
            wkey.key == caPASTE) )
      {
         //* 'Copy' or 'Cut' contents of the source Textbox.  *
         if ( wkey.key == caCOPY_SELECTED || wkey.key == caCUT_SELECTED )
         {
            //* The EditTextbox method will have copied/moved *
            //* the selected text from the Textbox control    *
            //* under edit to the Local Clipboard.            *
            //* Copy the contents of the Local Clipbord to    *
            //* the X-clipboard.                              *
            gString gs ;
            dxPtr->dps->GetLocalClipboard ( gs ) ;
            dxPtr->tbxPtr->WriteSelection ( xstCLIPBOARD, gs ) ;
         }

         // 'Paste' contents of clipboard to target Textbox.  *
         else if ( wkey.key == caPASTE )
         {
            //* Copy the contents of the X-clipboard to the   *
            //* Local Clipbord.                               *
            gString gs ;
            dxPtr->tbxPtr->ReadSelection ( xstCLIPBOARD, gs ) ;
            dxPtr->dps->SetLocalClipboard ( gs ) ;

            //* The EditTextbox method will now paste the  *
            //* contents of the Local Clipboard into the   *
            //* Textbox control under edit.                *
         }
      }
   }
   return OK ;
}

The code above is a somewhat simplified version of the callback method defined in the Dialogx test application. Because Dialogx is a test application, we give our callback full access to the application’s code and data. In a real-world application, you would likely restrict the non-member callback method to only the essential code and data needed to handle its task.


The next chapter, see tbXClip Library describes the public methods of the tbXClip class




tbXClip Library

** UNDER CONSTRUCTION **

tbXClip Overview

’tbXClip’ is an optional, stand-alone link library for use with the NcDialog API to provide access to the XWindows clipboard.

The ’tbXClip’ class and link library consist of one source module:
    'tbXClip.cpp' and one header file, 'tbXClip.hpp'.
These two files are compiled to create the ’tbXClip.a’ link library which is linked to the ’Dialogx’ application. ’tbXClip.hpp’ is ’included’ in the ’Dialogx.cpp’ source module to provide definitions, and only the mainline application code has any knowledge of the ’tbXClip’ library.

The NcDialog API know absolutely nothing about XWindows or the X clipboard or the tbXClip library. Your application knows the tbXClip class, and the tbXclip class knows the X clipboard. Thus, if your application has a need for clipboard access, link your application with the tbXClip library

For further information, note that the Dialogx test application as well as the tbXClip-class source code include extensive internal documentation about the joys and sorrows of low-level XWindows programming. There are also a number of third-party XWindows toolkits which contain a tremendous (if unorganized) amount of information. For toolkit-specific information, you can’t do better than the GNOME GTK+ toolkit’s documentation.


Public Methods of the tbXClip Class

What follows is a list of all public methods of the tbXClip class; however, under normal circumstances, your application will need only a small subset of these methods.

For instance, it is unlikey that you will need to transmit or receive raw binary data. Also, modern implementations no longer use the ’Cut Buffers’, which are byte-oriented buffers whose origins are now lost in history. However, for the adventurous, these methods can be used to configure special-purpose applications or to experiment with the X-clipboard’s capabilities.

Please see Dialogx App for detailed examples of using the tbXClip library.


  • virtual ~tbXClip ( void );
    Input  : none
    
    Returns: nothing
    

    Destructor for a tbXClip object. Release all resources held by the object.


  • tbXClip ( const char* host = NULL );
    Input  : 
         host  : (optional, NULL pointer by default)
                 Note that by default, the default communications
                 domain is used. This is taken from the 'DISPLAY'
                 environment variable.
                 To override this behavior, specify a host machine,
                 display server and display number:
                 "hostname:servernumber.screen_number"
    
    Returns: 
         implicitly returns a reference to the instantiated object
    

    Constructor for a tbXClip object.

      1) Initialize all data members.
      2) Establish a connection with the X Server.
      3) Open an invisible dummy ’window’ to send and receive notifications.
      4) Set the notification mask.
    

  • int ReadSelection ( xselTypes selType, gString& textData );
    Input  :
         selType  : source 'selection' (member of xselTypes)
         textData : (by reference) receives 'selection' data
    
    Returns:
         number of bytes read
         - data will be truncated if necessary to gsMAXBYTES, so if
           gsMAXBYTES is returned, then some data may have been lost
         - returns tbxERROR if:
           a) invalid selection type specified
           b) X-server memory allocation error
    

    Read data from specified X-clipboard ’selection’.

    Data are first converted from their native format to plain text, and are then copied into your gString object.


  • int WriteSelection ( xselTypes selType,
                         const gString& textData );
    Input  : 
         selType  : target 'selection' (member of xselTypes)
         textData : data to be written to target
    
    Returns:
         number of bytes written
         returns tbxERROR if:
           a) invalid selection type specified
           b) error in number of bytes written
           b) X-server memory allocation error
    

    Write data to X-clipboard ’selection’. Source data are null-terminated text.


  • const char* Get_tbXClip_Version ( void );
         Input  : none
    
         Returns: (const char*) pointer to version string
    

    Returns a const pointer to the tbXClip-class version number.



      ┌──────────────────────────────────────────────────────────────┐
      │ NOTE:                                                        │
      │   The following methods will seldom be needed at the         │
      │   application level. The NcDialog API user interface is      │
      │   designed for human readable data, not for binary data      │
      │   streams, and modern applications no longer use the cut     │
      │   buffers. These methods are, however, fully functional in   │
      │   in case you have a need for them.                          │
      └──────────────────────────────────────────────────────────────┘
    
  • int ReadSelection ( xselTypes selType, unsigned char* binData,
                        int maxBytes );
    Input  : 
         selType  : source 'selection' (member of xselTypes)
         binData  : buffer to receive data
                    It is the caller's responsibility to provide a
                    buffer large enough to hold the data.
         maxBytes : maximum number of bytes 'binData' can hold
    
    Returns: 
         number of binary bytes read
         - data will be truncated if necessary to the length specified
           by 'maxBytes'; therefore if maxBytes is returned, then
           some data may have been lost
         - returns tbxERROR if:
           a) invalid selection type specified
           b) X-server memory allocation error
    

    Read data from specified X-clipboard ’selection’. Data are returned as an array of 8-bit binary bytes.

    See ’GetSelectionSize’ method for pre-sizing the target array.


  • int ReadSelection ( xselTypes selType,
                        unsigned short* binData, int maxShorts );

    *** NOT YET IMPLEMENTED! ***

    Input  :
         selType  : source 'selection' (member of xselTypes)
         binData  : buffer to receive data
                    It is the caller's responsibility to provide a
                    buffer large enough to hold the data.
         maxShorts: maximum number of 16-bit unsigned short integers
                    'binData' can hold
    
    Returns: 
         number of binary words read
         - data will be truncated if necessary to the length specified
           by 'maxShorts'; therefore if maxShorts is returned, then
           some data may have been lost
         - returns tbxERROR if:
           a) invalid selection type specified
           b) X-server memory allocation error
    

    Read data from specified X-clipboard ’selection’. Data are returned as an array of 16-bit words.

    See ’GetSelectionSize’ method for pre-sizing the target array.


  • int ReadSelection ( xselTypes selType, unsigned int* binData,
                        int maxInts );

    *** NOT YET IMPLEMENTED! ***

    Input  :
         selType  : source 'selection' (member of xselTypes)
         binData  : buffer to receive data
                    It is the caller's responsibility to provide a
                    buffer large enough to hold the data.
         maxInts  : maximum number of 32-bit unsigned integers
                    'binData' can hold
    
    Returns:
         number of binary integers read
         - data will be truncated if necessary to the length specified
           by 'maxInts'; therefore if maxInts is returned, then
           some data may have been lost
         - returns tbxERROR if:
           a) invalid selection type specified
           b) X-server memory allocation error
    

    Read data from specified X-clipboard ’selection’. Data are returned as an array of 32-bit integers

    See ’GetSelectionSize’ method for pre-sizing the target array.


  • int ReadSelection ( xselTypes selType, const char* filespec,
                        bool append );

    *** NOT YET IMPLEMENTED! ***

    Input  :
         selType  : source 'selection' (member of xselTypes)
         binFile  : full path/filename specification to which data
                    is to be written. If file does not exist,
                    it will be created.
         append   : if 'true', then append new data to existing
                       target file (if any)
                    if 'false', then overwrite existing target 
                       file's contents (if any)
    
    Returns:
         number of bytes successfully written to target file
         returns tbxERROR if:
           a) invalid selection type specified
           b) error in number of bytes written
           c) X-server memory allocation error
           d) unable to create target file, or
              cannot obtain write access to target file
    

    Read binary stream data from specified X-clipboard ’selection’ AND write the binary data to specified file.

    If any header data is required in target file, it should be written first, and ’append’ should be set as ’true’.


  • int WriteSelection ( xselTypes selType,
                         const char* binData, int byteCount );
    Input  :
         selType  : target 'selection' (member of xselTypes)
         binData  : data to be written to target
         byteCount: number of bytes to be written
    
    Returns:
         number of bytes written
         returns tbxERROR if:
           a) invalid selection type specified
           b) invalid byte count specified
           c) error in number of bytes written
           d) X-server memory allocation error
    

    Write data to X-clipboard ’selection’. Data are handled as a binary byte stream.


  • int GetSelectionSize ( xselTypes selType,
                           binSourceBits bits = bsb8Bit );
    Input  : 
         selType  : target 'selection' (member of xselTypes)
         bits     : (optional, member of binSourceBits, bsb8Bit by default)
                    number of binary bits per item
                    NOTE: This parameter is currently ignored, and value
                          returned is the number of 8-bit bytes.
    
    Returns: 
         number of items in specified selection
         or tbxERROR if invalid selection type or bit count specified
    

    Returns the number of data items stored in specified X-clipboard ’selection’.

    Use this method to size the target buffer for receiving the contents of the specified X-clipboard ’Selection’ as an array of binary items.


  • int GetCutBufferSize ( int buffNum );
    Input  :
         buffNum  : index of source buffer (0 - 7)
    
    Returns:
         number of bytes in cut buffer
         or tbxERROR if invalid cut buffer specified
    

    Returns the number of data bytes stored in specified cut buffer.


  • int ReadCutBuffer ( int buffNum, gString& textData );
    Input  :
         buffNum  : index of source buffer (0 - 7)
         textData : receives text data from cut buffer.
    
    Returns:
         number of bytes read
         - data will be truncated if necessary to gsMAXBYTES, so if
           gsMAXBYTES is returned, then some data may have been lost
         - returns tbxERROR if:
           a) invalid cut buffer specified
           b) X-server memory allocation error
    

    Read null-terminated text data from specified cut buffer.


  • int ReadCutBuffer ( int buffNum, char* binData, int byteCount );
    Input  :
         buffNum  : index of source buffer (0 - 7)
         binData  : receives text data from cut buffer.
                    It is the caller's responsibility to provide a 
                    buffer large enough to hold the data.
         maxBytes : maximum number of data bytes to read
    
    Returns:
         number of bytes read
         returns tbxERROR if:
           a) target buffer too small (target buffer not modified)
           b) invalid cut buffer specified
           c) X-server memory allocation error
    

    Read binary-byte data from specified cut buffer.


  • int WriteCutBuffer ( int buffNum, const gString& textData );
    Input  :
         buffNum  : index of target buffer (0 - 7)
         textData : text data to be written
    
    Returns:
         number of bytes written
         returns tbxERROR if:
           a) invalid cut buffer specified
           b) error in number of bytes written
           c) X-server memory allocation error
    

    Write null-terminated text data to specified cut buffer.


  • int WriteCutBuffer ( int buffNum, const char* binData,
                         int byteCount );
    Input  :
         buffNum  : index of target buffer (0 - 7)
         binData  : data to be written
         byteCount: number of data bytes to write
    
    Returns:
         number of bytes written
         returns tbxERROR if:
           a) invalid cut buffer specified
           b) invalid byte count specified
           c) error in number of bytes written
           d) X-server memory allocation error
    

    Write binary-byte data to specified cut buffer.


  • int ClearCutBuffers ( void );
    Input  : none
    
    Returns:
         ZERO if successful, else tbxERROR
    

    Clear contents of all cut buffers.

    NOTE: This trashes all applications’ stored data, not just ours, so this method should be used only during testing.


  • Window GetSelectionOwner ( xselTypes selType );
    Input  :
         selType  : target 'selection' (member of xselTypes)
    
    Returns:
         ID of owner ('Window' type == unsigned long), or
         txbNO_OWNER if owner == 'None'
         returns tbxERROR if:
           a) invalid selection type
    

    Get ID of current ’owner’ for the specified Selection.
    An ’owner’ is the ID number of the XWindows client which currently owns the specified Selection.


  • Window SetSelectionOwner ( xselTypes selType, Window newOwner );
    Input  :
         selType  : target 'selection' (member of xselTypes)
         newOwner : any valid Window ID number
                    OR the special value: tbxNO_OWNER
    
    Returns:
         ID of new owner if successful
         returns tbxERROR if:
           a) invalid selection type
           b) invalid window ID
    

    Transfer ownership of the specified ’Selection’ to a new owner.

      1) Take ownership of another X-server client’s selection.
      2) Assign ownership of our selection to another X-server client.
      3) Set selection ownership to ’None’ i.e. tbxNO_OWNER.
         (Note that this makes the selection inaccessible.)
      4) If new owner == current owner, then do nothing.
    

    Although this is a powerful tool, it is important for all X clients to play nicely with one another, so use it with caution.

    Important Note: Specifying an invalid window ID will probably result in the X Server terminating your application. Beware!!




Inside the X-clipboard

  ┌──────────────────────────────────────────────────────────────┐
  │                                                              │
  │ NOTE: Modern GNU/Linux distributions no longer use XLIB to   │
  │       access the X Server. For this reason, the discussion   │
  │       of low-level access to the X-server clipboard is       │
  │       out-of-date. We will research the new XML-based access │
  │       method as soon as possible and report back to you.     │
  │                                                              │
  └──────────────────────────────────────────────────────────────┘


How Do X Clients Communicate?

** UNDER CONSTRUCTION **
The X.org group has defined a new (2013) protocol ’xcb’ (X-protocol C-language Bindings) which is designed, and we quote: "to replace xlib." This protocol is based on an XML translation layer, and provides, among other things, a non-blocking access to the event queue.

FreeDesktop.org is driving development of this new interface. For detailed information, please visit their website.

                  Main Page: http://xcb.freedesktop.org/


What is a ’Atom’?

While there is no easy answer to this question, in general, an ’Atom’ is a structure which contains indentifying information for some ’Property’. This usually consists of at least a text string with the name of the Atom. For instance, the identifier for a UTF-8 encoded string is defined as "UTF8_STRING".

Certain Atoms are well known and well defined. Others are created by organizations or individuals for use in closely-coupled XWindows clients. The Atom concept is designed to provide maximum flexibility to the XWindows programmer, but this flexibility can be a gigantic pain in the butt. So if you don’t actually NEED to know about Atoms, then save yourself the headache.



What is a ’Selection’?

The X Server documentation describes the data buffers used by the clipboard module as ’Selections’. There are three Selections, each of which is utilized for a different purpose. Within the tbXClip class, these three Selections are defined using enum xselTypes.

enum xselTypes : int
{
   xstNONE = ZERO,    // no type (not a valid selection type)
   xstPRIMARY,        // Atom: XA_PRIMARY
   xstSECONDARY,      // Atom: XA_SECONDARY
   xstCLIPBOARD,      // Atom: XA_CLIPBOARD
} ;

The clipboard ’Selections’ and their contents are ’Properties’.

  1. xstPRIMARY
    The ’Primary Selection’ is the data most recently highlighted using the mouse.
    Note, however, that some applications (e.g. OpenOffice) will put ANY highlighted data into the ’Primary Selection’ whether or not the highlighting was done using the mouse.
  2. xstSECONDARY
    The ’Secondary Selection’ is defined by X, but is not well defined, and is apparently seldom used.
  3. xstCLIPBOARD
    The ’ClipboardSelection’ is the X Server’s system clipboard. If it contains data, it was explicity put there by some XWindows client (or by the X Server itself).
  4. While the tbXClip class can access any of these Selections, at the application level, only the ’xstCLIPBOARD’ Selection is of practical significance.

Because the contents of these Selections can be in any format, it is necessary to convert the data into a format you can use. Either the X Server or the controlling application can be requested to perform the conversion before you import it into your application. For the NcDialog API (or for any console application) the most useful formats are UTF-8 text or the more generic binary character stream, known in X by the Atoms "UTF8_STRING" and "XA_STRING" respectively.



** UNDER CONSTRUCTION **




gString Text Tool

’gString’ is a small, fast and flexible way to seamlessly convert, format and analyze both UTF-8 and wchar_t (’wide’) text.


Introduction to gString

Introduction to a Wider World

Modern applications must be designed for a worldwide audience, and for this reason, the application designer must plan for multi-language support.

Fortunately, the Universal Character Set standard ISO/IEC 10646 and UTF-8, the most popular and flexible method of character-encoding smoothly provide all the character sets, alphabets and special characters which are currently in general use.

Unfortunately, the C and C++ languages offer only minimal support for internationalization. std::string and std::wstring are nothing more than a cruel joke to a serious application designer. The GTK+ toolkit’s Glib::ustring class is an excellent internationalization tool, but it requires installation of the GTK+ toolkit’s ’glib’ and ’glibmm’ libraries. For more information on Glib::ustring, see:
http://library.gnome.org/devel/glibmm/unstable/classGlib_1_1ustring.html

’gString’ falls halfway between the full feature set of Glib::ustring and the meaningless garbage that is std::string. ’gString’ consists of one C++ header file and one C++ source code module. ’gString’ is integrated into the NcDialog API library, but may also be compiled independently as a small (16Kb) link library or the source may be embedded directly into your application.

Preparing to Support Multiple Languages

Here are the basic ideas you will need to understand in order for your application to smoothly support multiple languages.

  1. ASCII (American Standard Code for Information Interchange) is only the very first step in character encoding. It is an ancient and venerable encoding, but supports only the 95 printable characters of the basic Latin alphabet.
    If you think you can say "你是在欺骗自己!" in ASCII, you’re just deluding yourself!
  2. NEVER assume that one character can be represented with one byte.
  3. NEVER assume that one character is one display column in width.
  4. The idea that text flows from left-to-right, may only be PROVISIONALLY assumed, because again: "!ךיז גנידוליד ראָנ טנעז ריא" you’re just deluding yourself. (In fact, the characters of the above Yiddish sentence had to be manually reversed so that it would be displayed correctly in this document.)
  5. NEVER assume that "everyone reads English, so why bother?". Native speakers of Spanish, French, Chinese, the various flavors of Arabic and others (i.e. your potential customers) all have a significant impact on the daily events of our planet, so include them when planning for your next killer app.

See also a discussion of multiple-language support in the NcDialog API,
see Multi-language Support.




gString Public Methods

What follows is a list of all public methods of the gString class.
Methods are arranged in functional groups.

        gString Method Name            Chapter Reference    
 gString [constructor] see gString Instantiation
 ~gString [destructor] 
 operator= see Assignment Operators
  
 compose see Formatted Assignments
  
 formatInt see Integer Formatting
  
 gstr see Data Access
 ustr 
  
 copy see Copying Data
 operator<< 
 substr 
  
 append see Modifying Existing Data
 insert 
 limitChars 
 limitCols 
 shiftChars 
 shiftCols 
 clear 
 erase 
 replace 
  
 compare see Comparisons
 operator== 
 operator!= 
 find 
 after 
  
 gschars see Statistical Info
 gscols 
 utfbytes 
 isASCII 
  
 Get_gString_Version see gString Miscellaneous
 dbMsg 



gString Instantiation

The following are the ’constructors’ for the gString class.

For those new to C++, a constructor creates an ’instance’ of the class. An instance is a particular, named object, and can be thought of as a complex variable.

  • gString ( void ) ;
      Input  :
         none
      Returns:
         nothing
    

    Constructor: Initialize members to default values (NULL string).


  • gString ( const char* usrc, short charLimit=gsMAXCHARS ) ;
      Input  :
         usrc      : pointer to a UTF-8-encoded, null-terminated string
         charLimit : (optional, gsMAXCHARS by default)
                     maximum number of characters from source array to 
                     convert
      Returns:
         nothing
    

    Constructor: Convert specified UTF-8-encoded source to gString.


  • gString ( const wchar_t* wsrc, short charLimit=gsMAXCHARS ) ;
      Input  :
         wsrc      : pointer to a wchar_t-encoded, null-terminated string
         charLimit : (optional, gsMAXCHARS by default)
                     maximum number of characters from source array to 
                     convert
      Returns: nothing
    

    Constructor: Convert specified wchar_t (’wide’) source to gString.


  • gString ( short iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( unsigned short iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( unsigned int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( long int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( unsigned long int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( long long int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;
  • gString ( unsigned long long int iVal, short fWidth, bool lJust = false,
              bool sign = false, bool kibi = false, fiUnits units = fiK ) ;

      Input  :
         iVal  : value to be converted
                 Supported value range: plus/minus 9.999999999999 terabytes
         fWidth: field width (number of display columns)
                 range: 1 to FI_MAX_FIELDWIDTH
         lJust : (optional, false by default)
                 if true, strip leading spaces to left-justify the value
                 in the field. (resulting string may be less than fWidth)
         sign  : (optional, false by default)
                 'false' : only negative values are signed
                 'true'  : always prepend a '+' or '-' sign.
         kibi  : (optional, false by default)
                 'false' : calculate as a decimal value (powers of 10)
                           kilobyte, megabyte, gigabyte, terabyte
                 'true'  : calculate as a binary value (powers of 2)
                           kibibyte, mebibyte, gibibyte, tebibyte
         units  : (optional) member of enum fiUnits (fiK by default)
                  specifies the format for the units suffix.
                  Note that if the uncompressed value fits in the field,
                  then this parameter is ignored.
    
      Returns: nothing
         Note: if field overflow, field will be filled with '#' characters.
    

    Constructor: Convert specified integer value to gString.
    Please see Integer Formatting, 'formatInt' method group for formatting details.


  • gString ( const char* fmt, const void* arg1, ... )
              __attribute__ ((format (gnu_printf, 2, 0))) ;
      Input  :
         fmt       : a format specification string in the style of 
                     sprintf(), swprintf() and related formatting 
                     C/C++ functions.
         arg1      : pointer to first value to be converted by 'fmt'
         ...       : optional arguments (between ZERO and gsfMAXARGS - 1)
                     Each optional argument is a POINTER (address of) the 
                     value to be formatted.
      Returns:
         nothing
    

    Constructor: Convert formatting specification and its arguments to gString. Please refer to the compose() method (see Formatted Assignments) for more information.


  • gString ( const gsForm& gsf, short charLimit=gsMAXCHARS ) ;
      Input  :
         gsf       : initialized gsForm class object containing parameters 
                     for creating a formatted text string.
         charLimit : (optional, gsMAXCHARS by default)
                     maximum number of characters from source array to 
                     convert
      Returns:
         nothing
    

    DEPRECATED: May be removed in a future release.
    This method seemed like a good idea back in 2011, but neither we, nor our beta testers have ever had a desire to use it.

    Constructor: Convert formatting instructions in gsForm class object to gString. See compose() for more information.


  • ~gString ( void ) ;
      Input  :
         none
      Returns:
         nothing
    

    Destructor: Release all resources associated with the gString object.

    Object is destroyed either when it goes out of scope, or by explicitly deleting the object.

    For those new to C++, please note that if you use the ’new’ keyword to create objects, then those objects persist (take up space) until you explicitly delete them or until the application is closed, even if the pointer to the object has gone out-of-scope. See examples below.


    Examples

    void calling_method ( void )
    {
       gString *gsPtr = playful_kitten ( "Hello World!" ) ;
    
       // report contents of object created by called method
       wcout << gsPtr->gstr() << endl ;
       delete gsPtr ; // delete object created by called method
    }
    
    gString* playful_kitten ( const char* msg )
    {
       gString gs_local( "I love tuna!" ) ;  // local object
       gString *gsPtr1 = new gString,        // global object
               *gsPtr2 = new gString(msg) ;  // global object (initialized)
       gString *gsArray = new gString[4] ;   // global array
    
       *gsPtr1 = gs_local ;   // be a kitten: play with the strings...
       gsArray[2] = *gsPtr2 ;
       gsArray[3] = "Scratch my belly!" ;
       gsArray[1] = gsArray[3] ;
    
       delete gsPtr1 ;      // delete object referenced by gsPtr1
       delete [] gsArray ;  // delete object array referenced by gsArray
       return gsPtr2 ;      // return pointer to object referenced by gsPtr2
                            // (caller is responsible for deleting object)
    }           // 'gs_local' goes out of scope and is destroyed here
    



Assignment Operators

For those new to C++, an assignment operator assigns (initializes) the object to the left of the ’=’ using the data on the right of the ’=’. You may also hear the term ’overloaded operator’. This just means that the ’=’ assignment operator may be defined in more than one way, so it will perform different tasks according to the context or circumstance.

  • void operator = ( const char* usrc ) ;
      Input  :
         usrc  : pointer to an array of UTF-8-encoded characters
      Returns:
         nothing
    

    Assignment operator: converts UTF-8-encoded source to gString.

  • void operator = ( const wchar_t* wsrc ) ;
      Input  :
         wsrc  : pointer to an array of wchar_t 'wide' characters
      Returns:
         nothing
    

    Assignment operator: converts wchar_t (’wide’) source to gString.

  • void operator = ( const gString& gssrc ) ;
      Input  :
         gssrc : gString object to be copied (by reference)
      Returns:
         nothing
    

    Assignment operator. Copies one gString object to another.

  • void operator = ( const gsForm& gsf ) ;
      Input  :
         gsf   : an initialized gsForm object (by reference)
      Returns:
         nothing
    

    DEPRECATED: May be removed in a future release.
    Assignment operator: Converts gsForm-class instructions to gString.

Examples

char utf8Data[] = { "Youth is wasted on the young." } ;
gString gs1, gs2 ;

gs1 = utf8Data ;
gs2 = gs1 ;
wcout << gs2 << endl ;
 - - -> Youth is wasted on the young.



Formatted Assignments

  • const wchar_t* compose ( const wchar_t* fmt, ... )
                           __attribute__ ((format (gnu_wprintf, 2, 0))) ;
  • const wchar_t* compose ( const char* fmt, ... )
                           __attribute__ ((format (gnu_printf, 2, 0))) ;
      Input  :
         fmt  : a format specification string in the style of sprintf(),
                swprintf() and related formatting C/C++ functions.
         ...  : optional arguments (between ZERO and gsfMAXARGS)
                Each optional argument is a POINTER (address of) the value
                to be formatted.
                - Important Note: There must be AT LEAST as many optional
                  arguments as the number of format specifiers defined in
                  the formatting string. Excess arguments will be ignored;
                  HOWEVER, too few arguments will result in an application
                  crash. You have been warned.
      Returns:
         const wchar_t* to formatted data
    

    Create formatted text data from a format specification string including between ZERO and gsfMAXARGS format specifications and their corresponding argument pointers.

    Supported data types:
     %d, %i  integer (decimal)
     %o      integer (octal)
     %u      integer (unsigned)
     %x, %X  integer (hex lower or upper case)
     %f      floating point (fixed point)
     %e, %E  floating point (scientific notation, lower/uppercase)
     %g, %G  floating point (normal/exponential, lower/uppercase)
     %a, %A  floating point (hex fraction)
     %c      character
     %C      character (alias for %lc)
     %s      string
     %S      string (alias for %ls)
     %p      pointer
     %b, %B  (extension to swprintf - see description below)
     %m      capture 'errno' description string (see /usr/include/errno.h)
     %n      number of characters printed so far
             (value written to corresponding argument's location)
     %%      literal '%'
    
    See man pages for the C/C++ function 'swprintf' or
    'Table of Output Conversions' for additional details.
    

Examples

char      Greeting[] = { "Hello!" } ;
int       iValue = 27064 ;
long long int qValue = 7842561 ;
long int  lValue = 485772 ;
short int sValue1 = 28875, sValue2 = -261, sValue3 = 529 ;
bool      flagValue = true ;
float     fltValue = 278.5610234 ;
double    dblValue = 9982.5610234 ;
gString gs ;
gs.compose( "%s - %d %12hd, %-hi, %#hx %08lXh %llu %hhd",
            Greeting, &iValue, &sValue1, &sValue2, &sValue3,
            &lValue, &qValue, &flagValue ) ;
wcout << gs << endl ;
 - - -> Hello! - 27064        28875, -261, 0x211 0007698Ch 7842561 1
gs.compose( "floating downstream:%10.2f and doubling our pace:%.4lf",
            &fltValue, &dblValue ) ;
wcout << gs << endl ;
 - - -> floating downstream:    278.56 and doubling our pace:9982.5610

See also formatted instantiation: see gString Instantiation.


Important Note on Formatting

Because THE PARAMETERS ARE POINTERS TO THEIR DATA, similar to the C/C++ library function ’sscanf’ and friends, the compiler cannot perform automatic promotions from short int* to int* or from float* to double*, and so-on as it would for swprintf.

This implementation was selected because a) it eliminates data-width conflicts when moving among hardware platforms, and b) it reduces code size while increasing performance.

This implementation relies on you, the designer, to use care that the data type you specify in the formatting string matches the data type of the variable referenced by its parameter pointer AND that you use the ’address-of’ (’&’) operator to reference non-pointer variables. Note also that ’literal’ values may not be used as parameters because literals have no address.

The following constructs will produce errors:

gString gs ;
char   grade = 'A' ;
short  age   = 21 ;
int    sat   = 1550 ;
double gpa   = 3.75 ;

   // These examples fail to use the 'address-of' operator for the 
   // referenced variables, and will cause a 'segmentation fault' 
   // i.e. an application crash.
   gs.compose( "My grade is an %c", grade ) ;
   gs.compose( "I got a %d on my SAT.", sat ) ;
   // The above should be:
   gs.compose( "My grade is an %c", &grade ) ;
   gs.compose( "I got a %d on my SAT.", &sat ) ;

   // These examples use mismatched format-specification/variable 
   // reference. This will result in either bad data out OR will 
   // cause a memory-access violation.
   gs.compose( "I can't wait to be %d.", &age ) ;
   gs.compose( "My GPA is %1.3f", &gpa ) ;
   gs.compose( "The hex value of %c is: %#x", &grade, &grade ) ;
   gs.compose( "My GPA is %1.3lf", 3.88 ) ; // (literal value)
   // The above should be:
   gs.compose( "I can't wait to be %hd.", &age ) ;
   gs.compose( "My GPA is %1.3lf", &gpa ) ;
   gs.compose( "The hex value of %c is: %#hhx", &grade, &grade ) ;

Parameter Type Checking:
Unfortunately, type-checking of wchar_t formatting strings is not yet supported by the gnu (v:4.8.0) compiler, (but see wchar.h which is preparing for the future). Thus, use care when constructing your ’wchar_t fmt’ formatting string. The ’char fmt’ string IS type-checked.

IMPORTANT NOTE:
Depending on your compiler version, you may get a warning when using the '%b' binary format specification (described below):
"warning: unknown conversion type character ‘b’ in format [-Wformat=]"
This is because the preprocessor does not recognize our custom format specifier. If this happens, use a ’wchar_t’ (wide) formatting template to avoid the preprocessor type checking.

Instead of:
      gs.compose( "bitmask: %b", &wk.mevent.eventType );
Use this (not type checked by the preprocessor):
      gs.compose( L"bitmask: %b", &wk.mevent.eventType );

Formatted binary output (extension to swprintf)

We implement an extension to the swprintf output-conversion-specifiers for binary formatted output. We have found this formatting option useful when working with bit masks, for verifying bit-shifting operations during encryption/decryption and other uses.

  • Base formatting specifier:
    %b , %B
    
    Note that the lower-case / upper-case variants have identical 
    function, and indicate only the case of the identifier character. 
    See format modifiers for data size.
    
  • Format modifiers for data size are the same as for swprintf:
    hh , h , l , ll , L , q
    Examples: %hhb  %hB  %llB  %qb
    
  • Format modifier for prepending of a data-type indicator.
    '#' (hash character)
    This is the same principle as for prepending a '0x' indicator 
    to hex output, and will place either a 'b' or 'B' character at 
    the beginning of the output.
    Examples:  %#hhb -> b0101.1010   %#hhB -> B0101.1010 
    
  • Format modifier for appending of a data-type indicator.
    '-#' (minus sign and hash character)
    Rather than prepending the indicator, the indicator will be 
    append to the end of the output.
    Examples: %-#hhb -> 0101.1010b   %-#hhB -> 0101.1010B
    
  • Format modifier for specifying the group-seperator character.
    By default, the bit groups are seperated by a '.' (fullstop) 
    character. To specify an alternate seperator character:
    % hB   -> 0111 0101 1010 0001   (' ' (space) as seperator)
    %_hB   -> 0111_0101_1010_0001   ('_' (underscore) as seperator)
    %#/hB  -> B0111/0101/1010/0001  ('/' (slash) as seperator)
    %-#-hB -> 0111-0101-1010-0001B  ('-' (dash) as seperator)
    
    Valid seperator characters are any printable ASCII character that
    IS NOT alphabetical, IS NOT a number, and IS NOT a '.'(fullstop)
    
  • Format modifier for specifying bit grouping.
    By default, bits are formatted in groups of four (4 nybble); however,
    if desired, bits can be formatted in groups of eight (8 byte):
    %.8hB    -> 01110101.10100001
    %-.8hB   -> 01110101-10100001
    %# .8hB  -> B01110101 10100001
    %-#`.8hb -> 01110101`10100001b
    

Field-width specification (swprintf bug fix)

The standard library ’swprintf’ function has a design flaw for format specifications that include a field-width specifier.

’swprintf’ pads the string to the specified number of CHARACTERS, not the number of COLUMNS as it should do. For ASCII numeric source values this is not a problem because one character equals one display column. For string source data, however, if the source string contains characters that require more than one display column each, then the output may be too wide.

Therefore, for string-source-formatting specifications ONLY:
          (examples: "%12s"  "%-6s"  "%16ls"  "%5S"  "%-24S")
we compensate for this ethnocentric behavior by interpreting the field-width specifier as number-of-columns, NOT number-of-characters. For non-ASCII string data, this will result in output that appears different (and better) than output created directly by the ’swprintf’ function.


Unsupported format specifications

Conversion modifiers that are not fully supported at this time:
                       ’j’, ’z’, ’t’, ’%[’
Also, the ’*’ field-width specification or precision specification which uses uses the following argument as the width/precision value IS NOT supported.




Integer Formatting

  • bool formatInt ( short iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( unsigned short iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( int iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( unsigned int iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( long iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( unsigned long iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( long long iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  • bool formatInt ( unsigned long long iVal, short fWidth,
                     bool lJust = false, bool sign = false,
                     bool kibi = false, fiUnits units = fiK ) ;
  Input  :
     iVal  : value to be converted
             Supported value range: plus/minus 9.999999999999 terabytes
     fWidth: field width (number of display columns)
             range: 1 to FI_MAX_FIELDWIDTH
     lJust : (optional, false by default)
             if true, strip leading spaces to left-justify the value
             in the field. (resulting string may be less than fWidth)
     sign  : (optional, false by default)
             'false' : only negative values are signed
             'true'  : always prepend a '+' or '-' sign.
     kibi  : (optional, false by default)
             'false' : calculate as a decimal value (powers of 10)
                       kilobyte, megabyte, gigabyte, terabyte
             'true'  : calculate as a binary value (powers of 2)
                       kibibyte, mebibyte, gibibyte, tebibyte
     units  : (optional) member of enum fiUnits (fiK by default)
              specifies the format for the units suffix.
              Note that if the uncompressed value fits in the field,
              then this parameter is ignored.

  Returns:
     'true' if successful
     'false' if field overflow (field will be filled with '#' chars)
             See notes below on field overflow.

Convert an integer value into a formatted display string of the specified width. Value is right-justified in the field, with leading spaces added if necessary (but see ’lJust’ parameter).

Maximum field width is FI_MAX_FIELDWIDTH. This is wide enough to display a 13-digit, signed and comma-formatted value: ’+9,876,543,210,777’

Actual formatting of the value depends on the combination of:
  a) magnitude of the value
  b) whether it is a signed value
  c) the specified field-width
  d) the specified suffix format
  e) locale-specific grouping of digits according the LC_NUMERIC locale 
     environment variable
  f) See notes below on the possible reasons for field overflow:
     see field overflow
     
The following examples are based on the ’C’ and U.S. English locale.

Examples

1) Simple comma formatted output if specified field-width is sufficient.
   345    654,345    782,654,345    4,294,967,295

2) Output with values compressed to fit a specified field width.
   12.3K    999K    12.345M    1.234G    4.3G

   
gString gs ;      // gString object

3) Convert a signed integer value:
   int iValue = 28954 ;

   // field width == 8, right justified (note: compression unnecessary)
   gs.formatInt( iValue, 8 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :  28,954:

   // field width == 8, left justified (note: compression unnecessary)
   gs.formatInt( iValue, 8, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :28,954:

   // field width == 6
   gs.formatInt( iValue, 6 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :28.95K:

   // field width == 6 with forced sign
   gs.formatInt( iValue, 6, false, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :+29.0K:

   // field width == 5
   gs.formatInt( iValue, 5 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :29.0K:

   iValue = -28954 ;    // convert negative source value

   // field width == 8, right justified (note: compression unnecessary)
   gs.formatInt( iValue, 8 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  : -28,954:

   // field width == 8, left justified (note: compression unnecessary)
   gs.formatInt( iValue, 8, true ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :-28,954:

   // field width == 6
   gs.formatInt( iValue, 6 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :-29.0K:

   // field width == 5
   gs.formatInt( iValue, 5 ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  : -29K:

4) Convert an unsigned long long integer value (field width == 11):
   unsigned long long int qValue = 39000009995 ;

   // decimal compression (gigabytes) with "official" IEC suffix
   gs.formatInt( qValue, 11, false, false, false, fikB ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :39.000010gB:

   // binary compression (gibibytes) with "official" IEC suffix
   gs.formatInt( qValue, 11, false, false, true, fiKiB ) ;
   wcout << ':' << gs << ':' << endl ;
    - - >  :38.08595GiB:

Please see (NcDialog test application, ’Dialogw’ for more examples.)
See Dialogw App.


   Important Note: As of gString version 0.0.25, the prototypes for the old ’formatInteger’ methods have been
   replaced by the ’formatInt’ method group.

   If your existing code calls the old ’formatInteger’ methods, then you will need to update your code.
   Only the method name needs to be changed from ’formatInteger’ to ’formatInt’. This is because only the first
   three(3) parameters of ’formatInteger’ had been implemented anyway. Happy formatting!


Notes on the formatInt group

Optional specification of the units suffix for the ’formatInt’ methods.
Units are specified using the optional 'units' parameter, which
is a member of the 'fiUnits' enumerated type..

enum fiUnits : short
{
   fiK,     // 'K'   'M'   'G'   'T'   (default)
   fik,     // 'k'   'm'   'g'   't'
   fiKb,    // 'Kb'  'Mb'  'Gb'  'Tb'
   fikB,    // 'kB'  'mB'  'gB'  'tB'  ("official" metric 'kilo' designation)  
   fiKiB,   // 'KiB' 'MiB' 'GiB' 'TiB' ("official" binary 'kibi' designation)
} ;

The ’formatInt’ methods use decimal (powers of 10) compression 
calculations by default. To use binary (powers of 2) compression, 
use the optional 'kibi' parameter.

   DECIMAL                      BINARY
   kilobytes (x/1000)           kibibytes (x/1024)
   megabytes (x/1000000)        mibibytes (x/1024000)
   gigabytes (x/1000000000)     gibibytes (x/1024000000)
   terabytes (x/1000000000000)  tebibytes (x/1024000000000)

The kilo/kibi controversy

The IEC (International System of Quantities) recommends lower-case for metric (powers of 10) and upper-case for binary (powers of 2). However, unless you must be accurate in presenting the data according to IEC standard, it is recommended that you choose the format according to: your space requirements, visual appeal, and clear communication with your users.
If you blindly follow style standards against your own better judgement, then be forever labelled as a weenie.


formatInt field overflow

As described above, the actual formatting of a fixed-width integer field depends on a number of factors. Every effort is made to compress the data to fit within the field while retaining an accurate representation of the numeric value.

There are cases, however, where it is not possible to represent the data within the specified field width. When this occurs, the entire field will be filled with HASH characters '#'.

The specified field must be wide enough to accomodate either the entire, uncompressed value, or the combination of compressed value, units designator and sign (if any). The following situations may cause field overflow.

 a) Values <= -10.0 Tbytes or >= 10.0 Tbytes cannot be represented
    by 'formatInt' methods.
 b) One-column fields can display values between 0 and 9.
    Values outside this range will cause overflow.
 c) Two-column fields can display values between -9 and 99.
    Values outside this range will cause overflow.
 d) Three-column fields can display compressed data
    only if the combined width of value, sign and units require
    no more than three(3) columns.
 e) Four-column fields can display compressed data
    only if the combined width of value, sign and units require
    no more than four(4) columns.
 f) Five-column fields can accurately display any value IF
    the units designator requires only one(1) column.
 g) Six-column fields can accurately display any value IF
    the units designator requires no more than two(2) columns.

Fields of seven(7) or more columns can display any formatted value without danger of overflow.




Data Access

  • const wchar_t* gstr ( void ) const ;
      Input  :
         none
      Returns:
         const pointer to array of wchar_t characters
    

    Return a const pointer to the wchar_t (wide) character array.


  • const wchar_t* gstr ( short& charCount ) const ;
      Input  :
         charCount : (by reference, initial value ignored)
                     receives number of characters in array, 
                     including null terminator
      Returns:
         const pointer to array of wchar_t characters
    

    Return a const pointer to the wchar_t (wide) character array, along with the number of characters in the array (including the null terminator).


  • const char* ustr ( void ) const ;
      Input  :
         none
      Returns:
         const pointer to array of UTF-8 characters
    

    Return a const pointer to the char (UTF-8) character array.


  • const char* ustr ( short& charCount, short& byteCount ) const ;
      Input  :
         charCount : (by reference, initial value ignored)
                     receives number of characters in array, 
                     including null terminator
         byteCount : (by reference, initial value ignored)
                     receives number of bytes in array, 
                     including null terminator
      Returns:
         const pointer to array of UTF-8 characters
    

    Return a const pointer to the char (UTF-8) character array, along with the number of characters and the number of bytes in the array (including the null terminator).


Examples

short charCount, byteCount ;
gString gs( "Wherever you go, there you are!" ) ;

const wchar_t* wPtr = gs.gstr() ;
const wchar_t* wPtr = gs.gstr( charCount ) ;
const char* utf8Ptr = gs.ustr() ;
const char* utf8Ptr = gs.ustr( charCount, byteCount ) ;



Copying Data

  • short copy ( char* uTarget, short maxBytes,
                 short maxCols=(gsMAXCHARS*2) ) const ;
      Input  :
         uTarget  : pointer to target array to receive UTF-8-encoded text
         maxBytes : maximum number of bytes to copy (incl. NULL terminator)
         maxCols  : (optional, default == gsMAXCHARS*2)
                    maximum number of display-columns to copy
      Returns:
         number of bytes copied (incl. NULL terminator)
    

    Copy gString text to specified target buffer.


  • short copy ( wchar_t* wTarget, short maxChars,
                 short maxCols=(gsMAXCHARS*2) ) const ;
      Input  :
         wTarget  : pointer to target array to receive wchar_t 'wide' text
         maxChars : maximum number of characters to copy (incl. NULL)
         maxCols  : (optional, default == gsMAXCHARS*2)
                    maximum number of display-columns to copy
      Returns:
         number of characters copied (incl. NULL terminator)
    

    Copy gString text to specified target buffer.


  • std::wostream& operator<< ( std:wostream& os,
                                const gString& gs2 );
      Input  :
         IMPLIED reference to the output stream
         IMPLIED reference to the gString object
      Returns: reference to the specified output stream
    

    !! NON-MEMBER METHOD !!
    Insertion operator: Copies the contents of the gString object into the ’wcout’ (wide) standard output stream.

    Note that due to the way the output stream is defined, you cannot mix 
    ’cout’ (narrow) and ’wcout’ (wide) output streams indiscriminately.
      -- If ’wcout’ is called first, then ’cout’ is disabled.
      -- If ’cout’ is called first, then both narrow and wide 
         channels are active.
      -- ’wcout’ handles both narrow and wide data, however, 
         ’cout’ handles ONLY narrow data.
    This is not related to gString, but is a characteristic of the default 
    C++ output stream itself. We recommend that you always use the ’wcout’ 
    stream in console applications for both narrow and wide text data.
    

  • std::ostream& operator<< ( std:ostream& os,
                               const gString& gs2 );
      Input  :
         IMPLIED reference to the output stream
         IMPLIED reference to the gString object
      Returns: reference to the specified output stream
    

    !! NON-MEMBER METHOD !!
    Insertion operator: Copies the contents of the gString object into the ’cout’ (narrow) standard output stream.

    IMPORTANT NOTE: Access to the narrow output stream is 
    provided for convenience only. It is recommended that the 
    wide stream version (above), if available on your system, 
    be used exclusively.
    

  • short substr ( char* uTarget,
                   short offset, short charCnt ) const ;
  • short substr ( wchar_t* wTarget,
                   short offset, short charCnt ) const ;
  • short substr ( gString& wTarget,
                   short offset, short charCnt ) const ;
      Input  :
         targ    : (by reference, initial contents ignored)
                   receives null-terminated contents of specified
                   character range
                   -- If target buffer is a char*, then data returned is 
                      a UTF-8 text string.
                   -- If target buffer is a wchar_t*, then data returned is 
                      a wchar_t (wide) text string.
                   IMPORTANT NOTE: It is the caller's responsibility to 
                   specify a target buffer large enough to hold the data.
                   Recommended: wchar_t wbuff[gsMAXCHARS]; or 
                                char ubuff[gsMAXBYTES];
                   -- If target buffer is a gString object, then both 
                      UTF-8 and wchar_t data are returned 
                      (with no chance of target buffer overflow).
    
         offset  : character index at which substring begins
                   (this IS NOT a byte index)
         charCnt : number of characters to copy (not incl. NULL terminator)
      Returns:
         if target is a wchar_t* or gString object, then returns number of
           characters written to target (not including the NULL terminator)
         if target is a char*, then returns number of bytes written to
           target (not including the NULL terminator)
    
         Note: returns ZERO if either 'offset' or 'charCnt' out of range
         Note: If 'charCnt' extends beyond the end of the source data, 
               then returns the available data.
    

    Copy the specified character range to target buffer.
    These methods copy the indicated substring (null terminated) to the target buffer, leaving the original data unchanged.

    If you have a fixed-format field, then the offset and character count will be known in advance. Otherwise you can use the ’find()’ method to locate the substring to be copied.

    Please Note: The number of bytes can NEVER be assumed to be the same as the number of characters.
    Please see Multi-language Support.


Examples

gString gs( "That's not flying, that's falling -- with style!\n"
            "Buzz Lightyear" ) ;
char utf8Data[gsMAXBYTES] ;
wchar_t wideData[gsMAXCHARS] ;

gs.copy( utf8Data, gs.utfbytes() ) ;
gs.copy( wideData, gs.gschars() ) ;

gString gstream( "You're a child's TOY! -- Woody" ) ;
wcout << gstream << endl ;

// get a copy of the first word starting with 'c'
gString AusAnimal( "Aardvark Kangaroo Cockatoo Dingo Wombat " ) ;
gString gsc ;
short b = AusAnimal.find( " c" ) ;
if ( b >= 0 )
{
   short e = AusAnimal.find( L' ', b + 1 ) ;
   if ( e > b )
   {
      AusAnimal.substr( gsc, (b + 1), (e - b - 1) ) ;
      wcout << gsc << endl ;
   }
}
 - - -> Cockatoo



Modifying Existing Data

  • short append ( const wchar_t* wPtr ) ;
  • short append ( const char* uPtr ) ;
  • short append ( const wchar_t wChar ) ;
      Input  :
         wPtr  : pointer to array of wchar_t 'wide' text to be appended
                 OR
         uPtr  : pointer to array of char UTF-8 text to be appended
                 OR
         wChar : a single, 'wide' character
      Returns:
         number of characters in resulting string (incl. NULL terminator)
         Note: if value returned equals gsMAXCHARS, then 
               some data MAY HAVE BEEN discarded.
    

    Append text to existing gString text data up to a combined length of gsMAXCHARS. Characters in excess of the maximum will not be appended.

    Example

    gString gs( L"Be kind to your manager." ) ;
    gs.limitChars( gs.gschars() - 2 ) ;
    gs.append( L", and other lower forms of life." ) ;
    wcout << gs << endl ;
     - - -> Be kind to your manager, and other lower forms of life.
    


  • short append ( const wchar_t* fmt, const void* arg1, ... )
                   __attribute__ ((format (gnu_wprintf, 2, 0)));
  • short append ( const char* fmt, const void* arg1, ... )
                   __attribute__ ((format (gnu_printf, 2, 0)));
      Input  :
         fmt  : a format specification string in the style of sprintf(),
                swprintf() and related formatting C/C++ functions.
         arg1 : pointer to first value to be converted by 'fmt'
         ...  : optional arguments (between ZERO and gsfMAXARGS - 1)
                Each optional argument is a POINTER (address of) the value
                to be formatted.
    
      Returns:
         number of characters in resulting string (incl. NULL terminator)
         Note: if return equals gsMAXCHARS, then
               some data MAY HAVE BEEN discarded.
    

    Append formatted text data to existing gString text data up to a combined length of gsMAXCHARS. Characters in excess of the maxmum will not be appended.

    Please refer to the ’compose’ method (see Formatted Assignments) for more information on converting data using a format specification string.

    Example

    short gaddress = 2840 ;
    wchar_t gdirection = L'E' ;
    const wchar_t* gstreet = L"Colorado Blvd." ;
    double gcost = 29.95 ;
    gString gs( "Gorilla Men's Clothing" ) ; // existing text
    
    gs.append( ", %hd %C %S\n  Dress shirts on sale, $%.2lf.", 
               &gaddress, &gdirection, gstreet, &gcost ) ;
    
    wcout << gs << endl ;
     - - -> Gorilla Men's Clothing, 2840 E Colorado Blvd.
              Dress shirts on sale, $29.95.
    


  • short insert ( const wchar_t* wPtr, short offset = 0 ) ;
  • short insert ( const char* uPtr, short offset = 0 ) ;
  • short insert ( wchar_t wChar, short offset = 0 ) ;
      Input  : 
         wPtr  : pointer to array of wchar_t 'wide' text to be inserted
                 OR
         uPtr  : pointer to array of char UTF-8 text to be inserted
                 OR
         wChar : a single wchar_t 'wide' character
         offset: (optional, ZERO by default)
                 character offset at which to insert specified text into
                 existing text.
                 Note: if specified 'offset' > number of characters in
                       existing text, then acts like 'append' method.
      Returns:
         number of characters in resulting string (incl. NULL terminator)
         Note: if value returned equals gsMAXCHARS, then 
               some data MAY HAVE BEEN discarded.
    

    Insert text into existing gString text data up to a combined length of gsMAXCHARS. Characters in excess of the maximum will be truncated.

    Example

    gString gs( L"Remember to hurt people!" ) ;
    gs.insert( L"NOT ", 9 ) ;
    wcout << gs << endl ;
     - - -> Remember NOT to hurt people!
    


  • short limitChars ( short charCount ) ;
      Input  :
         charCount : maximum number of characters allowed in formatted data 
                     (not including NULL) Range: 1 to gsMAXCHARS-1
      Returns:
         number of characters in the adjusted data (including NULL)
    

    Truncate the data to no more than charCount display characters.
    Insert a null terminator after the specified number of characters.

    Example

    gString gs( "This shirt is available in yellow or red." ) ;
    gs.limitChars( 34 ) ;
    gs.append( "only." ) ;
    wcout << gs << endl ;
     - - -> This shirt is available in yellow only.
    


  • short limitCols ( short colCount ) ;
      Input  :
         colCount : maximum number of display columns allowed in formatted data
                    Range: 1 to (gsMAXCHARS * 2)
      Returns:
         number of columns needed to display the adjusted data
         Note: If specified column count occurs in mid-character, then the 
               partial character will be removed from the string.
    

    Truncate the data to no more than colCount display columns. Insert a null terminator after the number of characters required to fill the specified number of display columns.

    Example

    gString gs( "The manual is located at:\n"
                "http://cdn.funcom.com/aoc/pdf/aoc_manual.pdf" ) ;
    gs.limitCols( 55 ) ;
    wcout << gs << endl ;
     - - -> The manual is located at:
            http://cdn.funcom.com/aoc/pdf/
    
    Note that there are 25 display columns for the first line, (the newline 
    character requires no column), and 30 columns remain on the second line.
    


  • short shiftChars ( short shiftCount, wchar_t padChar = L’ ’ ) ;
      Input  :
         shiftCount: < ZERO: shift data to the left, discarding the
                             specified number of characters from the
                             beginning of the array
                     > ZERO: shift data to the right, padding the vacated
                             positions on the left with 'padChar'
                     ==ZERO: do nothing
         padChar   : (optional, SPACE character, 0x20 by default)
                     when shifting data to the right, use this character
                     to fill the vacated character positions
                     NOTE: Specify a one-column character ONLY as the
                     padding character. (multi-column characters ignored)
      Returns:
         number of characters in adjusted array
    

    Shift text data by the specified number of characters.

    Note for writers of RTL (right-to-left) languages:
     In the above descriptions, the terms 'left' and 'right' are used for 
     convenience, but actually 'left' refers to the head of the data 
     and 'right' refers to the tail.
    

    Example