-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial.sgml
1483 lines (1095 loc) · 57.9 KB
/
tutorial.sgml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE Article PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
<article>
<artheader>
<title>Writing Programs Using <literal remap="tt">newt</literal></title>
<author>
<firstname>Erik Troan, <[email protected]></firstname>
</author>
<pubdate>v0.31, 2003-Jan-06</pubdate>
<abstract>
<para>
The <literal remap="tt">newt</literal> windowing system is a terminal-based window and widget
library designed for writing applications with a simple, but user-friendly,
interface. While <literal remap="tt">newt</literal> is not intended to provide the rich feature
set advanced applications may require, it has proven to be flexible enough
for a wide range of applications (most notably, Red Hat's installation
process). This tutorial explains the design philosophy behind <literal remap="tt">newt</literal> and
how to use <literal remap="tt">newt</literal> from your programs.
</para>
</abstract>
</artheader>
<sect1><title>Introduction</title>
<para>
<literal remap="tt">Newt</literal> has a definite design philosophy behind it, and knowing that design
makes it significantly easier to craft robust <literal remap="tt">newt</literal> applications. This
tutorial documents <literal remap="tt">newt</literal> 0.30 --- older versions of <literal remap="tt">newt</literal> had
annoying inconsistencies in it (which writing this tutorial pointed out),
which were removed while this tutorial was written. The latest version of
<literal remap="tt">newt</literal> is always available from Red Hat.</para>
<sect2><title>Background</title>
<para>
<literal remap="tt">Newt</literal> was originally designed for use in the install code for
Red Hat Linux. As this install code runs in an environment with limited
resources (most importantly limited filesystem space), <literal remap="tt">newt</literal>'s size
was immediately an issue. To help minimize its size, the following design
decisions were made early in its implementation:
</para>
<itemizedlist>
<listitem>
<para>
<literal remap="tt">newt</literal> does not use an event-driven architecture.
</para>
</listitem>
<listitem>
<para>
<literal remap="tt">newt</literal> is written in C, not C++. While there has been interest
in constructing C++ wrapper classes around the <literal remap="tt">newt</literal> API, nothing has
yet come of those ideas.</para>
</listitem>
<listitem>
<para>
Windows must be created and destroyed as a stack (in other words, all
<literal remap="tt">newt</literal> windows behave as modal dialogs). This is probably
the greatest functionality restriction of <literal remap="tt">newt</literal>.</para>
</listitem>
<listitem>
<para>
The tty keyboard is the only supported input device.</para>
</listitem>
<listitem>
<para>
Many behaviours, such as widget traversal order, are difficult
or impossible to change.
</para>
</listitem>
</itemizedlist>
<para>
While <literal remap="tt">newt</literal> provides a complete API, it does not handle the low-level
screen drawing itself. Instead, <literal remap="tt">newt</literal> is layered on top of the screen
management capabilities of John E. Davis's
<ulink url="ftp://space.mit.edu/pub/davis/slang/">S-Lang</ulink> library.</para></sect2>
<sect2><title>Designing <literal remap="tt">newt</literal> applications</title>
<para>
As <literal remap="tt">newt</literal> is not event driven and forces modal windows (forcing window
order to behave like a stack), newt applications tend to look quite like
other text-mode programs. It is quite straightforward to convert a command
line program which uses simple user prompts into a <literal remap="tt">newt</literal> application.
Some of the programs run as part of the Red Hat installation process
(such as <literal remap="tt">Xconfigurator</literal> and <literal remap="tt">mouseconfig</literal>) were originally written
as simple terminal mode programs which used line-oriented menus to get
input from the user and were later converted into <literal remap="tt">newt</literal> applications
(through a process affectionately known as newtering). Such a conversion
does not require changes to the control flow of most applications.
Programming <literal remap="tt">newt</literal> is dramatically different from writing programs for
most other windowing systems as <literal remap="tt">newt</literal>'s API is not event driven. This
means that <literal remap="tt">newt</literal> applications look dramatically different from programs
written for event-driven architectures such as Motif, <literal remap="tt">gtk</literal>, or even
Borland's old TurboVision libraries.
When you're designing your <literal remap="tt">newt</literal> program, keep this differentiation
in mind. As long as you plan your application to call a function to
get input and then continue (rather then having your program called
when input is ready), programming with the newt libraries should be
simple.</para></sect2>
<sect2><title>Components</title>
<para>
Displayable items in <literal remap="tt">newt</literal> are known as <emphasis remap="bf">components</emphasis>, which are
analogous to the widgets provided by most Unix widget sets. There are
two main types of components in <literal remap="tt">newt</literal>, forms and everything else.
Forms logically group components into functional sets. When an application
is ready to get input from a user, it ``runs a form'', which makes the
form active and lets the user enter information into the components the
form contains. A form may contain any other component, including other
forms. Using subforms in this manner lets the application change the details
of how the user tabs between components on the form, scroll regions of the
screen, and control background colors for portions of windows.
Every component is of type <literal remap="tt">newtComponent</literal>, which is an opaque type. It's
guaranteed to be a pointer though, which lets applications move it through
void pointers if the need arises. Variables of type <literal remap="tt">newtComponent</literal> should
never be directly manipulated -- they should only be passed to <literal remap="tt">newt</literal>
functions. As <literal remap="tt">newtComponent</literal> variables are pointers, remember that
they are always passed by value -- if you pass a <literal remap="tt">newtComponent</literal> to
a function which manipulates it, that component is manipulated everywhere,
not just inside of that function (which is nearly always the behaviour
you want).</para></sect2>
<sect2><title>Conventions</title>
<para>
<literal remap="tt">Newt</literal> uses a number of conventions to make it easier for programmers
to use.
<itemizedlist>
<listitem>
<para>
All functions which manipulate data structures take the data
structure being modified as their first parameter. For example, all
of the functions which manipulate forms expect the <literal remap="tt">newtComponent</literal>
for that form to be the first parameter.</para></listitem>
<listitem>
<para>
As <literal remap="tt">newt</literal> is loosely typed (forcing all of the components into
a single variable makes coding easier, but nullifies the value of type
checking), <literal remap="tt">newt</literal> functions include the name of the type they are
manipulating. An example of this is <literal remap="tt">newtFormAddComponent()</literal>, which
adds a component to a form. Note that the first parameter to this function
is a form, as the name would suggest.</para></listitem>
<listitem>
<para>
When screen coordinates are passed into a function, the
x location precedes the y location. To help keep this clear,
we'll use the words ``left'' and ``top'' to describe those indicators (with
left corresponding to the x position).</para></listitem>
<listitem>
<para>
When box sizes are passed, the horizontal width precedes the vertical
width.</para></listitem>
<listitem>
<para>
When both a screen location and a box size are being passed, the
screen location precedes the box size.</para></listitem>
<listitem>
<para>
When any component other then a form is created, the first two
parameters are always the (left, right) location.</para></listitem>
<listitem>
<para>
Many functions take a set of flags as the final parameter. These
flags may be logically ORed together to pass more then one flag at a time.</para></listitem>
<listitem>
<para>
<literal remap="tt">Newt</literal> uses <emphasis remap="bf">callback</emphasis> functions to convey certain events to
the application. While callbacks differ slightly in their parameters, most
of them allow the application to specify an arbitrary argument to be passed
to the callback when the callback is invoked. This argument is always a
<literal remap="tt">void *</literal>, which allows the application great flexibility.
</para>
</listitem>
</itemizedlist>
</para></sect2></sect1>
<sect1><title>Basic <literal remap="tt">Newt</literal> Functions</title>
<para>
While most <literal remap="tt">newt</literal> functions are concerned with widgets or groups
of widgets (called grids and forms), some parts of the <literal remap="tt">newt</literal> API
deal with more global issues, such as initializing <literal remap="tt">newt</literal> or writing
to the root window.</para>
<sect2><title>Starting and Ending <literal remap="tt">newt</literal> Services</title>
<para>
There are three functions which nearly every <literal remap="tt">newt</literal> application use. The
first two are used to initialize the system.
<screen>
int newtInit(void);
void newtCls(void);
</screen>
<literal remap="tt">newtInit()</literal> should be the first function called by every <literal remap="tt">newt</literal>
program. It initializes internal data structures and places the terminal
in raw mode. Most applications invoke <literal remap="tt">newtCls()</literal> immediately after
<literal remap="tt">newtInit()</literal>, which causes the screen to be cleared. It's not
necessary to call <literal remap="tt">newtCls()</literal> to use any of <literal remap="tt">newt</literal>'s features, but
doing so will normally give a much neater appearance.
When a <literal remap="tt">newt</literal> program is ready to exit, it should call <literal remap="tt">newtFinished()</literal>.
<screen>
int newtFinished(void);
</screen>
<literal remap="tt">newtFinished()</literal> restores the terminal to its appearance when
<literal remap="tt">newtInit()</literal> was called (if possible -- on some terminals the cursor will
be moved to the bottom, but it won't be possible to remember the original
terminal contents) and places the terminal in its original input state.
If this function isn't called, the terminal will probably need to be
reset with the <literal remap="tt">reset</literal> command before it can be used easily.</para></sect2>
<sect2><title>Handling Keyboard Input</title>
<para>
Normally, <literal remap="tt">newt</literal> programs don't read input directly from the
user. Instead, they let <literal remap="tt">newt</literal> read the input and hand it to the
program in a semi-digested form. <literal remap="tt">Newt</literal> does provide a couple of simple
functions which give programs (a bit of) control over the terminal.
<screen>
void newtWaitForKey(void);
void newtClearKeyBuffer(void);
</screen></para>
<para>
The first of these, <literal remap="tt">newtWaitForKey()</literal>, doesn't return until a key
has been pressed. The keystroke is then ignored. If a key is already in
the terminal's buffer, <literal remap="tt">newtWaitForKey()</literal> discards a keystroke and
returns immediately.
<literal remap="tt">newtClearKeyBuffer()</literal> discards the contents of the terminal's input
buffer without waiting for additional input.</para></sect2>
<sect2><title>Drawing on the Root Window</title>
<para>
The background of the terminal's display (the part without any windows
covering it) is known as the <emphasis remap="bf">root window</emphasis> (it's the parent of all
windows, just like the system's root directory is the parent of all
subdirectories). Normally, applications don't use the root window, instead
drawing all of their text inside of windows (<literal remap="tt">newt</literal> doesn't require
this though -- widgets may be placed directly on the root window without
difficulty). It is often desirable to display some text, such as a
program's name or copyright information, on the root window, however.
<literal remap="tt">Newt</literal> provides two ways of displaying text on the root window. These
functions may be called at any time. They are the only <literal remap="tt">newt</literal> functions
which are meant to write outside of the current window.
<screen>
void newtDrawRootText(int left, int top, const char * text);
</screen></para>
<para>
This function is straightforward. It displays the string <literal remap="tt">text</literal> at
the position indicated. If either the <literal remap="tt">left</literal> or <literal remap="tt">top</literal> is
negative, the position is measured from the opposite side of the
screen. The final measurement will seem to be off by one though. For
example, a <literal remap="tt">top</literal> of -1 indicates the last line on the screen, and
one of -2 is the line above that.
As it's common to use the last line on the screen to display help information,
<literal remap="tt">newt</literal> includes special support for doing exactly that. The last
line on the display is known as the <emphasis remap="bf">help line</emphasis>, and is treated as a
stack. As the value of the help line normally relates to the window
currently displayed, using the same structure for window order and the
help line is very natural. Two functions are provided to manipulate the
help line.
<screen>
void newtPushHelpLine(const char * text);
void newtPopHelpLine(void);
</screen>
The first function, <literal remap="tt">newtPushHelpLine()</literal>, saves the current help line
on a stack (which is independent of the window stack) and displays the
new line. If <literal remap="tt">text</literal> is <literal remap="tt">NULL</literal>, <literal remap="tt">newt</literal>'s default help line is
displayed (which provides basic instructions on using <literal remap="tt">newt</literal>). If
<literal remap="tt">text</literal> is a string of length 0, the help line is cleared. For all
other values of <literal remap="tt">text</literal>, the passed string is displayed at the bottom,
left-hand corner of the display. The space between the end of the displayed
string the the right-hand edge of the terminal is cleared.
<literal remap="tt">newtPopHelpLine()</literal> replaces the current help line with the one it
replaced. It's important not to call tt/newtPopHelpLine()/ more then
<literal remap="tt">newtPushHelpLine()</literal>!
<literal remap="tt">Suspending Newt Applications</literal>
By default, <literal remap="tt">newt</literal> programs cannot be suspended by the user (compare
this to most Unix programs which can be suspended by pressing the suspend
key (normally <literal remap="tt">^Z</literal>). Instead, programs can specify a <emphasis remap="bf">callback</emphasis>
function which gets invoked when the user presses the suspend key.
<screen>
typedef void (*newtSuspendCallback)(void);
void newtSetSuspendCallback(newtSuspendCallback cb);
</screen>
The suspend function neither expects nor returns any value, and can
do whatever it likes to when it is invoked. If no suspend callback
is registered, the suspend keystroke is ignored.
If the application should suspend and continue like most user applications,
the suspend callback needs two other <literal remap="tt">newt</literal> functions.
<screen>
void newtSuspend(void);
void newtResume(void);
</screen>
<literal remap="tt">newtSuspend()</literal> tells <literal remap="tt">newt</literal> to return the terminal to its initial
state. Once this is done, the application can suspend itself (by
sending itself a <literal remap="tt">SIGTSTP</literal>, fork a child program, or do whatever
else it likes. When it wants to resume using the <literal remap="tt">newt</literal> interface,
it must call <literal remap="tt">newtResume</literal> before doing so.
Note that suspend callbacks are not signal handlers. When <literal remap="tt">newtInit()</literal>
takes over the terminal, it disables the part of the terminal interface
which sends the suspend signal. Instead, if <literal remap="tt">newt</literal> sees the suspend
keystroke during normal input processing, it immediately calls the suspend
callback if one has been set. This means that suspending newt applications
is not asynchronous.</para></sect2>
<sect2><title>Refreshing the Screen</title>
<para>
To increase performance, S-Lang only updates the display when it needs
to, not when the program tells S-Lang to write to the terminal. ``When it
needs to'' is implemented as ``right before the we wait for the user to
press a key''. While this allows for optimized screen displays most of
the time, this optimization makes things difficult for programs which
want to display progress messages without forcing the user to input
characters. Applications can force S-Lang to immediately update modified
portions of the screen by calling <literal remap="tt">newtRefresh</literal>.
<orderedlist>
<listitem>
<para>
The program wants to display a progress message, without forcing
for the user to enter any characters.</para></listitem>
<listitem>
<para>
A misfeature of the program causes part of the screen to be
corrupted. Ideally, the program would be fixed, but that may not
always be practical.
</para>
</listitem></orderedlist>
</para></sect2>
<sect2><title>Other Miscellaneous Functions</title>
<para>
As always, some function defy characterization. Two of <literal remap="tt">newt</literal>'s general
function fit this oddball category.
<screen>
void newtBell(void);
void newtGetScreenSize(int * cols, int * rows);
</screen>
The first sends a beep to the terminal. Depending on the terminal's
settings, this been may or may not be audible. The second function,
<literal remap="tt">newtGetScreenSize()</literal>, fills in the passed pointers with the
current size of the terminal.</para></sect2>
<sect2><title>Basic <literal remap="tt">newt</literal> Example</title>
<para>
To help illustrate the functions presented in this section here is a short
sample <literal remap="tt">newt</literal> program which uses many of them. While it doesn't do
anything interesting, it does show the basic structure of <literal remap="tt">newt</literal> programs.
<screen>
#include <newt.h>
#include <stdlib.h>
int main(void) {
newtInit();
newtCls();
newtDrawRootText(0, 0, "Some root text");
newtDrawRootText(-25, -2, "Root text in the other corner");
newtPushHelpLine(NULL);
newtRefresh();
sleep(1);
newtPushHelpLine("A help line");
newtRefresh();
sleep(1);
newtPopHelpLine();
newtRefresh();
sleep(1);
newtFinished();
}
</screen></para></sect2></sect1>
<sect1><title>Windows</title>
<para>
While most <literal remap="tt">newt</literal> applications do use windows, <literal remap="tt">newt</literal>'s window
support is actually extremely limited. Windows must be destroyed in the
opposite of the order they were created, and only the topmost window may be
active. Corollaries to this are:
<itemizedlist>
<listitem>
<para>
The user may not switch between windows.</para></listitem>
<listitem>
<para>
Only the top window may be destroyed.
</para>
</listitem></itemizedlist>
While this is quite a severe limitation, adopting it greatly simplifies
both writing <literal remap="tt">newt</literal> applications and developing <literal remap="tt">newt</literal> itself, as it
separates <literal remap="tt">newt</literal> from the world of event-driven programming. However,
this tradeoff between function and simplicity may make <literal remap="tt">newt</literal>
unsuitable for some tasks.
</para>
<sect2><title>Creating Windows</title>
<para>
There are two main ways of opening <literal remap="tt">newt</literal> windows: with or without
explicit sizings. When grids (which will be introduced later in this
tutorial) are used, a window may be made to just fit the grid. When
grids are not used, explicit sizing must be given.
<screen>
int newtCenteredWindow(int width, int height, const char * title);
int newtOpenWindow(int left, int top, int width, int height,
const char * title);
</screen>
The first of these functions open a centered window of the specified
size. The <literal remap="tt">title</literal> is optional -- if it is <literal remap="tt">NULL</literal>, then no title
is used. <literal remap="tt">newtOpenWindow*(</literal> is similar, but it requires a specific
location for the upper left-hand corner of the window.</para></sect2>
<sect2><title>Destroying Windows</title>
<para>
All windows are destroyed in the same manner, no matter how the windows
were originally created.
<screen>
void newtPopWindow(void);
</screen>
This function removes the top window from the display, and redraws the
display areas which the window overwrote.</para></sect2></sect1>
<sect1><title>Components</title>
<para>
Components are the basic user interface element <literal remap="tt">newt</literal> provides. A
single component may be (for example) a listbox, push button checkbox,
a collection of other components. Most components are used to display
information in a window, provide a place for the user to enter data, or a
combination of these two functions.
Forms, however, are a component whose primary purpose is not noticed by
the user at all. Forms are collections of components (a form may contain
another form) which logically relate the components to one another. Once
a form is created and had all of its constituent components added to it,
applications normally then run the form. This gives control of the
application to the form, which then lets the user enter data onto the
form. When the user is done (a number of different events qualify as
``done''), the form returns control to the part of the application which
invoked it. The application may then read the information the user provided
and continue appropriately.
All <literal remap="tt">newt</literal> components are stored in a common data type, a
<literal remap="tt">newtComponent</literal> (some of the particulars of <literal remap="tt">newtComponent</literal>s have
already been mentioned. While this makes it easy for programmers to pass
components around, it does force them to make sure they don't pass
entry boxes to routines expecting push buttons, as the compiler can't
ensure that for them.
We start off with a brief introduction to forms. While not terribly
complete, this introduction is enough to let us illustrate the rest of
the components with some sample code. We'll then discuss the remainder of
the components, and end this section with a more exhaustive description of
forms.</para>
<sect2><title>Introduction to Forms</title>
<para>
As we've mentioned, forms are simply collections of components. As only one
form can be active (or running) at a time, every component which the user
should be able to access must be on the running form (or on a subform of
the running form). A form is itself a component, which means forms are
stored in <literal remap="tt">newtComponent</literal> data structures.
<screen>
newtComponent newtForm(newtComponent vertBar, const char * help, int flags);
</screen>
To create a form, call <literal remap="tt">newtForm()</literal>. The first parameter is a vertical
scrollbar which should be associated with the form. For now, that should
always be <literal remap="tt">NULL</literal> (we'll discuss how to create scrolling forms later in
this section). The second parameter, <literal remap="tt">help</literal>, is currently unused and
should always be <literal remap="tt">NULL</literal>. The <literal remap="tt">flags</literal> is normally 0, and other values
it can take will be discussed later. Now that we've waved away the
complexity of this function, creating a form boils down to simply:
<screen>
newtComponent myForm;
myForm = newtForm(NULL, NULL, 0);
</screen>
After a form is created, components need to be added to it --- after all,
an empty form isn't terribly useful. There are two functions which add
components to a form.
<screen>
void newtFormAddComponent(newtComponent form, newtComponent co);
void newtFormAddComponents(newtComponent form, ...);
</screen>
The first function, <literal remap="tt">newtFormAddComponent()</literal>, adds a single component
to the form which is passed as the first parameter. The second function
is simply a convenience function. After passing the form to
<literal remap="tt">newtFormAddComponents()</literal>, an arbitrary number of components is then
passed, followed by <literal remap="tt">NULL</literal>. Every component passed is added to the form.
Once a form has been created and components have been added to it, it's
time to run the form.
<screen>
newtComponent newtRunForm(newtComponent form);
</screen>
This function runs the form passed to it, and returns the component which
caused the form to stop running. For now, we'll ignore the return value
completely.
Notice that this function doesn't fit in with <literal remap="tt">newt</literal>'s normal
naming convention. It is an older interface which will not work for all
forms. It was left in <literal remap="tt">newt</literal> only for legacy applications. It is a
simpler interface than the new <literal remap="tt">newtFormRun()</literal> though, and is still used
quite often as a result.
When an application is done with a form, it destroys the form and
all of the components the form contains.
<screen>
void newtFormDestroy(newtComponent form);
</screen>
This function frees the memory resources used by the form and all of the
components which have been added to the form (including those components
which are on subforms). Once a form has been destroyed, none of the form's
components can be used.</para></sect2>
<sect2><title>Components</title>
<para>
Non-form components are the most important user-interface component for
users. They determine how users interact with <literal remap="tt">newt</literal> and how information
is presented to them.</para></sect2>
<sect2><title>General Component Manipulation</title>
<para>
There are a couple of functions which work on more then one type of
components. The description of each component indicates which (if any)
of these functions are valid for that particular component.
<screen>
typedef void (*newtCallback)(newtComponent, void *);
void newtComponentAddCallback(newtComponent co, newtCallback f, void * data);
void newtComponentTakesFocus(newtComponent co, int val);
</screen>
The first registers a callback function for that component. A callback
function is a function the application provides which <literal remap="tt">newt</literal> calls for a
particular component. Exactly when (if ever) the callback is invoked
depends on the type of component the callback is attached to, and will be
discussed for the components which support callbacks.
<literal remap="tt">newtComponentTakesFocus()</literal> works on all components. It allows the
application to change which components the user is allowed to select as the
current component, and hence provide input to. Components which do not
take focus are skipped over during form traversal, but they are displayed
on the terminal. Some components should never be set to take focus, such
as those which display static text.</para></sect2>
<sect2><title>Buttons</title>
<para>
Nearly all forms contain at least one button. <literal remap="tt">Newt</literal> buttons come in two
flavors, full buttons and compact buttons. Full buttons take up quit a bit
of screen space, but look much better then the single-row compact buttons.
Other then their size, both button styles behave identically. Different
functions are used to create the two types of buttons.
<screen>
newtComponent newtButton(int left, int top, const char * text);
newtComponent newtCompactButton(int left, int top, const char * text);
</screen>
Both functions take identical parameters. The first two parameters are the
location of the upper left corner of the button, and the final parameter is
the text which should be displayed in the button (such as ``Ok'' or
``Cancel'').</para>
<sect3><title>Button Example</title>
<para>
Here is a simple example of both full and compact buttons. It also
illustrates opening and closing windows, as well a simple form.
<screen>
#include <newt.h>
#include <stdlib.h>
void main(void) {
newtComponent form, b1, b2;
newtInit();
newtCls();
newtOpenWindow(10, 5, 40, 6, "Button Sample");
b1 = newtButton(10, 1, "Ok");
b2 = newtCompactButton(22, 2, "Cancel");
form = newtForm(NULL, NULL, 0);
newtFormAddComponents(form, b1, b2, NULL);
newtRunForm(form);
newtFormDestroy(form);
newtFinished();
}
</screen></para></sect3></sect2>
<sect2><title>Labels</title>
<para>
Labels are <literal remap="tt">newt</literal>'s simplest component. They display some given text and
don't allow any user input.
<screen>
newtComponent newtLabel(int left, int top, const char * text);
void newtLabelSetText(newtComponent co, const char * text);
</screen>
Creating a label is just like creating a button; just pass the location of
the label and the text it should display. Unlike buttons, labels do let the
application change the text in the label with <literal remap="tt">newtLabelSetText</literal>. When
the label's text is changed, the label automatically redraws itself. It
does not clear out any old text which may be leftover from the previous
time is was displayed, however, so be sure that the new text is at least
as long as the old text.</para></sect2>
<sect2><title>Entry Boxes</title>
<para>
Entry boxes allow the user to enter a text string into the form which the
application can later retrieve.
<screen>
typedef int (*newtEntryFilter)(newtComponent entry, void * data, int ch,
int cursor);
newtComponent newtEntry(int left, int top, const char * initialValue, int width,
char ** resultPtr, int flags);
void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd);
char * newtEntryGetValue(newtComponent co);
void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data);
</screen></para>
<para>
<literal remap="tt">newtEntry()</literal> creates a new entry box. After the location of the entry
box, the initial value for the entry box is passed, which may be <literal remap="tt">NULL</literal>
if the box should start off empty. Next, the width of the physical box is
given. This width may or may not limit the length of the string the user is
allowed to enter; that depends on the <literal remap="tt">flags</literal>. The <literal remap="tt">resultPtr</literal> must
be the address of a <literal remap="tt">char *</literal>. Until the entry box is destroyed by
<literal remap="tt">newtFormDestroy()</literal>, that <literal remap="tt">char *</literal> will point to the current value
of the entry box. It's important that applications make a copy of that
value before destroying the form if they need to use it later. The
<literal remap="tt">resultPtr</literal> may be <literal remap="tt">NULL</literal>, in which case the user must use the
<literal remap="tt">newtEntryGetValue()</literal> function to get the value of the entry box.
Entry boxes support a number of flags:
<variablelist>
<varlistentry>
<term>NEWT_ENTRY_SCROLL</term>
<listitem>
<para>If this flag is not specified, the user cannot
enter text into the entry box which is wider then the entry box itself.
This flag removes this limitation, and lets the user enter data of an
arbitrary length.</para></listitem>
</varlistentry>
<varlistentry>
<term>NEWT_FLAG_HIDDEN</term>
<listitem>
<para>If this flag is specified, the value of the entry box
is not displayed. This is useful when the application needs to read a
password, for example.</para></listitem>
</varlistentry>
<varlistentry>
<term>NEWT_FLAG_RETURNEXIT</term>
<listitem>
<para>When this flag is given, the entry box will cause
the form to stop running if the user pressed return inside of the entry
box. This can provide a nice shortcut for users.</para>
</listitem>
</varlistentry>
</variablelist>
After an entry box has been created, its contents can be set by
<literal remap="tt">newtEntrySet()</literal>. After the entry box itself, the new string to place
in the entry box is passed. The final parameter, <literal remap="tt">cursorAtEnd</literal>, controls
where the cursor will appear in the entry box. If it is zero, the cursor
remains at its present location; a nonzero value moves the cursor to the
end of the entry box's new value.
While the simplest way to find the value of an entry box is by using a
<literal remap="tt">resultPtr</literal>, doing so complicates some applications.
<literal remap="tt">newtEntryGetValue()</literal> returns a pointer to the string which the entry
box currently contains. The returned pointer may not be valid once the
user further modifies the entry box, and will not be valid after the
entry box has been destroyed, so be sure to save its value in a more
permanent location if necessary.
Entry boxes allow applications to filter characters as they are entered.
This allows programs to ignore characters which are invalid (such as
entering a ^ in the middle of a phone number) and provide intelligent aids
to the user (such as automatically adding a '.' after the user has typed in
the first three numbers in an IP address).
When a filter is registered through <literal remap="tt">newtEntrySetFilter()</literal>, both the
filter itself and an arbitrary <literal remap="tt">void *</literal>, which passed to the filter
whenever it is invoked, are recorded. This data pointer isn't used for any
other purpose, and may be <literal remap="tt">NULL</literal>. Entry filters take four arguments.
<orderedlist>
<listitem>
<para>
The entry box which had data entered into it</para></listitem>
<listitem>
<para>
The data pointer which was registered along with the filter</para></listitem>
<listitem>
<para>
The new character which <literal remap="tt">newt</literal> is considering inserting into the
entry box</para></listitem>
<listitem>
<para>
The current cursor position (0 is the leftmost position)
</para>
</listitem>
</orderedlist>
The filter returns 0 if the character should be ignored, or the value of
the character which should be inserted into the entry box. Filter functions
which want to do complex manipulations of the string should use
<literal remap="tt">newtEntrySet()</literal> to update the entry box and then return 0 to prevent
the new character from being inserted.
When a callback is attached to a entry box, the callback is invoked
whenever the user moves off of the callback and on to another component.
Here is a sample program which illustrates the use of both labels and
entry boxes.
<screen>
#include <newt.h>
#include <stdlib.h>
#include <stdio.h>
void main(void) {
newtComponent form, label, entry, button;
char * entryValue;
newtInit();
newtCls();
newtOpenWindow(10, 5, 40, 8, "Entry and Label Sample");
label = newtLabel(1, 1, "Enter a string");
entry = newtEntry(16, 1, "sample", 20, &entryValue,
NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
button = newtButton(17, 3, "Ok");
form = newtForm(NULL, NULL, 0);
newtFormAddComponents(form, label, entry, button, NULL);
newtRunForm(form);
newtFinished();
printf("Final string was: %s\n", entryValue);
/* We cannot destroy the form until after we've used the value
from the entry widget. */
newtFormDestroy(form);
}
</screen></para></sect2>
<sect2><title>Checkboxes</title>
<para>
Most widget sets include checkboxes which toggle between two value (checked
or not checked). <literal remap="tt">Newt</literal> checkboxes are more flexible. When the user
presses the space bar on a checkbox, the checkbox's value changes to the
next value in an arbitrary sequence (which wraps). Most checkboxes have
two items in that sequence, checked or not, but <literal remap="tt">newt</literal> allows an
arbitrary number of value. This is useful when the user must pick from a
limited number of choices.
Each item in the sequence is a single character, and the sequence itself is
represented as a string. The checkbox components displays the character
which currently represents its value the left of a text label, and returns
the same character as its current value. The default sequence for
checkboxes is <literal remap="tt">" *"</literal>, with <literal remap="tt">' '</literal> indicating false and <literal remap="tt">'*'</literal> true.
<screen>
newtComponent newtCheckbox(int left, int top, const char * text, char defValue,
const char * seq, char * result);
char newtCheckboxGetValue(newtComponent co);
</screen></para>
<para>
Like most components, the position of the checkbox is the first thing
passed to the function that creates one. The next parameter, <literal remap="tt">text</literal>, is
the text which is displayed to the right of the area which is checked. The
<literal remap="tt">defValue</literal> is the initial value for the checkbox, and <literal remap="tt">seq</literal> is the
sequence which the checkbox should go through (<literal remap="tt">defValue</literal> must be
in <literal remap="tt">seq</literal>. <literal remap="tt">seq</literal> may be <literal remap="tt">NULL</literal>, in which case <literal remap="tt">" *"</literal> is used.
The final parameter, <literal remap="tt">result</literal>, should point to a character which the
checkbox should always record its current value in. If <literal remap="tt">result</literal> is
<literal remap="tt">NULL</literal>, <literal remap="tt">newtCheckboxGetValue()</literal> must be used to get the current
value of the checkbox.
<literal remap="tt">newtCheckboxGetValue()</literal> is straightforward, returning the character
in the sequence which indicates the current value of the checkbox
If a callback is attached to a checkbox, the callback is invoked whenever
the checkbox responds to a user's keystroke. The entry box may respond by
taking focus or giving up focus, as well as by changing its current value.</para></sect2>
<sect2><title>Radio Buttons</title>
<para>
Radio buttons look very similar to checkboxes. The key difference between
the two is that radio buttons are grouped into sets, and exactly one radio
button in that set may be turned on. If another radio button is selected,
the button which was selected is automatically deselected.
<screen>
newtComponent newtRadiobutton(int left, int top, const char * text,
int isDefault, newtComponent prevButton);
newtComponent newtRadioGetCurrent(newtComponent setMember);
</screen>
Each radio button is created by calling <literal remap="tt">newtRadiobutton()</literal>. After
the position of the radio button, the text displayed with the button
is passed. <literal remap="tt">isDefault</literal> should be nonzero if the radio button is to
be turned on by default. The final parameter, <literal remap="tt">prevMember</literal> is used
to group radio buttons into sets. If <literal remap="tt">prevMember</literal> is <literal remap="tt">NULL</literal>, the
radio button is assigned to a new set. If the radio button should belong
to a preexisting set, <literal remap="tt">prevMember</literal> must be the previous radio button
added to that set.
Discovering which radio button in a set is currently selected necessitates
<literal remap="tt">newtRadioGetCurrent()</literal>. It may be passed any radio button in the set
you're interested in, and it returns the radio button component currently
selected.
Here is an example of both checkboxes and radio buttons.